Structured logging dengan slog (Go 1.21+)
Standard library `log/slog` untuk structured logging. JSON output, level, context-aware. Replace logrus/zerolog untuk kebanyakan use case.
Dipublikasikan 15 Mei 2026
Go 1.21 keluarkan log/slog — structured logging di stdlib. Sebelumnya saya pakai zerolog. Sekarang slog cukup untuk most use case, tanpa dependency tambahan.
Setup
package main
import (
"log/slog"
"os"
)
func main() {
// JSON output ke stderr, level Debug
logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
Level: slog.LevelDebug,
AddSource: true, // include file:line
}))
slog.SetDefault(logger)
slog.Info("server starting",
slog.String("addr", ":8080"),
slog.Int("workers", 4),
)
slog.Error("connection failed",
slog.String("host", "db.local"),
slog.Int("port", 5432),
slog.Any("error", errors.New("timeout")),
)
}
Output (formatted untuk readability):
{
"time": "2026-06-04T10:30:00+07:00",
"level": "INFO",
"source": {"function": "main.main", "file": "/app/main.go", "line": 15},
"msg": "server starting",
"addr": ":8080",
"workers": 4
}
Pattern: contextual logger per request
import (
"context"
"log/slog"
)
// Middleware: tambah request ID dan path ke logger
func WithLoggerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqID := uuid.New().String()
logger := slog.With(
slog.String("request_id", reqID),
slog.String("method", r.Method),
slog.String("path", r.URL.Path),
)
ctx := context.WithValue(r.Context(), "logger", logger)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// Handler: pakai contextual logger
func handler(w http.ResponseWriter, r *http.Request) {
logger := r.Context().Value("logger").(*slog.Logger)
logger.Info("processing request")
if err := doWork(); err != nil {
logger.Error("work failed", slog.Any("error", err))
http.Error(w, "Internal error", 500)
return
}
logger.Info("request done")
}
Setiap log dari handler include request_id, method, path — tanpa Anda pass explicit.
Levels
slog.Debug("only useful in dev", slog.Int("count", 42))
slog.Info("normal flow")
slog.Warn("something unusual", slog.String("reason", "rate limit near"))
slog.Error("recoverable error", slog.Any("err", err))
Set level dari env:
levelStr := os.Getenv("LOG_LEVEL")
var level slog.Level
switch levelStr {
case "debug": level = slog.LevelDebug
case "info": level = slog.LevelInfo
case "warn": level = slog.LevelWarn
case "error": level = slog.LevelError
default: level = slog.LevelInfo
}
handler := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: level})
slog.SetDefault(slog.New(handler))
Performance
Slog dengan JSON handler benchmark di laptop saya (M2):
- Simple log (5 attrs): ~1.2 µs/op, 1 alloc
- Complex log (15 attrs nested): ~5.3 µs/op, 4 allocs
Comparable dengan zerolog (yang dikenal cepat). Untuk high-throughput (10k req/sec), overhead total: ~1.2ms per 1000 log = negligible.
TextHandler untuk dev
JSON nggak nyaman dibaca di terminal. Untuk dev, pakai TextHandler:
var handler slog.Handler
if os.Getenv("ENV") == "production" {
handler = slog.NewJSONHandler(os.Stderr, opts)
} else {
handler = slog.NewTextHandler(os.Stderr, opts)
}
slog.SetDefault(slog.New(handler))
TextHandler output:
time=2026-06-04T10:30:00+07:00 level=INFO msg="server starting" addr=:8080 workers=4
Catatan
slog.With()di paket-level untuk tambah attribute permanent (e.g., service name, env)- Nested values via
slog.Group()untuk hierarchy:slog.Info("user signup", slog.Group("user", slog.String("id", userID), slog.String("email", email), )) - Don’t log secrets: implement
LogValue()method di struct yang punya sensitive field - Async logging (write to disk in background): pakai library tambahan (slogger, lumberjack) — slog stdlib synchronous
Migration dari logrus/zerolog
Mostly 1-to-1:
| logrus | slog |
|---|---|
log.WithFields(log.Fields{"k": v}).Info(msg) | slog.Info(msg, slog.Any("k", v)) |
log.WithError(err).Error("...") | slog.Error("...", slog.Any("err", err)) |
log.SetLevel(log.DebugLevel) | Set via handler options |
Untuk most project: migrate dalam 1-2 jam. Worth karena drop 1 dependency.
slog menjadi standard di Go ekosistem. Library baru (chi v6, fiber v3) sudah support slog adapter natively.
# tags
Ditulis oleh Asti Larasati · 15 Mei 2026