Logging Estruturado com slog
log/slog é o logger estruturado da biblioteca padrão do Go: campos chave-valor, saída com níveis e handlers plugáveis para JSON, texto ou sinks customizados.
Busque em todas as páginas da documentação
log/slog é o logger estruturado da biblioteca padrão do Go: campos chave-valor, saída com níveis e handlers plugáveis para JSON, texto ou sinks customizados.
Logging estruturado significa que cada linha carrega campos analisáveis por máquina (user_id, trace_id, err) em vez de texto livre.
Go 1.21 adicionou log/slog para substituir o log.Printf ad hoc e reduzir a dependência de loggers de terceiros para a maioria dos serviços.
Handlers controlam o formato de saída e a filtragem; métodos Logger anexam atributos contextuais que fluem para loggers filhos.
A migração de log e de zap geralmente começa encapsulando handlers e combinando os nomes de campo que sua plataforma de log já indexa.
Cartão de receita de referência rápida - pronto para copiar e colar.
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
AddSource: true,
}))
slog.SetDefault(logger)
slog.Info("pagamento capturado", "order_id", orderID, "amount_cents", amount)Quando usar isso:
*slog.Logger para logging injetávellog.Printf sem reescrever todos os pontos de chamada de uma vezpackage main
import (
"context"
"log/slog"
"net/http"
"os"
"time"
)
type ctxKey string
const loggerKey ctxKey = "logger"
func main() {
base := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
}))
slog.SetDefault(base)
mux := http.NewServeMux()
mux.Handle("GET /orders/{id}", injectLogger(base.With("service", "orders-api"))(
accessLog(http.HandlerFunc(getOrder)),
))
srv := &http.Server{Addr: ":8080", Handler: mux}
slog.Info("escutando", "addr", srv.Addr)
if err := srv.ListenAndServe(); err != nil {
slog.Error("servidor parado", "err", err)
os.Exit(1)
}
}
func injectLogger(l *slog.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqLog := l.With("request_id", r.Header.Get("X-Request-ID"))
ctx := context.WithValue(r.Context(), loggerKey, reqLog)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
func loggerFrom(ctx context.Context) *slog.Logger {
if l, ok := ctx.Value(loggerKey).(*slog.Logger); ok {
return l
}
return slog.Default()
}
func accessLog(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
loggerFrom(r.Context()).Info("requisição completa",
"method", r.Method,
"path", r.URL.Path,
"duration_ms", time.Since(start).Milliseconds(),
)
})
}
func getOrder(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
loggerFrom(r.Context()).Info("buscando pedido", "order_id", id)
w.Write([]byte(`{"id":"` + id + `"}`))
}O que isso demonstra:
With para request_id por requisição sem mutar o pai*slog.Logger transportado por contexto para handlers profundos na pilha de chamadasslog.Logger contém um Handler e atributos persistentes opcionais de With / WithGroup.Info, Warn, Error constrói um slog.Record (tempo, nível, mensagem, atributos) e o passa para o handler.Enabled, Handle e WithAttrs / WithGroup para imutabilidade ao derivar loggers filhos.slog.SetDefault configura o logger de todo o processo usado pelas chamadas de nível superior slog.Info.| Handler | Saída | Uso Típico |
|---|---|---|
TextHandler | texto chave=valor | Desenvolvimento local, visualização humana |
JSONHandler | linhas JSON | Agregadores de log de produção |
Handler customizado | Seu sink | Fan-out, redação, SDK de fornecedor |
| Nível | Valor | Quando |
|---|---|---|
Debug | -4 | Solução de problemas detalhada, apenas desenvolvimento |
Info | 0 | Operações normais |
Warn | 4 | Anomalias recuperáveis |
Error | 8 | Falhas que exigem atenção |
Defina HandlerOptions.Level para filtrar registros abaixo do limite antes que Handle seja executado.
// Campos caros preguiçosos - avaliados apenas se o nível estiver habilitado
slog.Info("snapshot", "payload", slog.LogValuerFunc(func() slog.Value {
return slog.StringValue(expensiveSerialize())
}))
// Grupos aninham atributos em JSON: {"request":{"method":"GET"}}
log := logger.WithGroup("request")
log.Info("in", "method", r.Method, "path", r.URL.Path)| De | Equivalente slog |
|---|---|
log.Printf | slog.Info com atributos |
log.Fatal | slog.Error + os.Exit(1) |
| campos zap | pares chave-valor slog ou LogValuer |
| configuração de produção zap | JSONHandler + Level: Info |
SetDefault - Logs iniciais em main usam o padrão de texto. Configure o handler antes que outros pacotes registrem.Handle customizado.Error - Use "err", err consistentemente; algumas plataformas mapeiam error para colunas especiais.AddSource ausente em produção - Linhas de origem ajudam na depuração, mas adicionam alocação; habilite seletivamente.slog.New(slog.NewTextHandler(&buf, nil)).| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
log/slog | Preferência por biblioteca padrão, QPS moderado | Você precisa do caminho rápido de alocação zero do zap em escala extrema |
| uber-go/zap | Ecossistema comprovado, JSON muito rápido | Você quer zero dependências de logging de terceiros |
| logrus | Bases de código legadas já o utilizam | Serviços Greenfield (projeto em modo de manutenção) |
| Apenas agente de plataforma | Serverless com captura automática | Você precisa de correlação customizada entre serviços |
Sim.
Ele está na biblioteca padrão desde Go 1.21 e é o padrão recomendado para novo código Go.
slog não anexa pilhas automaticamente.
Registre err e encapsule com fmt.Errorf("…: %w", err); use AddSource: true ou um handler customizado para arquivo/linha.
Sim.
Crie middleware que registra a partir do contexto *http.Request ou de um padrão de nível de pacote após SetDefault.
Escreva em bytes.Buffer via slog.NewTextHandler(&buf, nil) e afirme o conteúdo das substrings nos testes.
Ele agrupa atributos sob uma chave JSON, útil para estruturas aninhadas como sub-objetos http ou db.
Bibliotecas devem aceitar parâmetros *slog.Logger.
Aplicações podem chamar slog.SetDefault uma vez em main.
Encapsule o handler com um tipo que contenha um ponteiro de nível atomic, ou use um padrão de biblioteca como slog.LevelVar em exemplos do Go 1.22+.
Não.
A rotação é trabalho do gerenciador de processos, sidecar do contêiner ou plataforma (systemd, driver de log do Kubernetes).
Use TextHandler em desenvolvimento e JSONHandler em produção, selecionados por APP_ENV.
Existem handlers da comunidade; prefira um único logger em código novo para evitar emissão dupla e incompatibilidade de campos.
O pacote log antigo permanece para scripts mínimos.
slog o substitui para serviços com campos e níveis.
Evite por padrão.
Se necessário para depuração, limite o tamanho, redija campos e restrinja a requisições de debug amostradas.
Versões de Stack: Esta página foi escrita para Go 1.26.x (padrão Green Tea GC, go fix modernizers - verifique o patch na compilação), chi (última versão - verifique na compilação), gin (última versão - verifique na compilação), echo (última versão - verifique na compilação), google.golang.org/grpc (última versão - verifique na compilação), sigs.k8s.io/controller-runtime (última versão - verifique na compilação), kubebuilder (última versão - verifique na compilação), tinygo (última versão - verifique os alvos de placa na compilação), wazero (última versão - verifique na compilação) e golangci-lint (última versão - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 18 de jul. de 2026