Instrumentar um serviço com OpenTelemetry
Ao final deste guia, o serviço emite traces, métricas e logs que se conectam pelo mesmo
trace_id — e você troca o backend de observabilidade mudando configuração, não código.
1. Comece pela instrumentação automática
Seção intitulada “1. Comece pela instrumentação automática”Antes de escrever qualquer span, ligue o que vem pronto: framework HTTP, cliente de banco, cliente de fila. Isso já rende a maior parte do valor.
# Pythonpip install opentelemetry-distro opentelemetry-exporter-otlpopentelemetry-bootstrap -a installopentelemetry-instrument python -m app # o processo passa a ser instrumentado
# Nodenpm i @opentelemetry/auto-instrumentations-node @opentelemetry/sdk-nodenode --require @opentelemetry/auto-instrumentations-node/register app.js
# Javajava -javaagent:opentelemetry-javaagent.jar -jar app.jarEm Go e Rust não há agente automático: use as bibliotecas de instrumentação do framework
(otelhttp, otelgin) — são poucas linhas e o efeito é o mesmo.
2. Configure o exportador por variável de ambiente
Seção intitulada “2. Configure o exportador por variável de ambiente”Os mesmos nomes valem em todas as linguagens, o que é justamente a graça do padrão.
export OTEL_SERVICE_NAME=pedidosexport OTEL_RESOURCE_ATTRIBUTES=service.namespace=loja,deployment.environment=prod,service.version=1.8.3export OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector.observabilidade:4317export OTEL_EXPORTER_OTLP_PROTOCOL=grpcexport OTEL_TRACES_SAMPLER=parentbased_traceidratioexport OTEL_TRACES_SAMPLER_ARG=0.1 # 10% para começarservice.version e deployment.environment parecem detalhe e são o que permite responder
“a latência subiu depois da 1.8.3?” — preencha desde o começo.
3. Suba o Collector
Seção intitulada “3. Suba o Collector”Mandar direto da aplicação para o backend funciona e amarra você a ele. O Collector no meio é o que dá liberdade — e é onde você redige dado sensível.
receivers: otlp: { protocols: { grpc: { endpoint: 0.0.0.0:4317 }, http: { endpoint: 0.0.0.0:4318 } } }processors: memory_limiter: { check_interval: 1s, limit_percentage: 75 } attributes/limpeza: actions: - { key: http.request.header.authorization, action: delete } - { key: usuario.email, action: hash } batch: { timeout: 5s, send_batch_size: 1024 }exporters: otlphttp/tempo: { endpoint: http://tempo:4318 } prometheusremotewrite: { endpoint: http://mimir:9009/api/v1/push }service: pipelines: traces: { receivers: [otlp], processors: [memory_limiter, attributes/limpeza, batch], exporters: [otlphttp/tempo] } metrics: { receivers: [otlp], processors: [memory_limiter, batch], exporters: [prometheusremotewrite] }Implante como DaemonSet (um por nó) para começar. memory_limiter primeiro e batch por
último é a ordem recomendada.
4. Acrescente spans manuais onde a lógica importa
Seção intitulada “4. Acrescente spans manuais onde a lógica importa”Só depois de a instrumentação automática estar funcionando, e só onde ela não alcança: regra de negócio, chamada a sistema legado, processamento em lote.
from opentelemetry import tracetracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("autorizar-pagamento") as span: span.set_attribute("gateway", gateway) # baixa cardinalidade: dá para agrupar span.set_attribute("pedido.tipo", tipo) try: resultado = gateway_client.autorizar(pedido) except GatewayError as e: span.record_exception(e) span.set_status(Status(StatusCode.ERROR), "gateway recusou") # não esqueça disto raiseMarcar o status de erro é o passo mais esquecido — e é o que faz o trace aparecer nas buscas por falha e na amostragem por cauda.
Atributo é para o que você vai filtrar ou agrupar. Nada de token, CPF, e-mail ou corpo de requisição.
5. Correlacione log e trace
Seção intitulada “5. Correlacione log e trace”Inclua o trace_id em todo log de requisição:
span = trace.get_current_span().get_span_context()logger.info("pedido criado", extra={ "trace_id": format(span.trace_id, "032x"), "span_id": format(span.span_id, "016x"), "pedido_id": pedido.id,})No Grafana, configure derivedFields no datasource do Loki para transformar esse campo em
link para o trace. A partir daí, log de erro e trace lento ficam a um clique um do outro.
6. Confira a amostragem
Seção intitulada “6. Confira a amostragem”# o trace chegou inteiro? procure pelo trace_id de um log recentecurl -s "http://tempo:3200/api/traces/<trace_id>" | jq '.batches | length'# o Collector está descartando algo?curl -s http://otel-collector:8888/metrics | grep -E 'otelcol_(receiver_accepted|exporter_sent|processor_dropped)'Comece com 10% e ajuste pelo volume. Quando o custo apertar, migre para amostragem por cauda no Collector — assim você guarda todo erro e toda cauda lenta, e amostra o resto: veja tracing distribuído.
Se der errado
Seção intitulada “Se der errado”| Sintoma | Causa provável |
|---|---|
| Nenhum dado chega | Endpoint ou protocolo errado (4317 é gRPC, 4318 é HTTP) |
| Trace quebrado em pedaços | Contexto não propagado: fila, cliente HTTP feito à mão, proxy removendo traceparent |
Serviço aparece como unknown_service |
OTEL_SERVICE_NAME ausente |
| Métricas sem chegar ao Prometheus | Pipeline de metrics faltando no Collector |
| Collector reiniciando | Sem memory_limiter, ou lote grande demais |
| Latência da aplicação piorou | Exportação síncrona ou fila cheia — use o processador em lote |
Checklist de pronto
Seção intitulada “Checklist de pronto”- Instrumentação automática ativa.
-
service.name,service.versionedeployment.environmentpreenchidos. - Collector no caminho, com redação de dado sensível.
- Spans manuais só onde agregam, com status de erro marcado.
-
trace_idpresente nos logs e link configurado no Grafana. - Amostragem definida e verificada.