๐Ÿ“‹Structured Logging with slogLESSON

Structured Logging with slog

Go 1.21 added log/slog to the standard library โ€” a structured, levelled logger designed to produce machine-parseable output while still being readable by humans.

Why structured logging?

Structured logs let you filter across millions of events with queries like userID == 42 instead of regex scraping.

Basic usage

The default logger writes to stderr in a human-readable format:

Each call takes a message followed by alternating key/value pairs.

Typed attributes

For performance-sensitive paths, use explicitly typed attributes to avoid reflection:

slog.String, slog.Int, slog.Duration, slog.Any โ€” these create slog.Attr values that carry type information without boxing.

Creating a custom logger

slog.New takes a Handler โ€” the component that formats and writes log records:

Pass a *slog.HandlerOptions as the second argument to customise the minimum level:

Log levels

LevelValueUse when
slog.LevelDebug-4Fine-grained diagnostics
slog.LevelInfo0Normal operation events
slog.LevelWarn4Unexpected but recoverable
slog.LevelError8Errors that need attention

The default logger's level is Info โ€” Debug messages are dropped unless you set a lower level.

Setting the default logger

Replace the global default logger with your custom one:

Adding shared context with With

logger.With returns a new logger that attaches fixed attributes to every subsequent log call โ€” perfect for request-scoped fields:

Knowledge Check

What is the key advantage of structured logging over fmt.Printf / log.Printf?

Which slog handler produces output suitable for log aggregators like Datadog or Splunk?

What does logger.With(...) return?