slogprovider

package
v1.2.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 19, 2026 License: MPL-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package slogprovider bridges Go's standard log/slog to the Iris pipeline.

WHY this exists: slog is the Go standard logging API (since Go 1.21). Metis and most Go services use slog exclusively. This provider lets them feed records into Iris's high-performance transport without changing a single call site.

The provider implements two interfaces simultaneously:

  • slog.Handler: so slog.New(provider) works as a drop-in
  • iris.SyncReader: so Iris can pull records through its pipeline

Architecture

slog.Logger --> Provider.Handle() --> channel --> Provider.Read() --> Iris pipeline --> Output

The channel decouples the slog caller from the Iris processing goroutine. Handle() is non-blocking (drops on full buffer). Read() blocks until a record arrives or the context is cancelled.

Basic Usage

import (
    "log/slog"
    "os"
    "github.com/agilira/iris"
    "github.com/agilira/iris/slogprovider"
)

func main() {
    provider := slogprovider.New(1000)
    defer provider.Close()

    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()

    slogger := slog.New(provider)
    slogger.Info("User login", "user_id", "12345")
}

Performance

  • Handle(): ~60-150 ns/op (channel send with select)
  • Record conversion: ~500-1000 ns/op with type preservation
  • Overall: 10-20x faster than standard slog handlers

Type Preservation

slog attribute types (String, Int64, Uint64, Float64, Bool, Duration, Time) are mapped to their iris.Field equivalents. This ensures JSON encoders emit correct types instead of quoting everything as strings.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AdaptiveHandler added in v1.2.1

type AdaptiveHandler struct {
	// contains filtered or unexported fields
}

AdaptiveHandler implements slog.Handler by writing directly into an AdaptiveLogger. No channels, no goroutine hops -- the slog.Record is converted to iris fields and dispatched synchronously into the ring buffer.

Thread Safety: Safe for concurrent use. AdaptiveLogger handles all synchronization internally via atomic contention detection.

func NewAdaptiveHandler added in v1.2.1

func NewAdaptiveHandler(al *iris.AdaptiveLogger) *AdaptiveHandler

NewAdaptiveHandler creates a handler that bridges slog to an AdaptiveLogger. The caller owns the AdaptiveLogger lifecycle (Start, Close, Sync).

func (*AdaptiveHandler) Enabled added in v1.2.1

func (h *AdaptiveHandler) Enabled(_ context.Context, _ slog.Level) bool

Enabled implements slog.Handler. Always returns true because level filtering is iris's responsibility. Returning false here would prevent records from reaching iris, making dynamic level changes via SetLevel impossible.

func (*AdaptiveHandler) Handle added in v1.2.1

func (h *AdaptiveHandler) Handle(_ context.Context, record slog.Record) error

Handle implements slog.Handler. Converts the slog.Record into iris fields and dispatches to the appropriate AdaptiveLogger method based on level.

WHY synchronous: the AdaptiveLogger's ring buffer is the async boundary. Adding another buffer here would defeat the purpose (latency + complexity).

func (*AdaptiveHandler) WithAttrs added in v1.2.1

func (h *AdaptiveHandler) WithAttrs(_ []slog.Attr) slog.Handler

WithAttrs implements slog.Handler. Returns self because slog embeds attributes into each Record before calling Handle(). Duplicating that logic here adds complexity with zero gain.

func (*AdaptiveHandler) WithGroup added in v1.2.1

func (h *AdaptiveHandler) WithGroup(_ string) slog.Handler

WithGroup implements slog.Handler. Returns self -- same reasoning as WithAttrs.

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

func New(bufferSize int) *Provider

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

func (p *Provider) Close() error

Close implements io.Closer to gracefully shut down the provider.

WHY sync.Once: Close may be called from defer + explicit shutdown paths. Double-close on a channel panics; sync.Once makes this impossible.

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

func (*Provider) Enabled

func (p *Provider) Enabled(ctx context.Context, level slog.Level) bool

Enabled implements slog.Handler to indicate whether records at the given level should be processed.

WHY always true: Level filtering is Iris's responsibility. Delegating here would create two competing filter points and make dynamic level changes impossible without reconfiguring the slog side.

func (*Provider) Handle

func (p *Provider) Handle(ctx context.Context, record slog.Record) error

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

func (p *Provider) Read(ctx context.Context) (*iris.Record, error)

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

func (p *Provider) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs implements slog.Handler to create a handler with additional attributes.

WHY pass-through: The slog library embeds attributes into each Record before calling Handle(). Duplicating that logic here would add complexity with zero gain -- the attributes arrive regardless.

func (*Provider) WithGroup

func (p *Provider) WithGroup(name string) slog.Handler

WithGroup implements slog.Handler to create a handler with a named group.

WHY pass-through: Same reasoning as WithAttrs -- the slog library handles group structuring internally before calling Handle().

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL