Logging¶
Burrow uses Go's standard log/slog package. The framework logs to slog.Default() — it does not configure the logger itself. Your application is responsible for setting up the slog handler before starting the server.
Setting Up the Logger¶
Configure slog.SetDefault() in your main() function before calling srv.Run:
Text Output (development)¶
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
})))
Output:
time=2026-03-11T10:00:00.000Z level=INFO msg="starting server" host=localhost port=8080
time=2026-03-11T10:00:01.123Z level=INFO msg="request" method=GET path=/ status=200
JSON Output (production)¶
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
})))
Output:
{"time":"2026-03-11T10:00:00.000Z","level":"INFO","msg":"starting server","host":"localhost","port":8080}
JSON output is useful when shipping logs to aggregators like Loki, Datadog, or Elasticsearch.
Third-Party Handlers¶
Any slog.Handler works. Popular choices:
- tint — colorized terminal output for development
- slog-multi — fan-out to multiple handlers
// Example: tint for colorized dev output
slog.SetDefault(slog.New(
tint.NewHandler(os.Stdout, &tint.Options{Level: slog.LevelDebug}),
))
What Burrow Logs¶
The framework logs at these points:
| Event | Level | Message |
|---|---|---|
| Server start | INFO | starting server with host, port, TLS mode |
| HTTP requests | INFO | Method, path, status, duration (via httplog) |
| Dependency reorder | WARN | When app registration order is adjusted |
| Shutdown errors | ERROR | Failed app shutdown, HTTP server errors |
| Database close errors | ERROR | Failed to close database connection |
Your app code can log using slog directly — it shares the same default logger:
func (a *App) Create(w http.ResponseWriter, r *http.Request) error {
// ...
slog.Info("note created", "note_id", note.ID, "user_id", user.ID)
// ...
}
Logging in Deployment¶
All log output goes to stdout. Both systemd and Docker capture stdout automatically:
- systemd — logs are captured by journald, queryable with
journalctl -u myapp - Docker — logs are captured by the container runtime, queryable with
docker logs
No file-based logging configuration is needed. To persist logs beyond the journal or container lifecycle, configure your log aggregator to read from journald or the Docker log driver.
Log Level Configuration¶
Burrow intentionally does not provide a --log-level CLI flag. Log level is configured in your main.go when you create the slog handler:
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo, // Change to slog.LevelDebug for development
})))
This keeps logging configuration in Go code where it belongs, rather than adding a framework-specific flag that duplicates slog's built-in capabilities. If you need runtime log level changes, use slog.LevelVar with an atomic level: