Você já viveu o caos de um incidente em produção na madrugada, com APIs intermitentes, dashboards confusos e um oceano de logs que não dizem nada? Este guia prático e atualizado para 2026 mostra como montar uma observabilidade de ponta a ponta com OpenTelemetry em microsserviços no Kubernetes, cobrindo métricas, traces e logs, com instrumentação em Go e Java e integração com Prometheus e Jaeger. Se você precisa de respostas rápidas e confiáveis em cenários reais, aqui estão as decisões, armadilhas e ajustes que realmente funcionam em produção.
OpenTelemetry (OTel) unifica como coletamos dados de telemetria: uma única especificação para traces, metrics e logs, com SDKs e um Collector flexível. Em vez de acoplar sua aplicação a agentes proprietários, você envia tudo via OTLP e escolhe o melhor destino para visualização e retenção (Prometheus, Jaeger, entre outros). O ganho real não é só técnico: é independência de fornecedor, redução de vendor lock-in e padronização entre linguagens e times.
Em microsserviços, a evolução é inevitável: novos pods, novas versões, novos fluxos assíncronos. OTel fornece contexto distribuído para atravessar hop por hop, enquanto o Collector atua como um hub de roteamento, processamento e amostragem. Pense nele como um intercâmbio de dados: recebe telemetria dos aplicativos, enriquece, reduz ruído e entrega no formato certo para cada backend.
Fluxo recomendado: Aplicações enviam via OTLP para o Collector. O Collector aplica processors (ex.: k8sattributes, batch, tail_sampling), e encaminha para Prometheus (via prometheusremotewrite ou exporter que expõe /metrics) e para Jaeger (via otlp ou jaeger exporter do Collector). Assim você centraliza a lógica e minimiza reconfiguração nos serviços.
Dica de ouro: comece com head sampling baixo (ex.: 5–10%) para custo previsível e adote tail sampling gradualmente para casos críticos (erros 5xx, latências p95 acima do SLO, rotas de checkout). O ganho de sinal-ruído é enorme.
No Go, use o SDK oficial com propagadores W3C (tracecontext e baggage) e instrumentações para net/http e grpc. O padrão de projeto é simples: inicialize o TracerProvider apontando para o endpoint OTLP do Collector, envolva seus handlers com middleware e propague o context.Context de ponta a ponta. Para métricas, configure um MeterProvider e crie contadores, histogramas e up-down counters para filas e conexões.
O truque que evita dor: nomeie operações consistentemente (ex.: http.server.request, grpc.server, repository.query) e anexe atributos estáveis (service.name, deployment.environment, http.route sem IDs voláteis). Em gRPC, adicione interceptors em cliente e servidor para preservar o contexto.
Para logs em Go, você pode integrar seu logger (ex.: zerolog, zap) para emitir no formato OTLP via Collector com filelog receiver ou otlp diretamente. Unifique o trace_id no log chamando o logger a partir do contexto ativo; isso torna a correlação instantânea no Jaeger e nas buscas de logs.
No Java, o caminho rápido é o opentelemetry-javaagent. Ele instrumenta frameworks populares (Spring, JAX-RS, JDBC, Kafka) sem modificar seu código. Configure via variáveis de ambiente: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_RESOURCE_ATTRIBUTES e OTEL_TRACES_SAMPLER. Para quem usa Spring Boot, combinar o agente com exemplos de negócios (spans manuais em trechos críticos) dá visibilidade real sem ruído.
Se precisar de controle fino, use o SDK programático para criar spans customizados onde a latência explode (ex.: serialização JSON, chamadas a serviços legados, cache misses). Para métricas no Java moderno, exponha histogramas para latências, counters para throughput e gauge observables para filas. Evite cardinalidade explosiva em atributos de métricas, principalmente com labels de usuário ou ID de pedido.
Insight prático: auto-instrumentação acelera o “time-to-value”, mas sempre valide o mapeamento de rotas e operações. Rotas mal normalizadas explodem a cardinalidade em Prometheus e destroem seus índices de métricas.
Escolha uma abordagem e evite duplicidade. Se expor métricas no app e no Collector, você pode acabar com dados duplicados e confusão em consultas.
Regra de ouro: limite labels de alta cardinalidade. IDs, IPs e user-agent bruto viram um tsunami de séries. Normalize strings e categorize quando possível.
Configure o Collector com otlp receiver e jaeger exporter (ou otlp exporter apontando para o Jaeger com OTLP habilitado). Isso garante compatibilidade e processamento intermediário (amostragem, enriquecimento, máscaras de dados sensíveis) antes da persistência.
Para resultados consistentes, padronize service.name, deployment.environment e service.version. Sem esses atributos, sua busca no Jaeger vira caça ao tesouro.
Aprendizado de campo: ative exemplars nas métricas de latência e erro para vincular pontos anômalos diretamente a trace_ids no Jaeger. O salto do gráfico ao trace certo economiza minutos preciosos em incidentes.
Adote logs estruturados em JSON com trace_id e span_id. O Collector pode ingerir via filelog receiver (lendo arquivos nos pods) ou via otlp direto dos serviços. Use k8sattributes para adicionar namespace/pod