Handlers and levels
The fan-out handler, the handler options for custom-level names, and the TRACE and FATAL levels.
These are the primitives log.New builds on, exported for hand-assembly. Reach for them when you assemble handlers yourself instead of through the builder.
NewMultiHandler
func NewMultiHandler(handlers ...slog.Handler) *MultiHandlerReturns a handler that dispatches each record to every child handler in order, so one logger writes to several outputs at once. A record is dropped only when every child would discard it, and one failing output does not stop the others, with their errors joined.
multi := log.NewMultiHandler(
slog.NewTextHandler(os.Stdout, log.HandlerOptions(slog.LevelDebug)),
slog.NewJSONHandler(file, log.HandlerOptions(slog.LevelDebug)),
)HandlerOptions
func HandlerOptions(level slog.Leveler) *slog.HandlerOptionsReturns *slog.HandlerOptions wired to level with the custom-level name rendering. Pass it when building a raw slog.Handler, so the custom TRACE and FATAL levels render with their names instead of slog's numeric fallback. WithText and WithJSON set this for you.
The custom levels
const LevelTrace = slog.Level(-8)
const LevelFatal = slog.Level(12)LevelTrace sits below slog.LevelDebug for the noisiest diagnostics, and LevelFatal sits above slog.LevelError. Every Logger has Trace and Fatal helpers that log at these levels. Logging at LevelFatal does not exit the process. It only emits a FATAL record, and the decision to exit stays with the caller.