Como implementar observabilidade com OpenTelemetry
1. Fundamentos do OpenTelemetry e a Tríade da Observabilidade
1.1. O que é OpenTelemetry
OpenTelemetry (OTel) é um conjunto de APIs, SDKs e ferramentas de código aberto para gerar, coletar e exportar dados de telemetria. Criado a partir da fusão do OpenTracing e OpenCensus em 2019, sob governança da CNCF (Cloud Native Computing Foundation), tornou-se o padrão da indústria para observabilidade. Sua principal vantagem é fornecer uma especificação unificada que evita vendor lock-in.
1.2. Os três pilares unificados
O OpenTelemetry unifica os três pilares clássicos da observabilidade:
- Tracing distribuído: rastreamento de requisições através de microsserviços
- Métricas: dados numéricos agregados (contadores, histogramas)
- Logs: registros estruturados com contexto
A unificação permite correlação entre esses sinais, algo que ferramentas isoladas não oferecem.
1.3. Arquitetura geral
A arquitetura do OpenTelemetry segue este fluxo:
Aplicação → SDK OTel → Collector → Backends (Prometheus, Jaeger, Loki)
Componentes principais:
- SDKs: bibliotecas que instrumentam a aplicação
- Exporters: enviam dados para destinos específicos
- Collector: pipeline central de processamento e roteamento
2. Instrumentação Automática vs. Manual
2.1. Instrumentação automática
Para linguagens com suporte a agentes, a instrumentação automática é a forma mais rápida de começar. Exemplo com Java:
# Configuração do agente Java OTel
export OTEL_SERVICE_NAME=meu-servico
export OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318
java -javaagent:opentelemetry-javaagent.jar -jar minha-app.jar
Para Python:
pip install opentelemetry-distro
opentelemetry-bootstrap -a install
export OTEL_PYTHON_LOG_CORRELATION=true
python minha_app.py
2.2. Instrumentação manual
Para cenários que exigem controle granular, a instrumentação manual permite criar spans customizados:
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("processar_pagamento") as span:
span.set_attribute("valor", 150.00)
span.add_event("inicio_processamento")
# lógica de negócio
span.set_status(trace.StatusCode.OK)
2.3. Boas práticas para evitar overhead
- Use sampling adaptativo: colete 100% dos traces de erro, mas apenas 10% dos bem-sucedidos
- Evite spans excessivamente granulares (ex: um span por linha de código)
- Configure timeouts nos exporters para não bloquear a aplicação
3. Configuração do OpenTelemetry Collector
3.1. Instalação básica
# docker-compose.yml
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
3.2. Configuração do pipeline
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 1s
send_batch_size: 1024
memory_limiter:
check_interval: 1s
limit_mib: 512
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
loki:
endpoint: http://loki:3100/loki/api/v1/push
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [jaeger]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]
logs:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [loki]
3.3. Processamento avançado
Sampling baseado em tail para traces longos:
processors:
tail_sampling:
decision_wait: 30s
policies:
- name: error-policy
type: status_code
status_code: ERROR
- name: slow-policy
type: latency
latency_threshold_ms: 500
4. Rastreamento Distribuído (Tracing)
4.1. Criação de spans com contexto
import { trace, context } from '@opentelemetry/api';
const tracer = trace.getTracer('meu-servico');
function handler(req, res) {
const span = tracer.startSpan('GET /api/users');
span.setAttribute('user.id', req.userId);
context.with(trace.setSpan(context.active(), span), () => {
// código que propaga contexto automaticamente
span.end();
});
}
4.2. Propagação W3C Trace Context
O OpenTelemetry implementa o padrão W3C Trace-Context, que adiciona headers HTTP automaticamente:
# Headers propagados
traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
tracestate: congo=t61rcWkgMzE
4.3. Depuração com flame graphs
Use o Jaeger para visualizar trace waterfalls:
# Consulta no Jaeger UI
service=meu-servico AND http.status_code>=500
5. Métricas Customizadas e Alertas
5.1. Criação de métricas com API OTel
from opentelemetry import metrics
meter = metrics.get_meter(__name__)
request_counter = meter.create_counter(
"requests_total",
description="Total de requisições",
unit="1"
)
def process_request():
request_counter.add(1, {"endpoint": "/api/users"})
5.2. Exportação para Prometheus
# Métricas expostas pelo Collector
# HELP requests_total Total de requisições
# TYPE requests_total counter
requests_total{endpoint="/api/users"} 42
5.3. Transformando em alertas
# Regra Prometheus
groups:
- name: otel-alerts
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status="500"}[5m]) > 0.05
for: 2m
labels:
severity: critical
6. Logs Estruturados e Correlação
6.1. Configuração de logging estruturado
# app.py
import logging
from opentelemetry import trace
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(trace_id)s] %(message)s'
)
span = trace.get_current_span()
logging.info("Pedido processado", extra={
"trace_id": format(span.get_span_context().trace_id, '032x')
})
6.2. Pipeline de logs para Loki
# Configuração do Collector para logs
exporters:
loki:
endpoint: http://loki:3100/loki/api/v1/push
labels:
attributes:
- service.name
- trace_id
7. Operação e Troubleshooting
7.1. Monitoramento do Collector
# Métricas do próprio Collector
otelcol_process_uptime_seconds
otelcol_exporter_sent_spans
otelcol_receiver_accepted_spans
7.2. Debugging comum
Problemas frequentes:
- Spans perdidos: verifique se o batch processor tem timeout adequado
- Alta latência: use memory_limiter para evitar OOM
- Erros de exportação: configure retry e circuit breaker
7.3. Versionamento de configurações
# Mantenha versões do config em git
git tag v1.0.0-otel-config
git checkout v1.0.0-otel-config -- otel-collector-config.yaml
8. Evolução Contínua: Do MVP ao Padrão Corporativo
8.1. Roadmap gradual
- Fase 1 (MVP): Tracing automático em 2-3 serviços críticos
- Fase 2: Adicionar métricas customizadas e dashboards
- Fase 3: Logs estruturados com correlação
- Fase 4: Sampling adaptativo e governança de custos
8.2. Controle de custos
# Sampling adaptativo no Collector
processors:
probabilistic_sampler:
hash_seed: 42
sampling_percentage: 10
8.3. SLOs e dashboards unificados
# Exemplo de SLI com métricas OTel
sli: rate(http_requests_total{status=~"2.."}[5m]) / rate(http_requests_total[5m])
slo: 99.9%
Referências
- OpenTelemetry Official Documentation — Documentação completa da CNCF com guias de instrumentação, configuração do Collector e especificações
- OpenTelemetry Collector Contrib — Repositório oficial com componentes adicionais do Collector (receivers, processors, exporters)
- Prometheus Documentation - Alerting Rules — Guia para criação de alertas baseados em métricas exportadas pelo OpenTelemetry
- Jaeger Documentation - Trace Visualization — Documentação do Jaeger para análise de traces distribuídos e flame graphs
- Loki Documentation - LogQL — Referência para consultas de logs estruturados correlacionados com traces
- W3C Trace Context Specification — Especificação oficial do padrão de propagação de contexto entre serviços
- CNCF OpenTelemetry Project — Página oficial do projeto na Cloud Native Computing Foundation com histórico e governança