100 lines
3.6 KiB
Go
100 lines
3.6 KiB
Go
package lh
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
|
|
"github.com/olekukonko/ll/lx"
|
|
)
|
|
|
|
// 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
|
|
//
|
|
// 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 in order (preserving insertion order)
|
|
for _, pair := range e.Fields {
|
|
record.AddAttrs(slog.Any(pair.Key, pair.Value)) // 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, lx.LevelFatal:
|
|
return slog.LevelError
|
|
default:
|
|
return slog.LevelInfo // Default for unknown levels
|
|
}
|
|
}
|