← Voltar para o blog

slog em Go: Logging Estruturado com a Biblioteca Padrão

Aprenda slog em Go: logging estruturado com TextHandler, JSONHandler, log levels, atributos, handlers customizados e integração com context na prática.

O pacote log/slog foi introduzido no Go 1.21 como a solução oficial para logging estruturado na biblioteca padrão. Antes dele, o pacote log era limitado a mensagens de texto simples, forçando a maioria dos projetos a depender de bibliotecas externas como zerolog ou zap. Com slog, você tem logging estruturado nativo, de alta performance, pronto para produção.

Por que Logging Estruturado?

Logs tradicionais em texto puro funcionam para debugging local, mas em ambientes de produção com centenas de serviços, você precisa de logs que sejam pesquisáveis, filtráveis e agregáveis. Logging estruturado emite cada entrada como um conjunto de pares chave-valor, facilitando a ingestão por ferramentas como Elasticsearch, Grafana Loki ou Datadog.

// Log tradicional — difícil de parsear automaticamente
log.Printf("usuário %s fez login em %s", userID, time.Now())

// Log estruturado com slog — facilmente parseável
slog.Info("usuário fez login",
    slog.String("user_id", userID),
    slog.Time("timestamp", time.Now()),
)

A saída estruturada permite que você filtre logs por user_id, agrupe por nível de severidade e correlacione eventos entre microsserviços — algo essencial em arquiteturas distribuídas como as que discutimos no guia de APIs REST em Go.

Primeiros Passos com slog

O pacote slog vem com dois handlers prontos: TextHandler para saída legível e JSONHandler para saída estruturada em JSON.

package main

import (
    "log/slog"
    "os"
)

func main() {
    // Handler de texto (bom para desenvolvimento)
    textHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelDebug,
    })
    logger := slog.New(textHandler)
    logger.Info("servidor iniciando", slog.Int("porta", 8080))
    // Saída: time=2026-03-25T10:00:00.000-03:00 level=INFO msg="servidor iniciando" porta=8080

    // Handler JSON (ideal para produção)
    jsonHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelInfo,
    })
    prodLogger := slog.New(jsonHandler)
    prodLogger.Info("servidor iniciando", slog.Int("porta", 8080))
    // Saída: {"time":"2026-03-25T10:00:00.000-03:00","level":"INFO","msg":"servidor iniciando","porta":8080}
}

Para definir um logger como padrão global, use slog.SetDefault(logger). Depois disso, chamadas diretas como slog.Info() usarão o handler que você configurou.

Níveis de Log

O slog define quatro níveis padrão, cada um com um valor numérico:

NívelValorUso
Debug-4Informações detalhadas para debugging
Info0Eventos normais de operação
Warn4Situações inesperadas mas recuperáveis
Error8Erros que precisam de atenção
slog.Debug("query executada", slog.String("sql", query), slog.Duration("duracao", elapsed))
slog.Info("requisição processada", slog.Int("status", 200))
slog.Warn("cache miss", slog.String("chave", key))
slog.Error("falha ao conectar", slog.String("erro", err.Error()))

Você pode criar níveis customizados entre os padrões. Por exemplo, slog.Level(2) ficaria entre Info e Warn — útil para diferenciar severidades em sistemas de tratamento de erros mais complexos.

Atributos e Grupos

Atributos tipados garantem que seus logs sejam consistentes. O slog oferece construtores como slog.String, slog.Int, slog.Bool, slog.Duration, slog.Any e outros:

slog.Info("pagamento processado",
    slog.String("transacao_id", txID),
    slog.Float64("valor", 199.90),
    slog.Bool("recorrente", true),
    slog.Duration("latencia", 45*time.Millisecond),
    slog.Any("metadados", metadata),
)

Para organizar atributos relacionados, use grupos:

slog.Info("requisição recebida",
    slog.Group("http",
        slog.String("metodo", "POST"),
        slog.String("path", "/api/usuarios"),
        slog.Int("status", 201),
    ),
    slog.Group("cliente",
        slog.String("ip", "192.168.1.100"),
        slog.String("user_agent", "Mozilla/5.0"),
    ),
)
// JSON: {"http":{"metodo":"POST","path":"/api/usuarios","status":201},"cliente":{"ip":"192.168.1.100",...}}

Loggers com Contexto

Uma das funcionalidades mais poderosas do slog é a integração com context.Context. Isso permite propagar informações como request ID e trace ID automaticamente por toda a cadeia de chamadas — algo fundamental quando você trabalha com context em Go.

func middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        requestID := uuid.New().String()

        // Cria logger com atributos fixos
        logger := slog.Default().With(
            slog.String("request_id", requestID),
            slog.String("metodo", r.Method),
            slog.String("path", r.URL.Path),
        )

        // Injeta no context
        ctx := context.WithValue(r.Context(), loggerKey, logger)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func handler(w http.ResponseWriter, r *http.Request) {
    logger := r.Context().Value(loggerKey).(*slog.Logger)
    logger.Info("processando requisição")
    // Todos os logs incluem request_id, metodo e path automaticamente
}

