Introduction
Go 1.21 introduced log/slog, a structured logging package in the standard library. Unlike fmt.Printf or the old log package, slog outputs machine-parseable key-value pairs that integrate with log aggregation systems.
Key Concepts
- Structured logging: Logging with explicit key-value pairs instead of free-form strings, making logs searchable and filterable.
- Log levels:
Debug,Info,Warn,Error— each represents increasing severity. - Handler: The backend that formats log output.
slog.NewJSONHandlerproduces JSON;slog.NewTextHandlerproduces human-readable text. - slog.With(): Creates a child logger with pre-attached attributes, avoiding repetition across related log calls.
Real World Context
In production, plain-text logs are nearly useless. Log aggregation tools (Datadog, Grafana Loki, CloudWatch) need structured fields to filter, search, and alert. Using slog from the start means your logs are production-ready without a third-party library.
Deep Dive
The simplest usage passes key-value pairs after the message string.
goimport "log/slog" slog.Info("user login", "user_id", 42, "ip", "127.0.0.1")
This outputs structured data with user_id and ip as searchable fields.
Four log levels are available, each for a different severity.
slog.Debug()— detailed development informationslog.Info()— normal operational eventsslog.Warn()— unexpected but recoverable situationsslog.Error()— failures that need attention
For production, configure a JSON handler to output machine-parseable logs.
gologger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelInfo, })) slog.SetDefault(logger)
This produces output like the following, which log aggregation tools can parse automatically.
json{"time":"2024-01-01T00:00:00Z","level":"INFO","msg":"user login","user_id":42,"ip":"127.0.0.1"}
The JSON format makes every field independently queryable in your logging platform.
Common Pitfalls
- Using
fmt.Printffor logging in production — Free-form strings cannot be filtered or aggregated. Switch to slog for any code that will run in production. - Logging at the wrong level — Using
Errorfor non-critical events creates alert fatigue. ReserveErrorfor actual failures.
Best Practices
- Set the default logger at application startup — Call
slog.SetDefault()early so all packages use the same structured handler. - Use JSON handler in production, text handler in development — JSON is for machines; text is for humans reading terminal output.
Summary
log/slog(Go 1.21+) provides structured logging with key-value pairs.- Use
slog.NewJSONHandlerfor production andslog.NewTextHandlerfor development. - Four log levels (Debug, Info, Warn, Error) help filter by severity.
Code Examples
{"time":"2024-01-01T00:00:00Z","level":"INFO","msg":"user login","user_id":42,"ip":"127.0.0.1"}