Sua aplicação está funcionando, mas você tem certeza de que sabe o que está acontecendo internamente?
\n\nImagine: você implantou um microsserviço em Go em produção. Ele não cai, não retorna erros 500, mas os usuários reclamam que a página carrega "meio devagar". Você verifica o monitoramento — CPU 30%, memória normal. O que fazer? Sem observabilidade em Go, você é um capitão de navio sem mapa e bússola.
\n\nObservabilidade não é apenas "logs". São três pilares: tracing, métricas e logs. Juntos, eles lhe dão o superpoder de entender o que está acontecendo no sistema, mesmo que você não esperasse um problema. Neste artigo, vamos abordar cada um desses aspectos com exemplos de código em Go. Você não apenas lerá teoria — receberá receitas prontas que podem ser implementadas hoje mesmo.
\n\nPor que Go é a linguagem ideal para observabilidade?
\n\nGo foi criado para sistemas backend onde desempenho e confiabilidade são importantes. A biblioteca padrão (net/http, database/sql) já consegue fornecer métricas e suportar contextos para tracing. E o ecossistema OpenTelemetry Go e Prometheus Go torna a implementação de observabilidade simples e elegante.
Mas o principal é que em Go não há "mágica". Você escreve código que pode ser medido, rastreado e registrado sem dor. Vamos ver como isso é feito.
\n\nTracing em Go: OpenTelemetry em ação
\n\nTracing (rastreamento) é a história de uma requisição desde a entrada no sistema até a saída. Ele mostra quanto tempo cada etapa levou: consulta ao banco de dados, chamada a uma API externa, processamento de dados. Em Go, o tracing é construído sobre contextos (context.Context).
Configuração do SDK OpenTelemetry
\n\nVamos instalar os pacotes:
\ngo get go.opentelemetry.io/otel \\\n go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp \\\n go.opentelemetry.io/otel/sdk/trace\n\nAgora inicializamos o tracer:
\n\npackage main\n\nimport (\n "context"\n "log"\n "go.opentelemetry.io/otel"\n "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"\n "go.opentelemetry.io/otel/sdk/trace"\n)\n\nfunc initTracer() {\n exporter, err := otlptracehttp.New(context.Background())\n if err != nil {\n log.Fatal(err)\n }\n\n tp := trace.NewTracerProvider(\n trace.WithBatcher(exporter),\n trace.WithSampler(trace.AlwaysSample()),\n )\n otel.SetTracerProvider(tp)\n}\n\nCriando um span no manipulador HTTP
\n\nVeja como o tracing se parece no código real:
\n\nfunc userHandler(w http.ResponseWriter, r *http.Request) {\n tracer := otel.Tracer("user-service")\n ctx, span := tracer.Start(r.Context(), "getUser")\n defer span.End()\n\n // Simulação de consulta ao banco de dados\n user, err := getUserFromDB(ctx, r.URL.Query().Get("id"))\n if err != nil {\n span.RecordError(err)\n span.SetStatus(codes.Error, err.Error())\n http.Error(w, err.Error(), 500)\n return\n }\n\n span.SetAttributes(\n attribute.String("user.id", user.ID),\n attribute.Int64("user.age", int64(user.Age)),\n )\n\n // Span aninhado para chamada externa\n _, childSpan := tracer.Start(ctx, "callAnalyticsAPI")\n defer childSpan.End()\n\n callAnalyticsAPI(user)\n \n w.Write([]byte("OK"))\n}\n\nCada span contém nome, hora de início e fim, atributos e status. No Jaeger ou Grafana Tempo, você verá um mapa completo da requisição.
\n\nDica prática
\nNão se esqueça de passar o contexto por toda a aplicação. Em Go, isso é feito automaticamente se você usar context.Context nas assinaturas das funções. O OpenTelemetry Go capturará o contexto da requisição HTTP automaticamente se você usar middleware.
Métricas: Prometheus e exportação em Go
\n\nMétricas são números que indicam o estado do sistema: RPS, latência, quantidade de erros, uso