O método With() cria um novo logger que inclui os atributos em todas as mensagens subsequentes, sem necessidade de repeti-los.

A Interface LogValuer

Quando você precisa logar structs complexas, implemente a interface LogValuer para controlar como o objeto aparece nos logs:

type Usuario struct {
    ID    string
    Nome  string
    Email string
    Senha string // nunca deve aparecer nos logs
}

func (u Usuario) LogValue() slog.Value {
    return slog.GroupValue(
        slog.String("id", u.ID),
        slog.String("nome", u.Nome),
        // Email e Senha omitidos intencionalmente
    )
}

// Uso
slog.Info("usuário criado", slog.Any("usuario", usuario))
// Saída segura: sem senha ou dados sensíveis

Criando um Handler Customizado

Para cenários avançados, você pode criar seu próprio handler implementando a interface slog.Handler:

type ColorHandler struct {
    handler slog.Handler
    writer  io.Writer
}

func (h *ColorHandler) Enabled(ctx context.Context, level slog.Level) bool {
    return h.handler.Enabled(ctx, level)
}

func (h *ColorHandler) Handle(ctx context.Context, r slog.Record) error {
    var cor string
    switch {
    case r.Level >= slog.LevelError:
        cor = "\033[31m" // vermelho
    case r.Level >= slog.LevelWarn:
        cor = "\033[33m" // amarelo
    default:
        cor = "\033[0m" // padrão
    }
    fmt.Fprintf(h.writer, "%s[%s]\033[0m %s\n", cor, r.Level, r.Message)
    return nil
}

func (h *ColorHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
    return &ColorHandler{handler: h.handler.WithAttrs(attrs), writer: h.writer}
}

func (h *ColorHandler) WithGroup(name string) slog.Handler {
    return &ColorHandler{handler: h.handler.WithGroup(name), writer: h.writer}
}

slog em HTTP Middleware

Na prática, slog brilha quando integrado ao middleware HTTP para logar cada requisição automaticamente:

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        inicio := time.Now()

        // Wrapper para capturar o status code
        rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}

        next.ServeHTTP(rw, r)

        slog.Info("requisição completada",
            slog.String("metodo", r.Method),
            slog.String("path", r.URL.Path),
            slog.Int("status", rw.statusCode),
            slog.Duration("duracao", time.Since(inicio)),
            slog.String("ip", r.RemoteAddr),
        )
    })
}

Esse padrão se aplica diretamente quando você constrói APIs REST — veja nosso guia sobre APIs REST em Go para um exemplo completo.

Performance: slog vs zerolog vs zap

O slog foi projetado para ser rápido, mas como se compara com as alternativas consolidadas?

BibliotecaAlocações por logLatência
slog (JSON)0-1~200ns
zerolog0~150ns
zap0-1~180ns
log (padrão)1-2~300ns

O slog é competitivo com zap e ligeiramente mais lento que zerolog, mas a vantagem de ser parte da biblioteca padrão compensa na maioria dos cenários. Não há dependências externas para gerenciar e a API é estável.

Para otimizar a performance, use atributos tipados (slog.String, slog.Int) em vez de slog.Any, e configure o nível mínimo de log corretamente para evitar processamento desnecessário em produção.

Migrando de log para slog

Se você já usa o pacote log padrão, a migração é gradual:

// 1. Configure slog como backend do log padrão
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, nil)))

// 2. Chamadas ao log padrão agora passam pelo slog
log.Println("isso agora é processado pelo slog")

// 3. Gradualmente substitua por chamadas diretas ao slog
slog.Info("versão melhorada", slog.String("contexto", "migração"))

Quando Usar slog

Use slog quando:

  • Você quer logging estruturado sem dependências externas
  • Precisa de integração nativa com context para rastreamento de requisições
  • Está começando um projeto novo em Go 1.21+
  • Quer uma API estável mantida pelo time do Go

Considere zerolog ou zap quando:

  • Precisa de zero alocações absolutas (hot path extremo)
  • Já tem uma base de código grande usando essas bibliotecas
  • Precisa de funcionalidades específicas como sampling ou sink rotation

Próximos Passos

O slog transforma a forma como fazemos observabilidade em Go. Combine com context para rastreamento distribuído, aplique em middleware HTTP para monitorar suas APIs, e use tratamento de erros estruturado para logs de erro mais informativos.

Se você também trabalha com Python, confira o guia de logging estruturado em Python para comparar abordagens. No ecossistema Rust, o crate tracing oferece funcionalidades semelhantes — veja mais em Rust Brasil.

Se você está explorando outros recursos modernos do Go, veja também como iteradores com range over func trazem novos padrões para processar dados — inclusive em pipelines de log customizados.

Para aprofundar nos fundamentos de concorrência em Go, que impacta diretamente como loggers devem ser thread-safe, confira nosso guia dedicado.