Documentation
¶
Overview ¶
Package slogprovider provides an iris.SyncReader implementation for Go's standard log/slog package.
This provider enables existing slog-based applications to benefit from Iris's high-performance logging pipeline without requiring code changes. It implements both the iris.SyncReader interface for integration with Iris and the slog.Handler interface for compatibility with slog.
Key Features ¶
- Zero Code Changes: Use existing slog code unchanged with enhanced performance
- High Performance: 10-20x faster than standard slog through Iris acceleration
- Feature Inheritance: Automatic OpenTelemetry, security, and advanced Iris features
- Drop-in Replacement: Simply replace slog.Handler with this provider
- Thread Safety: Safe for concurrent access from multiple goroutines
Performance Characteristics ¶
- slog Handle: ~60-150 ns/op (compared to ~1000+ ns/op for standard handlers)
- Record Conversion: ~500-1000 ns/op with zero additional allocations
- Overall: 10-20x faster than standard slog implementations
Basic Usage ¶
import (
"log/slog"
"os"
"github.com/agilira/iris"
slogprovider "github.com/agilira/iris-provider-slog"
)
func main() {
// Create provider
provider := slogprovider.New(1000)
defer provider.Close()
// Create Iris logger with provider
readers := []iris.SyncReader{provider}
logger, err := iris.NewReaderLogger(iris.Config{
Output: iris.WrapWriter(os.Stdout),
Encoder: iris.NewJSONEncoder(),
Level: iris.Info,
}, readers)
if err != nil {
panic(err)
}
defer logger.Close()
logger.Start()
// Use slog normally - but get Iris performance!
slogger := slog.New(provider)
slogger.Info("User login", "user_id", "12345")
}
Advanced Integration ¶
For advanced features like Loki integration, install the writer module:
go get github.com/agilira/iris-writer-loki
Then configure multiple outputs:
import loki "github.com/agilira/iris-writer-loki"
lokiWriter, err := loki.NewWriter(loki.Config{
Endpoint: "http://loki:3100/loki/api/v1/push",
Labels: map[string]string{"service": "my-app"},
})
if err != nil {
panic(err)
}
logger, err := iris.NewReaderLogger(iris.Config{
Output: iris.MultiWriter(
iris.WrapWriter(os.Stdout),
lokiWriter,
),
Encoder: iris.NewJSONEncoder(),
}, readers,
iris.WithOTel(), // OpenTelemetry integration
iris.WithCaller(), // Caller information
)
Now slog gets ALL Iris features automatically:
- OpenTelemetry trace correlation
- Grafana Loki batching
- Automatic secret redaction
- High-performance ring buffer
Architecture ¶
This provider implements the iris.SyncReader interface, allowing slog records to be processed by Iris's high-performance pipeline:
slog.Logger → SlogProvider → iris.SyncReader → Iris Ring Buffer → Features
The provider maintains an internal buffer of slog records and converts them to Iris records on demand. This design ensures:
- Non-blocking slog operations
- Efficient batching and processing
- Automatic cleanup and resource management
- Graceful handling of buffer overflow conditions
Thread Safety ¶
All provider operations are thread-safe:
- Multiple goroutines can call Handle() simultaneously
- Read() operations are safe for concurrent access
- Close() can be called while other operations are in progress
- Internal state is protected with appropriate synchronization
Buffer Management ¶
The provider uses a buffered channel for record storage:
- Buffer size is configurable during construction
- Full buffers result in record dropping (non-blocking behavior)
- Buffer size should be tuned based on logging volume and processing speed
- Recommended buffer sizes: 100-1000 for typical applications, 1000+ for high-volume
Error Handling ¶
The provider follows Iris patterns for error handling:
- Handle() drops records on buffer full rather than blocking
- Read() respects context cancellation for graceful shutdown
- Close() is idempotent and safe to call multiple times
- Conversion errors are handled gracefully with fallback behavior
Level Mapping ¶
Slog levels are mapped to Iris levels as follows:
- slog.LevelDebug → iris.Debug
- slog.LevelInfo → iris.Info
- slog.LevelWarn → iris.Warn
- slog.LevelError → iris.Error
- Custom levels are mapped to the nearest Iris level
Field Conversion ¶
Slog attributes are converted to Iris fields with type preservation:
- String values → iris.String
- Integer values → iris.Int64
- Float values → iris.Float64
- Boolean values → iris.Bool
- Duration values → iris.Dur
- Time values → iris.Time
- Other types → iris.String (with String() conversion)
Dependencies ¶
This package requires:
- github.com/agilira/iris (core logging library)
- Go's standard log/slog package (Go 1.21+)
No additional dependencies are required for basic functionality.
License ¶
iris-provider-slog is licensed under the Mozilla Public License 2.0.
Copyright (c) 2025 AGILira Series: an AGILira library SPDX-License-Identifier: MPL-2.0
Index ¶
- type Provider
- func (p *Provider) Close() error
- func (p *Provider) Enabled(ctx context.Context, level slog.Level) bool
- func (p *Provider) Handle(ctx context.Context, record slog.Record) error
- func (p *Provider) Read(ctx context.Context) (*iris.Record, error)
- func (p *Provider) WithAttrs(attrs []slog.Attr) slog.Handler
- func (p *Provider) WithGroup(name string) slog.Handler
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider implements iris.SyncReader for Go's standard log/slog package.
Provider acts as a bridge between slog and Iris, implementing both the iris.SyncReader interface for Iris integration and the slog.Handler interface for slog compatibility. It captures slog records in an internal buffer and converts them to Iris records on demand.
The provider is designed for high performance and thread safety:
- Non-blocking Handle() operations (drops records on buffer full)
- Efficient record conversion with type preservation
- Safe concurrent access from multiple goroutines
- Graceful shutdown with proper resource cleanup
Example usage:
provider := slogprovider.New(1000)
defer provider.Close()
slogger := slog.New(provider)
slogger.Info("Message", "key", "value")
func New ¶
New creates a new Provider that captures slog records for processing by Iris.
The bufferSize parameter controls the internal channel buffer size. A larger buffer provides better performance under burst loads but uses more memory. Recommended values:
- 100-500: Low to moderate logging volume applications
- 1000-5000: High volume applications
- 5000+: Very high volume or burst-heavy applications
When the buffer is full, new records are dropped to maintain non-blocking behavior. Monitor your application's logging patterns to choose an appropriate buffer size.
The returned Provider must be closed when no longer needed to free resources:
provider := New(1000) defer provider.Close()
func (*Provider) Close ¶
Close implements io.Closer to gracefully shut down the provider.
This method signals the provider to stop accepting new records and allows pending Read() operations to complete gracefully. It's safe to call multiple times and from multiple goroutines.
After Close() is called:
- Handle() will return an error for new records
- Read() will return nil, nil after processing remaining buffered records
- The provider should not be used for new operations
Close() does not wait for pending operations to complete. Use context cancellation and proper coordination if you need to ensure all records are processed before shutdown.
func (*Provider) Enabled ¶
Enabled implements slog.Handler to indicate whether records at the given level should be processed.
This implementation always returns true, allowing Iris to handle level filtering according to its own configuration. This approach provides more flexibility and ensures that level changes in Iris are respected without requiring provider reconfiguration.
If you need level filtering at the slog level, consider creating a wrapper handler that checks levels before delegating to this provider.
func (*Provider) Handle ¶
Handle implements slog.Handler to capture slog records for processing by Iris.
This method is called by the slog library for each log record. It attempts to store the record in the internal buffer for later processing by Iris. The operation is non-blocking:
- If buffer space is available, the record is stored successfully
- If the provider is closed, an error is returned
- If the buffer is full, the record is dropped silently (returns nil)
The non-blocking behavior ensures that logging never blocks the application, even under high load conditions. Applications should monitor buffer sizes and provider performance if record dropping is a concern.
Thread Safety: Safe for concurrent access from multiple goroutines.
func (*Provider) Read ¶
Read implements iris.SyncReader to provide slog records to the Iris pipeline.
This method is called by Iris to retrieve the next available log record for processing. It blocks until:
- A record becomes available (returns the converted record)
- The context is cancelled (returns context error)
- The provider is closed (returns nil, nil)
The method converts slog records to Iris records, preserving message content, level information, and all attributes with appropriate type conversion.
Thread Safety: Safe for concurrent access, though typically called by a single Iris reader goroutine.
func (*Provider) WithAttrs ¶
WithAttrs implements slog.Handler to create a handler with additional attributes.
This implementation returns the same provider instance, as attribute handling is delegated to the slog library. The slog library will include the attributes in each record before calling Handle(), so no special handling is needed here.
For more sophisticated attribute handling, consider implementing a wrapper handler that manages attributes before delegating to this provider.
func (*Provider) WithGroup ¶
WithGroup implements slog.Handler to create a handler with a named group.
This implementation returns the same provider instance, as group handling is delegated to the slog library. The slog library will structure the attributes appropriately before calling Handle(), so no special handling is needed here.
For more sophisticated group handling, consider implementing a wrapper handler that manages groups before delegating to this provider.