Instrumentação Python com Elven Observability


Sumário

  • Visão geral
  • Pré-requisitos
  • Instalação
  • Quick Start — Zero-Code (recomendado)
  • Quick Start — Inicialização manual
  • Configuração por variáveis de ambiente
  • Guia por framework
  • Usando logger, tracer e metrics
  • Instrumentações automáticas
  • Configuração avançada
  • Boas práticas
  • Deploy com Docker
  • Troubleshooting
  • FAQ

Visão geral

A instrumentação Python da Elven é composta por um pacote unificado que coordena dois componentes internos:

┌──────────────────────────────────────────────────────────┐
│              elven-unified-observability-py               │
│                                                          │
│  ┌────────────────────────┐  ┌────────────────────────┐  │
│  │  Logs Interceptor      │  │  OTel Instrumentation  │  │
│  │                        │  │                        │  │
│  │  - Captura logs        │  │  - Traces distribuídos │  │
│  │  - Intercepta console  │  │  - Métricas            │  │
│  │  - Batching + retry    │  │  - Auto-instrumentação │  │
│  │  - Envia para Loki     │  │  - Exporta via OTLP    │  │
│  └───────────┬────────────┘  └───────────┬────────────┘  │
│              │                           │               │
└──────────────┼───────────────────────────┼───────────────┘
               │                           │
               ▼                           ▼
    Elven Loki Gateway            Elven OTLP Collector
         (logs)                  (traces & métricas)
Componente O que faz
elven-unified-observability-py Pacote principal — instale apenas este. Coordena bootstrap, config e lifecycle
elven-logs-interceptor-python Pipeline de logs: captura, filtra, faz batching e envia para Loki
elven-opentelemetry-instrumentation-py Distro OTel: configura traces, métricas, auto-instrumentação e privacy

Você só precisa instalar o pacote unificado. As dependências são resolvidas automaticamente.


Pré-requisitos

  • Python 3.11+
  • pip (ou gerenciador de pacotes compatível)
  • Credenciais da Elven Observability:
  • Tenant ID
  • API Token
  • Endpoint OTLP (para traces/métricas)
  • Endpoint Loki (para logs)

Instalação

Instalação básica

pip install elven-unified-observability-py

Instalação com extras completos (recomendado)

Inclui compressão avançada (brotli), serialização otimizada (orjson) e integrações de framework:

pip install "elven-unified-observability-py[full]"

Verificar instalação

python -c "import elven_unified_observability; print('OK')"

Quick Start — Zero-Code (recomendado)

O modo zero-code instrumenta sua aplicação sem nenhuma alteração no código fonte. A lib injeta um sitecustomize.py que inicializa tudo antes da sua app rodar.

1. Crie um arquivo .env na raiz do projeto

# === Identidade do serviço ===
OTEL_SERVICE_NAME=meu-servico-api
OTEL_SERVICE_VERSION=1.0.0
OTEL_DEPLOYMENT_ENVIRONMENT=production

# === Telemetria (traces + métricas) ===
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.elvenobservability.com
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp

# === Logs ===
LOGS_URL=https://loki.elvenobservability.com/loki/api/v1/push
LOGS_TENANT=seu-tenant-id
LOGS_TOKEN=seu-api-token
LOGS_COMPRESSION=gzip

# === Controle unificado ===
UO_ENABLE_LOGGING=true
UO_ENABLE_METRICS=true
UO_ENABLE_TRACING=true

2. Rode sua aplicação com a CLI

elven-unified-observability -- python app.py

Funciona com qualquer comando:

# FastAPI com Uvicorn
elven-unified-observability -- uvicorn app:app --host 0.0.0.0 --port 8000

# Flask
elven-unified-observability -- flask --app app run

# Celery worker
elven-unified-observability -- celery -A worker.app worker -l info

# Módulo Python
elven-unified-observability -- python -m meu_servico

Pronto! Traces, métricas e logs já estão sendo coletados.


Quick Start — Inicialização manual

Use esta abordagem quando precisar de controle programático sobre a inicialização, ou quando não puder usar a CLI.

Exemplo mínimo

from elven_unified_observability import init, logger, tracer, metrics

handle = init({
    "service": {
        "serviceName": "meu-servico-api",
        "serviceVersion": "1.0.0",
        "environment": "production",
    },
    "logging": {
        "transport": {
            "url": "https://loki.elvenobservability.com/loki/api/v1/push",
            "tenantId": "seu-tenant-id",
            "authToken": "seu-api-token",
        },
    },
    "telemetry": {
        "exporters": {
            "protocol": "http/protobuf",
        },
    },
    "enableLogging": True,
    "enableMetrics": True,
    "enableTracing": True,
})

# Usar os proxies globais
logger.info("aplicação iniciada", {"version": "1.0.0"})
metrics.increment("app_startups_total", 1)

def do_work(span):
    span.set_attribute("work.type", "processing")
    return {"result": "ok"}

result = tracer.with_span("meu-trabalho", do_work)

# No shutdown da aplicação
handle.force_flush()
handle.shutdown()

Inicialização a partir do ambiente

Se as variáveis de ambiente já estiverem configuradas (ex: Kubernetes, Docker):

from elven_unified_observability import init_from_env, logger

handle = init_from_env()

logger.info("serviço pronto")

Configuração por variáveis de ambiente

Identidade do serviço

Estas variáveis definem como o serviço aparece na Elven Observability. A resolução segue uma ordem de precedência:

Atributo 1a opção 2a opção 3a opção Default
Nome OTEL_SERVICE_NAME LOGS_APP_NAME LOKI_APP_NAME unknown-service
Versão OTEL_SERVICE_VERSION LOGS_APP_VERSION LOKI_APP_VERSION 1.0.0
Ambiente OTEL_DEPLOYMENT_ENVIRONMENT LOGS_ENVIRONMENT ENVIRONMENT / APP_ENV development
Namespace OTEL_SERVICE_NAMESPACE

Controle unificado (wrapper)

Variável Descrição Default
UO_ENABLED Liga/desliga todos os sinais de uma vez true
UO_ENABLE_LOGGING Override para logs valor de UO_ENABLED
UO_ENABLE_METRICS Override para métricas valor de UO_ENABLED
UO_ENABLE_TRACING Override para tracing valor de UO_ENABLED
UO_STRICT Se true, falha o bootstrap se qualquer componente falhar false
UO_LOAD_DOTENV Carregar .env automaticamente no preload/CLI true
UO_DOTENV_PATH Caminho alternativo para o arquivo .env .env

Telemetria — Traces e Métricas

Variável Descrição Default
OTEL_EXPORTER_OTLP_ENDPOINT URL do coletor OTLP
OTEL_EXPORTER_OTLP_PROTOCOL Protocolo: http/protobuf ou grpc http/protobuf
OTEL_EXPORTER_OTLP_HEADERS Headers extras (formato key=value,key2=value2)
OTEL_EXPORTER_OTLP_COMPRESSION Compressão: gzip ou none gzip
OTEL_TRACES_EXPORTER Exporter de traces: otlp, console ou none otlp
OTEL_METRICS_EXPORTER Exporter de métricas: otlp, console ou none otlp
OTEL_TRACING_ENABLED Habilitar/desabilitar tracing explicitamente true
OTEL_METRICS_ENABLED Habilitar/desabilitar métricas explicitamente true
OTEL_METRIC_EXPORT_INTERVAL Intervalo de export de métricas (ms) 60000
OTEL_METRIC_EXPORT_TIMEOUT Timeout de export de métricas (ms) 30000

Telemetria — Privacy

Variável Descrição Default
OTEL_PRIVACY_REDACT_DB_STATEMENT Redactar statements SQL nos spans false
OTEL_PRIVACY_HASH_USER_ID Fazer hash de user IDs nos spans false

Logs — Transporte

Variável Descrição Default
LOGS_URL URL completa do Loki push endpoint
LOGS_TENANT Tenant ID (header X-Scope-OrgID)
LOGS_TOKEN Token de autenticação (Bearer)
LOGS_COMPRESSION Compressão: gzip, brotli, none gzip
LOGS_TIMEOUT Timeout HTTP (segundos) 10
LOGS_MAX_RETRIES Número de retentativas 3
LOGS_RETRY_DELAY Delay base entre retentativas (segundos) 1

Fallback LOKI_*: Todas as variáveis LOGS_* aceitam o prefixo LOKI_* como fallback. Ex: LOKI_URL, LOKI_TENANT, LOKI_TOKEN.

Logs — Buffer e Filtro

Variável Descrição Default
LOGS_BUFFER_MAX_SIZE Tamanho máximo do buffer de logs 100
LOGS_BUFFER_FLUSH_INTERVAL Intervalo de flush (segundos) 5
LOGS_BUFFER_MAX_MEMORY_MB Limite de memória do buffer (MB)
LOGS_FILTER_LEVELS Níveis permitidos (separados por vírgula) debug,info,warn,error,fatal
LOGS_FILTER_SAMPLING_RATE Taxa de amostragem (0.0 a 1.0) 1.0
LOGS_FILTER_MAX_MESSAGE_LENGTH Tamanho máximo da mensagem

Logs — Resiliência

Variável Descrição Default
LOGS_CIRCUIT_BREAKER_ENABLED Habilitar circuit breaker true
LOGS_CIRCUIT_BREAKER_FAILURE_THRESHOLD Falhas para abrir o circuito 5
LOGS_CIRCUIT_BREAKER_RESET_TIMEOUT Tempo para tentar fechar o circuito (segundos) 30
LOGS_DLQ_ENABLED Habilitar Dead Letter Queue false
LOGS_DLQ_TYPE Tipo: memory ou file memory
LOGS_DLQ_MAX_SIZE Tamanho máximo da DLQ 1000

Logs — Interceptação

Variável Descrição Default
LOGS_INTERCEPT_CONSOLE Capturar print() e root logger false
LOGS_PRESERVE_ORIGINAL_CONSOLE Manter output original no console ao interceptar true
LOGS_DEBUG Logs de debug interno da lib false

Labels customizados

Qualquer variável com prefixo LOGS_LABEL_ vira um label no Loki:

LOGS_LABEL_TEAM=backend
LOGS_LABEL_SQUAD=payments
LOGS_LABEL_CLUSTER=prod-us

Resultado no Loki: labels team=backend, squad=payments, cluster=prod-us.


Guia por framework

FastAPI

# .env
OTEL_SERVICE_NAME=minha-api
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.elvenobservability.com
LOGS_URL=https://loki.elvenobservability.com/loki/api/v1/push
LOGS_TENANT=meu-tenant
LOGS_TOKEN=meu-token
OTEL_INSTR_FASTAPI=true
OTEL_INSTR_HTTPX=true
UO_ENABLE_LOGGING=true
UO_ENABLE_TRACING=true
UO_ENABLE_METRICS=true
elven-unified-observability -- uvicorn app:app --host 0.0.0.0 --port 8000

A auto-instrumentação do FastAPI gera spans automaticamente para cada request, incluindo:

  • Método HTTP, rota, status code
  • Duração da requisição
  • Propagação de contexto (trace ID nos headers)

Django

OTEL_SERVICE_NAME=meu-django-app
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.elvenobservability.com
LOGS_URL=https://loki.elvenobservability.com/loki/api/v1/push
LOGS_TENANT=meu-tenant
LOGS_TOKEN=meu-token
OTEL_INSTR_DJANGO=true
UO_ENABLE_LOGGING=true
UO_ENABLE_TRACING=true
elven-unified-observability -- python manage.py runserver

Flask

OTEL_SERVICE_NAME=meu-flask-app
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.elvenobservability.com
LOGS_URL=https://loki.elvenobservability.com/loki/api/v1/push
LOGS_TENANT=meu-tenant
LOGS_TOKEN=meu-token
OTEL_INSTR_FLASK=true
UO_ENABLE_LOGGING=true
UO_ENABLE_TRACING=true
elven-unified-observability -- flask --app app run

Celery

OTEL_SERVICE_NAME=meu-worker
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.elvenobservability.com
LOGS_URL=https://loki.elvenobservability.com/loki/api/v1/push
LOGS_TENANT=meu-tenant
LOGS_TOKEN=meu-token
OTEL_INSTR_CELERY=true
UO_ENABLE_LOGGING=true
UO_ENABLE_TRACING=true
elven-unified-observability -- celery -A worker.app worker -l info

Script Python simples

Para scripts que não são web servers:

from elven_unified_observability import init_from_env, logger, shutdown

handle = init_from_env()

logger.info("processamento iniciado")
# ... sua lógica ...
logger.info("processamento finalizado")

handle.force_flush()
handle.shutdown()

Usando logger, tracer e metrics

Após a inicialização (via CLI ou init()), os proxies globais ficam disponíveis:

from elven_unified_observability import logger, tracer, metrics

Logger

# Níveis de log
logger.debug("detalhes técnicos", {"key": "value"})
logger.info("evento importante", {"user_id": "123", "action": "login"})
logger.warn("algo inesperado", {"retry_count": 3})
logger.error("falha no processamento", {"error": "timeout", "service": "payments"})
logger.fatal("erro crítico irrecuperável")

# Log genérico com nível
logger.log("info", "mensagem customizada", {"custom": "data"})

# Tracking de eventos (semântico)
logger.track_event("user.signup", {"plan": "pro", "source": "landing"})

# Context manager — adiciona labels a todos os logs no bloco
with logger.with_context({"request_id": "abc-123", "user_id": "456"}):
    logger.info("processando request")  # inclui request_id e user_id automaticamente
    logger.info("request finalizado")

# Context manager async
async with logger.with_context_async({"request_id": "abc-123"}):
    logger.info("processando async")

Tracer

# Span com callback (recomendado — gerencia lifecycle automaticamente)
def process_order(span):
    span.set_attribute("order.id", "ORD-789")
    span.set_attribute("order.total", 150.00)
    # ... lógica ...
    return {"status": "processed"}

result = tracer.with_span("process-order", process_order)

# Span async
async def fetch_data(span):
    span.set_attribute("source", "external-api")
    async with httpx.AsyncClient() as client:
        response = await client.get("https://api.example.com/data")
        return response.json()

data = await tracer.with_span("fetch-external-data", fetch_data)

# Span manual (mais controle)
span = tracer.start_span("custom-operation")
try:
    # ... lógica ...
    span.set_attribute("result", "success")
finally:
    span.end()

Metrics

# Contador (incrementos)
metrics.increment("requests_total", 1, {"method": "GET", "route": "/api/users"})
metrics.increment("orders_processed_total", 1, {"status": "success"})

# Histograma (distribuições — latência, tamanho, etc.)
metrics.record_histogram(
    "request_duration_ms",
    145.2,
    {"route": "/api/users", "method": "GET"},
    unit="ms",
)

metrics.record_histogram(
    "payload_size_bytes",
    2048.0,
    {"endpoint": "/upload"},
    unit="bytes",
)

Instrumentações automáticas

A lib detecta automaticamente as bibliotecas instaladas e instrumenta-as. Você pode controlar quais instrumentações ficam ativas:

Toggles por variável de ambiente

Cada instrumentação tem uma variável OTEL_INSTR_*:

Variável Biblioteca
OTEL_INSTR_FASTAPI FastAPI
OTEL_INSTR_FLASK Flask
OTEL_INSTR_DJANGO Django
OTEL_INSTR_REQUESTS requests
OTEL_INSTR_HTTPX httpx
OTEL_INSTR_URLLIB3 urllib3
OTEL_INSTR_AIOHTTP_CLIENT aiohttp (client)
OTEL_INSTR_SQLALCHEMY SQLAlchemy
OTEL_INSTR_PSYCOPG psycopg (3.x)
OTEL_INSTR_PSYCOPG2 psycopg2
OTEL_INSTR_PYMYSQL PyMySQL
OTEL_INSTR_PYMONGO PyMongo
OTEL_INSTR_REDIS redis-py
OTEL_INSTR_CELERY Celery
OTEL_INSTR_KAFKA kafka-python
OTEL_INSTR_PIKA pika (RabbitMQ)
OTEL_INSTR_BOTO3SQS boto3 SQS
OTEL_INSTR_GRPC gRPC
OTEL_INSTR_GRAPHQL graphql-core
OTEL_INSTR_LOGGING stdlib logging (correlação trace/span ID)
OTEL_INSTR_THREADING threading
OTEL_INSTR_ASYNCIO asyncio

Valores aceitos: true / 1 / yes / on para habilitar, false / 0 / no / off para desabilitar.

