vendor/github.com/olekukonko/ll/lh/slog.go
package lh
import (
"context"
"github.com/olekukonko/ll/lx"
"log/slog"
)
// SlogHandler adapts a slog.Handler to implement lx.Handler.
// It converts lx.Entry objects to slog.Record objects and delegates to an underlying
// slog.Handler for processing, enabling compatibility with Go's standard slog package.
// Thread-safe if the underlying slog.Handler is thread-safe.
type SlogHandler struct {
slogHandler slog.Handler // Underlying slog.Handler for processing log records
}
// NewSlogHandler creates a new SlogHandler wrapping the provided slog.Handler.
// It initializes the handler with the given slog.Handler, allowing lx.Entry logs to be
// processed by slog's logging infrastructure.
// Example:
//
// slogText := slog.NewTextHandler(os.Stdout, nil)
// handler := NewSlogHandler(slogText)
// logger := ll.New("app").Enable().Handler(handler)
// logger.Info("Test") // Output: level=INFO msg=Test namespace=app class=Text
func NewSlogHandler(h slog.Handler) *SlogHandler {
return &SlogHandler{slogHandler: h}
}
// Handle converts an lx.Entry to slog.Record and delegates to the slog.Handler.
// It maps the entry's fields, level, namespace, class, and stack trace to slog attributes,
// passing the resulting record to the underlying slog.Handler.
// Returns an error if the slog.Handler fails to process the record.
// Thread-safe if the underlying slog.Handler is thread-safe.
// Example:
//
// handler.Handle(&lx.Entry{Message: "test", Level: lx.LevelInfo}) // Processes as slog record
func (h *SlogHandler) Handle(e *lx.Entry) error {
// Convert lx.LevelType to slog.Level
level := toSlogLevel(e.Level)
// Create a slog.Record with the entry's data
record := slog.NewRecord(
e.Timestamp, // time.Time for log timestamp
level, // slog.Level for log severity
e.Message, // string for log message
0, // pc (program counter, optional, not used)
)
// Add standard fields as attributes
record.AddAttrs(
slog.String("namespace", e.Namespace), // Add namespace as string attribute
slog.String("class", e.Class.String()), // Add class as string attribute
)
// Add stack trace if present
if len(e.Stack) > 0 {
record.AddAttrs(slog.String("stack", string(e.Stack))) // Add stack trace as string
}
// Add custom fields
for k, v := range e.Fields {
record.AddAttrs(slog.Any(k, v)) // Add each field as a key-value attribute
}
// Handle the record with the underlying slog.Handler
return h.slogHandler.Handle(context.Background(), record)
}
// toSlogLevel converts lx.LevelType to slog.Level.
// It maps the logging levels used by the lx package to those used by slog,
// defaulting to slog.LevelInfo for unknown levels.
// Example (internal usage):
//
// level := toSlogLevel(lx.LevelDebug) // Returns slog.LevelDebug
func toSlogLevel(level lx.LevelType) slog.Level {
switch level {
case lx.LevelDebug:
return slog.LevelDebug
case lx.LevelInfo:
return slog.LevelInfo
case lx.LevelWarn:
return slog.LevelWarn
case lx.LevelError:
return slog.LevelError
default:
return slog.LevelInfo // Default for unknown levels
}
}