Analogia da caixa-preta de avião: quando um avião cai, ninguém acha o piloto e pergunta "o que aconteceu". Ele tá morto. Vão na caixa-preta — que registrou tudo. Logs, métricas e traces são a caixa-preta do seu backend.
Observabilidade tem 3 pilares:
- Logs — diário do que aconteceu.
- Métricas — números agregados (req/s, latência, erro %).
- Traces — caminho de uma request por todo o sistema.
Para começar, foco em logs estruturados e métricas básicas.
Logs estruturados
Por que estruturado? Porque fmt.Println("usuário 123 fez login") é inútil em escala. Como você busca? Como filtra? Logs estruturados são JSON, com campos. Aí sim ferramentas como Datadog, Loki, Elasticsearch fazem mágica.
Go 1.21+ trouxe log/slog na biblioteca padrão. É o padrão moderno:
package main
import (
"log/slog"
"os"
)
func main() {
// Logger que escreve JSON no stdout.
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
slog.SetDefault(logger)
slog.Info("servidor iniciado",
"porta", 8080,
"env", "production",
)
// Saída:
// {"time":"2025-...","level":"INFO","msg":"servidor iniciado","porta":8080,"env":"production"}
}Boas práticas de log:
- Use níveis corretos (
Debug,Info,Warn,Error). Não logue tudo comoInfo. - Nunca logue dados sensíveis — senha, token, CPF, cartão. Nunca. Mesmo. Em ambiente nenhum.
- Inclua um
request_idem cada log de request — isso permite rastrear a jornada inteira. - Loga onde a decisão acontece, não em todo lugar. Excesso de log polui mais do que ajuda.
- Erros com contexto:
slog.Error("falha ao salvar pedido", "order_id", id, "err", err).
Métricas simples
Métricas mínimas que todo backend deveria ter:
- RED: Rate (req/s), Errors (% erros), Duration (latência p50, p95, p99).
- Saúde de dependências: banco está respondendo? Redis está respondendo?
- Filas: quantos itens pendentes? Quanto tempo o item mais antigo está esperando?
A ferramenta mais comum em Go é Prometheus (cliente: prometheus/client_golang):
import "github.com/prometheus/client_golang/prometheus"
var (
httpRequests = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total de requests HTTP por método e status",
},
[]string{"method", "path", "status"},
)
httpDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "Duração de requests HTTP",
Buckets: prometheus.DefBuckets,
},
[]string{"method", "path"},
)
)
func init() {
prometheus.MustRegister(httpRequests, httpDuration)
}
// middleware que mede tudo
func metricsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rw := &statusRecorder{ResponseWriter: w, status: 200}
next.ServeHTTP(rw, r)
dur := time.Since(start).Seconds()
httpRequests.WithLabelValues(r.Method, r.URL.Path,
strconv.Itoa(rw.status)).Inc()
httpDuration.WithLabelValues(r.Method, r.URL.Path).Observe(dur)
})
}Depois é só expor o endpoint /metrics e apontar o Prometheus para ele.