Exemplo: habilitar apenas o necessário

# API FastAPI que usa PostgreSQL e Redis
OTEL_INSTR_FASTAPI=true
OTEL_INSTR_PSYCOPG=true
OTEL_INSTR_REDIS=true
OTEL_INSTR_HTTPX=true

Precedência

Config explícita no init() > variáveis de ambiente OTEL_INSTR_* > defaults da lib.


Configuração avançada

Inicialização manual com config completo

from elven_unified_observability import init

handle = init({
    "service": {
        "serviceName": "payments-service",
        "serviceVersion": "2.1.0",
        "environment": "production",
        "serviceNamespace": "fintech",
        "attributes": {
            "team": "payments",
            "region": "us-east-1",
        },
    },
    "logging": {
        "transport": {
            "url": "https://loki.elvenobservability.com/loki/api/v1/push",
            "tenantId": "meu-tenant",
            "authToken": "meu-token",
            "compression": "gzip",
        },
        "labels": {
            "team": "payments",
            "squad": "checkout",
        },
        "buffer": {
            "maxSize": 500,
            "flushInterval": 3,
        },
        "filter": {
            "levels": ["info", "warn", "error", "fatal"],
            "samplingRate": 1.0,
        },
        "circuitBreaker": {
            "enabled": True,
            "failureThreshold": 5,
            "resetTimeout": 30,
        },
        "interceptConsole": False,
    },
    "telemetry": {
        "exporters": {
            "protocol": "http/protobuf",
            "compression": "gzip",
        },
        "tracing": {
            "enabled": True,
            "ratio": 1.0,
        },
        "metrics": {
            "enabled": True,
            "exportIntervalMillis": 15000,
        },
        "privacy": {
            "redactDbStatement": True,
            "hashUserId": True,
        },
        "instrumentations": {
            "fastapi": {"enabled": True},
            "httpx": {"enabled": True},
            "sqlalchemy": {"enabled": True},
            "redis": {"enabled": True},
        },
    },
    "enableLogging": True,
    "enableMetrics": True,
    "enableTracing": True,
    "strict": False,
})

Integrações com bibliotecas de logging

Com logging (stdlib)

from logs_interceptor import LoggingHandler

import logging

handler = LoggingHandler()
logging.getLogger("meu_modulo").addHandler(handler)

Com structlog

from logs_interceptor import StructlogProcessor

import structlog

structlog.configure(
    processors=[
        # ... seus processors ...
        StructlogProcessor(),
        structlog.dev.ConsoleRenderer(),
    ]
)

Com loguru

from logs_interceptor import LoguruSink
from loguru import logger as loguru_logger

loguru_logger.add(LoguruSink())

Usando FastAPIMiddleware (opcional)

Se quiser middleware explícito do log interceptor (além da auto-instrumentação OTel):

from fastapi import FastAPI
from logs_interceptor import FastAPIMiddleware

app = FastAPI()
app.add_middleware(FastAPIMiddleware)

Boas práticas

Naming convention para serviços

Use nomes descritivos e consistentes:

{dominio}-{componente}

Exemplos: payments-api, orders-worker, notifications-scheduler, auth-service.

Labels no Loki

Use labels para dimensões de baixa cardinalidade (equipe, ambiente, cluster). Não use para valores de alta cardinalidade (user ID, request ID) — esses devem ir no corpo do log:

# Bom: labels de baixa cardinalidade
LOGS_LABEL_TEAM=backend
LOGS_LABEL_CLUSTER=prod-us

# Ruim: não coloque isso como label
# LOGS_LABEL_USER_ID=...    # alta cardinalidade!
# User ID e request ID vão como metadata, não como label
logger.info("request processada", {"user_id": "123", "request_id": "abc"})

Sampling em produção

Para serviços com alto throughput, considere amostragem de traces:

handle = init({
    "telemetry": {
        "tracing": {
            "ratio": 0.1,  # 10% dos traces
        },
    },
    # ...
})

Ou via ambiente:

OTEL_TRACES_SAMPLER=traceidratio
OTEL_TRACES_SAMPLER_ARG=0.1

Redação de dados sensíveis

Habilite privacy para evitar vazamento de dados em spans:

