OpenTelemetry: Observabilidade Unificada para PME em 2026
Neste artigo
Introdução: Metrics, Logs e Traces
O OpenTelemetry (OTel) é o padrão CNCF para observabilidade, unificando métricas, logs e traces num único framework open-source. Em 2026, é a solução de facto para pequenas e médias empresas (PME) que precisam de visibilidade sobre sistemas distribuídos sem ficar refém de vendors comerciais caros como Datadog ou New Relic.
Os três pilares da observabilidade são:
Métricas — dados numéricos agregados ao longo do tempo (CPU, memória, latency de requests, taxa de erros). Respondem à pergunta “o que está a acontecer?”. São leves e baratas de armazenar, ideais para dashboards e alertas em tempo real.
Logs — eventos discretos com timestamp (uma linha de log, uma mensagem de erro, uma entrada de auditoria). Respondem à pergunta “porque é que aconteceu?”. Contêm o contexto detalhado que métricas e traces não capturam.
Traces — o caminho completo de um request através de múltiplos serviços, dividido em spans. Respondem à pergunta “onde é que falhou?”. Permitem identificar o bottleneck exacto numa cadeia de microserviços, desde a entrada até à base de dados.
ℹ Porquê unificar os três pilares?
Antes do OTel, cada pilar usava uma ferramenta separada (Prometheus para métricas, ELK para logs, Jaeger para traces) com instrumentação manual e incompatível entre si. O OpenTelemetry unifica a recolha, processamento e exportação num só pipeline, reduzindo a complexidade operacional para PME que não têm equipas dedicadas de SRE.
Collector e Arquitectura
O OpenTelemetry Collector é o componente central da arquitectura. Recebe dados de múltiplas fontes (apps, infraestrutura, bibliotecas), processa-os (filtragem, transformação, batching) e exporta para um ou mais backends. É um binário único escrito em Go, sem dependências externas.
A arquitectura típica para uma PME tem três camadas:
1. Instrumentação — SDKs do OTel nas apps enviam dados via OTLP (OpenTelemetry Protocol) para o Collector. Suporta auto-instrumentação em Python, Node.js, Java, .NET e Go.
2. Collector — processo intermédio que recebe, processa e encaminha. Pode correr como sidecar em Kubernetes, container standalone em Docker, ou serviço em VM. Configurado via YAML.
3. Backends — Prometheus (métricas), Loki (logs), Tempo ou Jaeger (traces). Todos open-source e auto-alojável.
Configuração mínima do Collector para uma PME com Docker:
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 5s
send_batch_size: 1000
memory_limiter:
check_interval: 1s
limit_percentage: 80
spike_limit_percentage: 25
resource:
attributes:
- key: deployment.environment
value: produção
action: upsert
exporters:
prometheusremotewrite:
endpoint: http://prometheus:9090/api/v1/write
loki:
endpoint: http://loki:3100/loki/api/v1/push
otlp/tempo:
endpoint: tempo:4317
tls:
insecure: true
service:
pipelines:
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheusremotewrite]
logs:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [loki]
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/tempo]
O memory_limiter é obrigatório — sem ele, picos de tráfego podem fazer o Collector consumir toda a memória disponível e morrer com OOM kill. O batch agrupa dados antes de enviar, reduzindo o número de requests HTTP aos backends.
Para correr o Collector em Docker:
docker run -d --name otel-collector \
-p 4317:4317 -p 4318:4318 \
-v $(pwd)/otel-collector-config.yaml:/etc/otelcol/config.yaml \
otel/opentelemetry-collector-contrib:latest \
--config=/etc/otelcol/config.yaml
💡 Dica
Use a imagem otel/opentelemetry-collector-contrib e não a core. A versão contrib inclui todos os exporters (Loki, Prometheus remote write, etc.) que a core não tem.
Exportar para Prometheus e Loki
O Collector pode exportar métricas de duas formas para Prometheus: via prometheus exporter (Collector expõe um endpoint scrape) ou via prometheusremotewrite (Collector envia para o Prometheus). Para PME, o remote write é mais simples porque não requer configurar scrape configs no Prometheus.
Para activar remote write no Prometheus, adicionar ao prometheus.yml:
# prometheus.yml
remote_write:
- url: http://prometheus:9090/api/v1/write
# Garantir que o feature flag está activo
# Iniciar Prometheus com:
# --enable-feature=remote-write-receiver
scrape_configs:
- job_name: otel-collector
static_configs:
- targets: [otel-collector:8888]
scrape_interval: 15s
Para o Loki, o exporter envia logs directamente via HTTP push. Não é preciso configurar nada no Loki além do endpoint padrão. O Collector envia os logs com os atributos do OTel (service.name, trace_id, span_id) como labels do Loki, permitindo correlacionar logs com traces.
Um docker-compose típico para uma PME com toda a stack:
# docker-compose.yml
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
ports: ["4317:4317", "4318:4318"]
volumes:
- ./otel-collector-config.yaml:/etc/otelcol/config.yaml
prometheus:
image: prom/prometheus:latest
ports: ["9090:9090"]
command: --enable-feature=remote-write-receiver
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
loki:
image: grafana/loki:latest
ports: ["3100:3100"]
tempo:
image: grafana/tempo:latest
ports: ["4317"]
grafana:
image: grafana/grafana:latest
ports: ["3000:3000"]
environment:
GF_SECURITY_ADMIN_PASSWORD: mudar-esta-palavra-passe
O Grafana serve como painel único para visualizar métricas (Prometheus), logs (Loki) e traces (Tempo). A correlação entre os três pilares é automática porque o OTel propaga o trace_id entre métricas, logs e traces.
✓ Toda a stack em menos de 100MB de YAML
Com 4 containers (Collector, Prometheus, Loki, Grafana) uma PME tem observabilidade completa e auto-alojável. O custo de alojamento não excede 20€/mês num VPS com 2GB RAM.
Instrumentação de Apps
O OpenTelemetry oferece dois nˆveis de instrumentação: automática (zero code changes) e manual (spans e métricas customizadas). Para PME, começar com auto-instrumentação e ir adicionando instrumentação manual conforme necessário.
Python — auto-instrumentação via pacote opentelemetry-instrument:
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install
# Correr a app com instrumentação automática
opentelemetry-instrument \
--service_name minha-api \
--exporter otlp \
--endpoint http://otel-collector:4317 \
python app.py
Isto instrumenta automaticamente Flask, Django, FastAPI, requests, SQLAlchemy e Redis, gerando traces e mítricas sem alterar uma linha de código.
Node.js — auto-instrumentação via @opentelemetry/auto-instrumentations:
npm install @opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-grpc
# tracing.js - carregar ANTES da app
const { NodeSDK } = require("@opentelemetry/auto-instrumentations-node");
const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-grpc");
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: "http://otel-collector:4317",
}),
serviceName: "minha-api-node",
});
sdk.start();
Carregar com node --require ./tracing.js app.js. O ficheiro tracing.js tem de ser carregado antes de qualquer outra importação para que os hooks de instrumentação funcionem.
Instrumentação manual para adicionar spans customizados em Python:
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
def processar_pagamento(pedido_id):
with tracer.start_as_current_span("processar_pagamento") as span:
span.set_attribute("pedido.id", pedido_id)
span.set_attribute("pagamento.método", "cartão")
# lógica de negócio
resultado = gateway_cobrar(pedido_id)
span.set_attribute("pagamento.status", resultado.status)
span.record_exception(resultado.erro) if resultado.erro else None
return resultado
Os atributos customizados aparecem no trace no Grafana Tempo, permitindo filtrar e procurar pagamentos por ID, método ou status. Isto é a vantagem real do OTel: contexto estruturado que não existe em logs simples.
⚠ Atenção ao overhead
A instrumentação manual com muitos atributos por span pode adicionar 5-15% de overhead. Para PME com tráfego moderado, não é problemático. Para alto tráfego, usar batch processor e sampling (ex: traceidratio com ratio 0.1) para reduzir volume.
Erros Comuns
A tabela abaixo resume os erros mais frequentes na implementação do OpenTelemetry em PME, com causa e solução:
| Problema | Causa | Solução |
|---|---|---|
| Sem traces no backend | Endpoint errado (4317 gRPC vs 4318 HTTP) | Confirmar protocolo: gRPC usa 4317, HTTP usa 4318 |
| Collector morre com OOM | Falta memory_limiter processor | Adicionar memory_limiter no início de cada pipeline |
| Logs sem trace_id no Loki | Logs não instrumentados com OTel context | Usar OTel logging SDK ou inject trace_id manualmente |
| Métricas duplicadas no Prometheus | Scrape + remote write activos em simultâneo | Usar só remote write OU só scrape, nunca ambos |
| Latência alta na app após OTel | Export síncrono a bloquear requests | Configurar export assíncrono com batch processor |
| Spans não se propagam entre serviços | Falta propagação de contexto (W3C TraceContext) | Garantir que SDK injecta traceparent header |
| Collector não arranca | Imagem core em vez de contrib (sem exporters) | Usar opentelemetry-collector-contrib:latest |
| Volume de traces excessivo | Sampling a 100% em produção | Configurar traceidratio com 0.05-0.1 em produção |
O erro mais comum em PME é o primeiro: usar o endpoint errado. O OTLP tem dois portos — 4317 para gRPC e 4318 para HTTP. Misturar os portos resulta em traces que nunca chegam ao Collector, sem qualquer erro visível. Verificar sempre com otelcol debug logs (--log-level=debug).