---
title: "OpenTelemetry com Python: Logs, Métricas e Traces"
url: "https://python.dev.br/blog/opentelemetry-python-observabilidade/"
markdown_url: "https://python.dev.br/blog/opentelemetry-python-observabilidade.MD"
description: "Aprenda a usar OpenTelemetry em Python para instrumentar uma API FastAPI com traces, métricas, logs correlacionados e boas práticas de observabilidade."
date: "2026-05-25"
author: "Equipe Python Brasil"
---

# OpenTelemetry com Python: Logs, Métricas e Traces

Aprenda a usar OpenTelemetry em Python para instrumentar uma API FastAPI com traces, métricas, logs correlacionados e boas práticas de observabilidade.


Observabilidade deixou de ser um luxo de times grandes. Em 2026, muita vaga de backend, dados, SRE, plataforma e IA já espera que a pessoa desenvolvedora saiba responder perguntas simples sobre um sistema em produção: a requisição falhou onde? Qual endpoint ficou lento? Quantas chamadas deram erro depois do deploy? O problema está no banco, em uma API externa ou no próprio código Python?

Logs soltos ajudam, mas não contam a história completa. Métricas mostram volume e latência, mas nem sempre explicam a causa. Traces mostram o caminho de uma requisição, mas precisam estar ligados aos logs para serem úteis no dia a dia. É aí que entra o **OpenTelemetry**, um padrão aberto para coletar sinais de observabilidade sem prender seu projeto a uma ferramenta específica.

Neste guia, vamos montar uma base prática de observabilidade em Python com **FastAPI**, `opentelemetry-sdk`, instrumentação automática e exemplos de logs correlacionados. Se você ainda está estruturando uma API, leia primeiro [API com FastAPI](/guias/criando-api-fastapi/). Se quer fortalecer a base antes de produção, veja também [logging em Python](/blog/logging-em-python/), [testes com pytest](/guias/testes-com-pytest/) e [deploy de aplicação Python](/blog/deploy-aplicacao-python/).

## O que o OpenTelemetry resolve

OpenTelemetry é um conjunto de especificações, bibliotecas e ferramentas para gerar, coletar e exportar telemetria. Na prática, ele padroniza três sinais principais:

- **traces**: mostram o caminho de uma requisição entre funções, serviços e dependências externas;
- **métricas**: registram contadores, histogramas, latência, taxa de erro e uso de recursos;
- **logs**: guardam eventos textuais ou estruturados que explicam decisões do sistema.

A vantagem é que seu código não precisa depender diretamente de uma plataforma específica. Você pode exportar para um collector, Prometheus, Grafana, Jaeger, Tempo, Datadog, New Relic, Honeycomb ou outra ferramenta compatível. Para um projeto de portfólio, isso já passa uma mensagem forte: você não escreveu apenas uma API que responde `200 OK`; você pensou em como operá-la.

## Exemplo de projeto

Vamos imaginar uma API simples de pedidos:

```text
api-pedidos/
  app/
    main.py
    observability.py
    orders.py
  pyproject.toml
  README.md
```

Instale as dependências principais:

```bash
python -m venv .venv
source .venv/bin/activate
pip install fastapi uvicorn opentelemetry-sdk opentelemetry-api
pip install opentelemetry-instrumentation-fastapi opentelemetry-exporter-otlp
```

Em produção, é comum enviar dados para um OpenTelemetry Collector usando OTLP. No ambiente local, você pode começar exportando traces no console para entender o formato antes de ligar Grafana Tempo, Jaeger ou uma plataforma SaaS.

## Configurando traces

Crie um arquivo `app/observability.py` para concentrar a configuração. Isso evita espalhar setup de telemetria pela aplicação.

```python
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter


def configurar_tracing(service_name: str, ambiente: str = "local") -> None:
    resource = Resource.create({
        "service.name": service_name,
        "deployment.environment": ambiente,
    })

    provider = TracerProvider(resource=resource)

    if ambiente == "local":
        exporter = ConsoleSpanExporter()
    else:
        exporter = OTLPSpanExporter()

    provider.add_span_processor(BatchSpanProcessor(exporter))
    trace.set_tracer_provider(provider)
```

Esse código define dois atributos importantes. `service.name` identifica a aplicação nos painéis, enquanto `deployment.environment` separa local, staging e produção. Parece detalhe, mas sem isso você mistura dados de teste com dados reais e dificulta qualquer investigação.

## Instrumentando FastAPI

Agora conecte a instrumentação automática no `main.py`:

```python
import os
import time

from fastapi import FastAPI, HTTPException
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor

from app.observability import configurar_tracing


ambiente = os.getenv("APP_ENV", "local")
configurar_tracing("api-pedidos-python", ambiente)

app = FastAPI(title="API de Pedidos")
FastAPIInstrumentor.instrument_app(app)

tracer = trace.get_tracer(__name__)


@app.get("/pedidos/{pedido_id}")
def buscar_pedido(pedido_id: int):
    with tracer.start_as_current_span("buscar_pedido_no_banco") as span:
        span.set_attribute("pedido.id", pedido_id)
        time.sleep(0.05)

        if pedido_id == 404:
            span.set_attribute("pedido.encontrado", False)
            raise HTTPException(status_code=404, detail="Pedido não encontrado")

        span.set_attribute("pedido.encontrado", True)
        return {"id": pedido_id, "status": "pago", "total": 149.90}
```

Execute a API:

```bash
uvicorn app.main:app --reload
```

Ao acessar `http://localhost:8000/pedidos/123`, a instrumentação do FastAPI cria spans para a requisição HTTP. O span manual `buscar_pedido_no_banco` adiciona contexto de negócio. Em um sistema real, você poderia criar spans para consulta SQL, chamada HTTP externa, validação antifraude, envio para fila ou geração de nota fiscal.