OTEL_PRIVACY_REDACT_DB_STATEMENT=true   # redacta queries SQL
OTEL_PRIVACY_HASH_USER_ID=true          # faz hash de user IDs

Shutdown gracioso

Sempre faça flush antes de encerrar:

import atexit
from elven_unified_observability import force_flush, shutdown

atexit.register(shutdown)

A CLI zero-code já registra handlers de SIGTERM, SIGINT e atexit automaticamente.

Instrumentações — habilite só o necessário

Cada instrumentação adiciona overhead. Habilite apenas as que sua aplicação realmente usa:

# API REST simples com PostgreSQL
OTEL_INSTR_FASTAPI=true
OTEL_INSTR_PSYCOPG=true
OTEL_INSTR_HTTPX=true

Deploy com Docker

Dockerfile — FastAPI com Uvicorn

Se a lib já está no seu requirements.txt:

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["elven-unified-observability", "--", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Se você quer adicionar a instrumentação direto no Dockerfile sem alterar o requirements.txt da aplicação (útil para instrumentar apps existentes sem mexer no código):

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Instalar a lib de observabilidade separadamente
RUN pip install --no-cache-dir "elven-unified-observability-py[full]"

COPY . .

CMD ["elven-unified-observability", "--", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Credenciais: As variáveis de ambiente são passadas no docker run ou no docker-compose.ymlnunca hardcoded no Dockerfile.

docker run

docker build -t minha-api .

docker run -p 8000:8000 <br>
  -e OTEL_SERVICE_NAME=minha-api <br>
  -e OTEL_SERVICE_VERSION=1.0.0 <br>
  -e OTEL_DEPLOYMENT_ENVIRONMENT=production <br>
  -e OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.elvenobservability.com <br>
  -e OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf <br>
  -e OTEL_TRACES_EXPORTER=otlp <br>
  -e OTEL_METRICS_EXPORTER=otlp <br>
  -e OTEL_INSTR_FASTAPI=true <br>
  -e OTEL_INSTR_HTTPX=true <br>
  -e OTEL_PRIVACY_REDACT_DB_STATEMENT=true <br>
  -e LOGS_URL=https://loki.elvenobservability.com/loki/api/v1/push <br>
  -e LOGS_TENANT=seu-tenant-id <br>
  -e LOGS_TOKEN=seu-api-token <br>
  -e LOGS_COMPRESSION=gzip <br>
  -e UO_ENABLE_LOGGING=true <br>
  -e UO_ENABLE_TRACING=true <br>
  -e UO_ENABLE_METRICS=true <br>
  minha-api

docker-compose.yml

services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      # Identidade
      OTEL_SERVICE_NAME: minha-api
      OTEL_SERVICE_VERSION: "1.0.0"
      OTEL_DEPLOYMENT_ENVIRONMENT: production

      # Telemetria
      OTEL_EXPORTER_OTLP_ENDPOINT: https://otlp.elvenobservability.com
      OTEL_EXPORTER_OTLP_PROTOCOL: http/protobuf
      OTEL_TRACES_EXPORTER: otlp
      OTEL_METRICS_EXPORTER: otlp
      OTEL_METRIC_EXPORT_INTERVAL: "15000"
      OTEL_INSTR_FASTAPI: "true"
      OTEL_INSTR_HTTPX: "true"
      OTEL_PRIVACY_REDACT_DB_STATEMENT: "true"

      # Logs
      LOGS_URL: https://loki.elvenobservability.com/loki/api/v1/push
      LOGS_TENANT: seu-tenant-id
      LOGS_TOKEN: seu-api-token
      LOGS_COMPRESSION: gzip

      # Controle
      UO_ENABLE_LOGGING: "true"
      UO_ENABLE_TRACING: "true"
      UO_ENABLE_METRICS: "true"

  worker:
    build: .
    command: ["elven-unified-observability", "--", "celery", "-A", "worker.app", "worker", "-l", "info"]
    environment:
      OTEL_SERVICE_NAME: meu-worker
      OTEL_SERVICE_VERSION: "1.0.0"
      OTEL_DEPLOYMENT_ENVIRONMENT: production
      OTEL_EXPORTER_OTLP_ENDPOINT: https://otlp.elvenobservability.com
      OTEL_INSTR_CELERY: "true"
      LOGS_URL: https://loki.elvenobservability.com/loki/api/v1/push
      LOGS_TENANT: seu-tenant-id
      LOGS_TOKEN: seu-api-token
      UO_ENABLE_LOGGING: "true"
      UO_ENABLE_TRACING: "true"

Dica: Para não repetir variáveis entre serviços, use um arquivo .env compartilhado com env_file: ou um bloco x-common-env: com YAML anchors.

Dockerfile multi-stage (produção)

Para imagens menores e mais seguras:

# --- Build ---
FROM python:3.12-slim AS builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# Se a lib não está no requirements.txt, adicione aqui:
RUN pip install --no-cache-dir --prefix=/install "elven-unified-observability-py[full]"

# --- Runtime ---
FROM python:3.12-slim

WORKDIR /app

COPY --from=builder /install /usr/local
COPY . .

RUN useradd -r -s /bin/false appuser
USER appuser

EXPOSE 8000

CMD ["elven-unified-observability", "--", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Kubernetes — Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: minha-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: minha-api
  template:
    metadata:
      labels:
        app: minha-api
    spec:
      containers:
        - name: api
          image: minha-api:1.0.0
          ports:
            - containerPort: 8000
          env:
            # Identidade
            - name: OTEL_SERVICE_NAME
              value: minha-api
            - name: OTEL_SERVICE_VERSION
              value: "1.0.0"
            - name: OTEL_DEPLOYMENT_ENVIRONMENT
              value: production

            # Telemetria
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: https://otlp.elvenobservability.com
            - name: OTEL_EXPORTER_OTLP_PROTOCOL
              value: http/protobuf
            - name: OTEL_TRACES_EXPORTER
              value: otlp
            - name: OTEL_METRICS_EXPORTER
              value: otlp
            - name: OTEL_INSTR_FASTAPI
              value: "true"

            # Logs
            - name: LOGS_URL
              value: https://loki.elvenobservability.com/loki/api/v1/push
            - name: LOGS_TENANT
              valueFrom:
                secretKeyRef:
                  name: elven-credentials
                  key: tenant-id
            - name: LOGS_TOKEN
              valueFrom:
                secretKeyRef:
                  name: elven-credentials
                  key: api-token
            - name: LOGS_COMPRESSION
              value: gzip

            # Controle
            - name: UO_ENABLE_LOGGING
              value: "true"
            - name: UO_ENABLE_TRACING
              value: "true"
            - name: UO_ENABLE_METRICS
              value: "true"

            # Labels customizados
            - name: LOGS_LABEL_CLUSTER
              value: prod-us
            - name: LOGS_LABEL_TEAM
              value: backend

Com o Secret:

kubectl create secret generic elven-credentials <br>
  --from-literal=tenant-id=seu-tenant-id <br>
  --from-literal=api-token=seu-api-token

Troubleshooting

A aplicação não inicia / crash no bootstrap

Sintoma: Erro no import ou na inicialização.

Verificações:

  1. Confirme a versão do Python (python --version): requer 3.11+
  2. Confirme que o pacote está instalado: pip show elven-unified-observability-py
  3. Se usar strict=true / UO_STRICT=true, qualquer falha em logs ou OTel vai travar o bootstrap. Mude para false durante debug
# Testar import
python -c "from elven_unified_observability import init; print('OK')"

Logs não aparecem no Loki

Sintoma: A aplicação roda normalmente, mas nenhum log aparece.

Verificações:

  1. Confirme que LOGS_URL está correto e inclui o path completo: https://loki.elvenobservability.com/loki/api/v1/push
  2. Confirme que LOGS_TENANT e LOGS_TOKEN estão preenchidos
  3. Confirme que UO_ENABLE_LOGGING=true
  4. Habilite debug: LOGS_DEBUG=true e verifique o stderr
# Teste rápido
LOGS_DEBUG=true UO_STRICT=true elven-unified-observability -- python -c "
from elven_unified_observability import logger
logger.info('teste de log')
"

Nota: Diferente da Lambda layer, aqui o LOGS_URL deve incluir o path completo (/loki/api/v1/push), pois a lib Python não appenda o path automaticamente.

Traces não aparecem

Sintoma: Logs funcionam, mas traces não aparecem na Elven.

Verificações:

  1. Confirme que OTEL_EXPORTER_OTLP_ENDPOINT está definido
  2. Confirme que OTEL_TRACES_EXPORTER=otlp (e não none)
  3. Confirme que UO_ENABLE_TRACING=true
  4. Verifique conectividade com o endpoint:
curl -v https://otlp.elvenobservability.com/v1/traces

Métricas não aparecem

Sintoma: Traces e logs funcionam, mas métricas não.

Verificações:

  1. Confirme que OTEL_METRICS_EXPORTER=otlp (e não none)
  2. Confirme que UO_ENABLE_METRICS=true
  3. Métricas são exportadas em intervalos (OTEL_METRIC_EXPORT_INTERVAL). Aguarde pelo menos o intervalo configurado antes de verificar

Auto-instrumentação não funciona

Sintoma: A lib não gera spans para requests HTTP, queries SQL, etc.

Verificações:

  1. Confirme que a biblioteca está instalada (ex: pip show opentelemetry-instrumentation-fastapi)
  2. Confirme que o toggle está ativo: OTEL_INSTR_FASTAPI=true
  3. A auto-instrumentação deve ser inicializada antes de importar o framework. O modo zero-code (CLI) garante isso. No modo manual, chame init() antes de criar a app

Erro de autenticação (401/403) no Loki

Sintoma: LOGS_DEBUG=true mostra server error 401 ou 403.

Verificações:

  1. Confirme LOGS_TOKEN no painel da Elven
  2. Confirme LOGS_TENANT
  3. Verifique se o token não expirou

Alto consumo de memória

Sintoma: A aplicação consome memória crescente.

Verificações:

  1. Verifique o tamanho do buffer: LOGS_BUFFER_MAX_SIZE (default 100)
  2. Se o Loki estiver inacessível, logs ficam no buffer / DLQ. Verifique LOGS_CIRCUIT_BREAKER_ENABLED=true para evitar acúmulo
  3. Reduza LOGS_BUFFER_MAX_MEMORY_MB para limitar uso

FAQ

Preciso instalar os 3 pacotes separadamente? Não. Instale apenas elven-unified-observability-py — ele puxa as duas libs automaticamente.

A CLI zero-code funciona com qualquer framework? Sim. A CLI injeta a instrumentação via sitecustomize.py antes de qualquer import. Funciona com FastAPI, Django, Flask, Celery, scripts puros, etc.

Posso usar apenas logs sem traces? Sim. Defina UO_ENABLE_TRACING=false e UO_ENABLE_METRICS=false para usar apenas a pipeline de logs.

Posso usar apenas traces sem logs? Sim. Defina UO_ENABLE_LOGGING=false para desabilitar a pipeline de logs e usar apenas OTel.

Funciona com asyncio / async? Sim. O tracer.with_span() detecta funções async automaticamente. O logger.with_context_async() fornece context manager async. As instrumentações de asyncio, aiohttp, httpx são totalmente async-safe.

Qual a diferença entre LOGS_URL na lib Python e LOKI_URL na Lambda layer? Na lib Python, LOGS_URL deve ser a URL completa incluindo o path (https://loki.elvenobservability.com/loki/api/v1/push). Na Lambda layer (Go), LOKI_URL deve ser apenas a URL base (https://loki.elvenobservability.com), pois a extension appenda o path automaticamente.

Posso usar structlog / loguru em vez do logger padrão? Sim. A lib fornece StructlogProcessor e LoguruSink que redirecionam logs para a pipeline da Elven. Você pode usar sua lib preferida e os logs ainda chegam no Loki.

Qual versão mínima do Python? Python 3.11 ou superior.

Como funciona o circuit breaker? Se o Loki retornar erros consecutivos (LOGS_CIRCUIT_BREAKER_FAILURE_THRESHOLD), o circuito "abre" e logs são descartados (ou vão para a DLQ se habilitada). Após LOGS_CIRCUIT_BREAKER_RESET_TIMEOUT segundos, o circuito tenta fechar gradualmente.

Posso usar em containers / Kubernetes? Sim! Veja a seção Deploy com Docker para exemplos completos de Dockerfile, docker-compose e Kubernetes Deployment com Secrets.