logger

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jun 23, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package logger is a thin wrapper around log/slog tailored for services.

It injects a trace_id into every record (via a pluggable TraceIDFn), tags records with the service name, exposes context-aware Debug/Info/Warn/Error methods, and can fan records out to multiple sinks by level (see FanoutHandler) — e.g. everything to stdout and ERROR+ to Sentry. The same *Logger is meant to be used everywhere: HTTP/gRPC handlers, business code, and background workers.

Usage

Build one Logger at startup and inject it. Pair TraceIDFn with the otel package so every line carries the active trace id:

log := logger.New(os.Stdout, logger.Config{
	Service:   "myapp",
	Level:     logger.LevelInfo,
	AddSource: true,
	TraceIDFn: otel.GetTraceID,
})
log.Info(ctx, "service started", "addr", ":8080")

access := log.Named("access")   // child tagged logger=access
access.Info(ctx, "request", "method", "GET", "path", "/")

Multiple sinks

To split output by level, build a FanoutHandler and pass it to NewWithHandler. Each wrapped handler's own Enabled check gates which records it receives, so per-sink level filtering lives on the handlers:

stdout := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: logger.LevelInfo})
h := logger.NewFanout(stdout, sentryHandler) // sentryHandler.Enabled gates ERROR+
log := logger.NewWithHandler(h, logger.Config{Service: "myapp", TraceIDFn: otel.GetTraceID})

Interop with slog

Handler returns the underlying slog.Handler and Slog returns a stdlib *slog.Logger backed by it, so third-party middleware (e.g. httplog) can log through the same sink, formatting, and trace-id injection. Trace-id injection is installed as a handler wrapper rather than in the *Logger write path, so it applies uniformly to *Logger, Slog(), and anything sharing the handler.

Config

Config fields:

  • Service: tags every record with attribute "service".
  • Level: minimum level handled (LevelDebug/LevelInfo/LevelWarn/LevelError).
  • AddSource: attach a "source" attribute shortened to file:line.
  • TraceIDFn: injects "trace_id"; may be nil. Typically otel.GetTraceID.

Deriving child loggers:

  • Named(name): child tagged logger=name (access logger, worker logger, ...).
  • With(args...): child whose records always carry the given attributes.

Children share the parent's handler, so output, level, and any fan-out stay unified — the intended way to have several logger instances without proliferating handlers or config.

Index

Constants

View Source
const (
	LevelDebug = slog.LevelDebug
	LevelInfo  = slog.LevelInfo
	LevelWarn  = slog.LevelWarn
	LevelError = slog.LevelError
)

Log levels, re-exported from slog for convenience.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Service tags every record with this name (attribute "service").
	Service string
	// Level is the minimum level handled.
	Level Level
	// AddSource attaches a "source" attribute (file:line) to records.
	AddSource bool
	// TraceIDFn injects "trace_id"; may be nil.
	TraceIDFn TraceIDFn
}

Config configures a Logger.

type FanoutHandler

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

FanoutHandler dispatches each record to every wrapped handler whose own Enabled check passes. This lets you, for example, send all records to a stdout JSON handler while also forwarding ERROR+ records to a Sentry handler.

stdout := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: LevelInfo})
h := logger.NewFanout(stdout, sentryHandler) // sentryHandler.Enabled gates ERROR+
log := logger.NewWithHandler(h, logger.Config{Service: "svc", TraceIDFn: otel.GetTraceID})

func NewFanout

func NewFanout(handlers ...slog.Handler) *FanoutHandler

NewFanout builds a FanoutHandler over the given handlers (nil handlers are skipped). Per-sink level filtering is the responsibility of each handler.

func (*FanoutHandler) Enabled

func (f *FanoutHandler) Enabled(ctx context.Context, level slog.Level) bool

Enabled reports whether any wrapped handler is enabled for the level.

func (*FanoutHandler) Handle

func (f *FanoutHandler) Handle(ctx context.Context, r slog.Record) error

Handle forwards the record to every wrapped handler that is enabled for it.

func (*FanoutHandler) WithAttrs

func (f *FanoutHandler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs implements slog.Handler, fanning attrs out to each child handler.

func (*FanoutHandler) WithGroup

func (f *FanoutHandler) WithGroup(name string) slog.Handler

WithGroup implements slog.Handler, fanning the group out to each child handler.

type Level

type Level = slog.Level

Level mirrors slog.Level for convenience.

type Logger

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

Logger is the application logger.

func New

func New(w io.Writer, cfg Config) *Logger

New builds a Logger writing JSON to w. For multi-sink behavior, build the Logger with NewWithHandler and a FanoutHandler instead.

func NewWithHandler

func NewWithHandler(h slog.Handler, cfg Config) *Logger

NewWithHandler builds a Logger from a custom slog.Handler (e.g. a FanoutHandler). The service attribute and TraceIDFn from cfg are applied.

Trace-id injection is installed as a handler wrapper (not in the Logger's write path), so it also applies to any *slog.Logger derived via Slog() and to third-party middleware that logs through the same handler (e.g. httplog).

func (*Logger) Debug

func (l *Logger) Debug(ctx context.Context, msg string, args ...any)

Debug logs at debug level.

func (*Logger) Error

func (l *Logger) Error(ctx context.Context, msg string, args ...any)

func (*Logger) Handler

func (l *Logger) Handler() slog.Handler

Handler returns the underlying slog.Handler, so callers can build a stdlib *slog.Logger that shares the same sink and formatting.

func (*Logger) Info

func (l *Logger) Info(ctx context.Context, msg string, args ...any)

Info logs at info level.

func (*Logger) Named

func (l *Logger) Named(name string) *Logger

Named returns a child Logger tagged with logger=name. Use it to derive purpose-specific instances (e.g. an access logger, a worker logger) that share the same underlying handler — so output, level, and any multi-sink fan-out (stdout + Sentry, ...) stay unified — while remaining distinguishable in the logs. This is the intended way to have several logger instances without proliferating handlers/config.

func (*Logger) Slog

func (l *Logger) Slog() *slog.Logger

Slog returns a stdlib *slog.Logger backed by the same handler.

func (*Logger) Warn

func (l *Logger) Warn(ctx context.Context, msg string, args ...any)

Warn logs at warn level.

func (*Logger) With

func (l *Logger) With(args ...any) *Logger

With returns a child Logger whose records always carry the given attributes. The child shares the parent's handler.

type TraceIDFn

type TraceIDFn func(ctx context.Context) string

TraceIDFn extracts a trace id from the context. Return "" when none exists.

Jump to

Keyboard shortcuts

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