## O que colocar em atributos

A regra é: atributo bom ajuda a filtrar investigação sem vazar dados sensíveis. Bons exemplos:

- nome do endpoint;
- método HTTP;
- código de status;
- ambiente;
- versão do serviço;
- tipo de operação;
- identificador interno não sensível;
- nome da fila, tabela ou integração.

Evite CPF, e-mail, telefone, endereço, token, payload completo e qualquer dado pessoal desnecessário. Observabilidade não pode virar vazamento organizado. Para projetos brasileiros, esse cuidado conversa diretamente com LGPD e com o bom senso de produção.

Um padrão seguro é registrar `pedido.id` se ele for um identificador interno, mas não registrar `cliente.cpf`. Se precisar depurar um caso específico, use uma estratégia controlada, com permissão, retenção curta e mascaramento.

## Logs correlacionados com traces

Logs continuam úteis, principalmente para decisões de negócio e mensagens de erro. O ganho vem de incluir o `trace_id` e o `span_id`, permitindo sair de um log para o trace completo.

```python
import logging

from opentelemetry import trace


logger = logging.getLogger("api-pedidos")


def log_info(mensagem: str, **campos: object) -> None:
    span = trace.get_current_span()
    contexto = span.get_span_context()

    logger.info(
        mensagem,
        extra={
            "trace_id": format(contexto.trace_id, "032x"),
            "span_id": format(contexto.span_id, "016x"),
            **campos,
        },
    )
```

Em um projeto pequeno, você pode começar com `logging` padrão. Em um projeto maior, vale estudar logs em JSON com `structlog` ou configuração de `logging.Formatter` própria. O ponto não é a biblioteca, e sim a disciplina: logar eventos relevantes, com campos consistentes e sem dados sensíveis.

## Métricas que importam

Nem toda métrica precisa virar gráfico. Para uma API Python, comece pelo conjunto que realmente ajuda operação:

| Métrica | Por que importa |
| --- | --- |
| requisições por endpoint | mostra volume e mudança de tráfego |
| taxa de erro por status | separa falhas 4xx e 5xx |
| latência p50/p95/p99 | mostra lentidão que média esconde |
| chamadas a APIs externas | identifica dependências problemáticas |
| tamanho de fila | revela gargalo assíncrono |
| tempo de job | mostra degradação em automações |

Se sua API usa tarefas em segundo plano, Celery, Redis ou filas, combine métricas com logs claros de início, fim, erro e retry. O artigo sobre [FastAPI, background tasks, Celery e Redis](/blog/fastapi-background-tasks-celery-redis-2026/) aprofunda essa parte.

## Usando OTLP em produção

No ambiente real, a aplicação normalmente exporta para um collector:

```bash
export APP_ENV=production
export OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-collector:4318"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
uvicorn app.main:app --host 0.0.0.0 --port 8000
```

O collector recebe telemetria de vários serviços, aplica filtros, adiciona atributos e encaminha para os backends finais. Essa arquitetura é útil porque você troca destino sem redeployar toda aplicação. Também evita que cada serviço precise conhecer detalhes de autenticação de cada ferramenta.

Para quem trabalha com múltiplas linguagens, esse padrão conversa bem com ecossistemas como Go. Se você mantém serviços Python e Go no mesmo time, vale comparar a abordagem com o guia de <a href="https://golang.com.br/blog/slog-go-logging-estruturado/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">logging estruturado com slog em Go</a>, já que OpenTelemetry é justamente mais forte quando padroniza observabilidade entre stacks diferentes.

## Checklist para portfólio

Um bom projeto de portfólio não precisa simular uma empresa inteira. Mas ele deve provar maturidade. Inclua no README:

- como rodar a API localmente;
- como ver traces no console;
- quais variáveis OTLP configurar;
- quais atributos são seguros;
- quais dados nunca devem ir para logs;
- exemplo de investigação de erro;
- screenshot ou descrição do painel local, se usar Jaeger ou Grafana.

Também vale criar um endpoint propositalmente lento ou uma rota que chama uma API externa fake. Assim você demonstra latência, erro, retry e trace em uma história compreensível.

## Erros comuns

O primeiro erro é instrumentar tudo e não olhar nada. Observabilidade boa nasce de perguntas: qual rota falha mais? O deploy piorou a latência? O banco ficou lento ou foi uma integração externa? Sem pergunta, você só acumula ruído.

O segundo erro é registrar dados demais. Payload completo em log parece útil no começo, mas vira risco de privacidade, custo e confusão. Registre IDs, contagens, tipos e estados. Use amostras controladas quando necessário.

O terceiro erro é deixar ambiente local, staging e produção com o mesmo `service.name` e sem atributos de ambiente. Quando o painel mistura tudo, a equipe perde confiança na ferramenta.

O quarto erro é esquecer deploy. Se o serviço roda em container, garanta que as variáveis `OTEL_*` estejam documentadas no compose, no Kubernetes manifest ou na plataforma usada. Observabilidade que só funciona no notebook do desenvolvedor não ajuda em incidente.

## Próximos passos

Depois de configurar traces básicos, evolua em camadas. Primeiro, padronize logs e atributos. Depois, exporte para um collector local com Docker Compose. Em seguida, adicione métricas de latência e taxa de erro. Só então pense em alertas.

Para carreira, esse tema rende um projeto muito forte: uma API FastAPI com banco PostgreSQL, autenticação simples, testes, OpenTelemetry, logs estruturados e deploy documentado. Ele mostra backend, operação, segurança básica e capacidade de explicar problemas em produção, exatamente o tipo de maturidade que diferencia uma pessoa que apenas escreve endpoints de alguém pronta para cuidar de sistemas reais.
