Instrumentação Python no Google Cloud Run

Colete logs, métricas e traces de aplicações Python no Cloud Run com um único bootstrap e envio OTLP para um OpenTelemetry Collector.

Nota: O exemplo principal usa Python 3.11.16, mas o pacote publicado declara suporte a Python 3.10 ou superior. Valide a versão exata da aplicação em homologação antes do rollout de produção.

Caminho padrão: configure a identidade do serviço e uma URL-base em OTEL_EXPORTER_OTLP_ENDPOINT. Sem LOGS_URL ou LOKI_URL, a versão publicada 0.1.22 escolhe OTLP para os logs e deriva /v1/logs automaticamente. Não é necessário definir LOGS_EXPORTER, LOGS_TENANT nem LOGS_TOKEN na aplicação; o Collector ou gateway aprovado precisa aceitar os três sinais.

O interceptor 0.1.17 envia logs em OTLP/HTTP JSON (application/json), enquanto o OTel 0.1.5 envia traces e métricas em OTLP/HTTP protobuf (application/x-protobuf). Confirme que o gateway aceita os dois formatos nos caminhos /v1/logs, /v1/traces e /v1/metrics.

Sumário

Visão geral

O pacote elven-unified-observability-py inicializa os três sinais de observabilidade antes da aplicação:

  • intercepta logging e exceções não tratadas; print() depende de LOGS_INTERCEPT_CONSOLE=true, e loguru e structlog dependem das bridges opcionais;
  • cria e exporta traces das bibliotecas Python suportadas pelo OpenTelemetry;
  • exporta métricas automáticas das instrumentações compatíveis e métricas customizadas da aplicação;
  • adiciona identidade de serviço consistente aos três sinais;
  • correlaciona logs emitidos dentro de um span com trace_id e span_id;
  • coordena force_flush() e shutdown() quando o processo recebe SIGTERM ou termina normalmente.

A integração não exige uma imagem de aplicação fornecida pela Elven. Instale a biblioteca PyPI na imagem da própria aplicação. Quando um Collector sidecar for necessário, use somente a imagem e o digest aprovados para o ambiente do cliente.

Importante: A versão pública estável usada neste guia é elven-unified-observability-py==0.1.22. Não use números de versões candidatas ou imagens sem release verificável em produção.

Cobertura por sinal

Sinal Cobertura automática Cobertura manual
Logs logging, Uvicorn, Gunicorn e exceções não tratadas; print() com interceptação de console habilitada e bridges para loguru e structlog Proxy logger para logs estruturados e eventos
Traces Frameworks web, clientes HTTP, bancos, filas e bibliotecas instaladas compatíveis Proxy tracer para spans de negócio
Métricas Métricas emitidas pelas instrumentações OpenTelemetry compatíveis Counters, histograms e up/down counters pelo proxy metrics

Aviso: CPU, memória, quantidade de instâncias e métricas nativas do Cloud Run não são coletadas pela biblioteca Python. Use Cloud Monitoring ou um Collector configurado para essas métricas de infraestrutura.

Bibliotecas instrumentadas

A distribuição tenta instrumentar somente bibliotecas presentes no ambiente. A ausência de uma biblioteca não impede o startup.

Categoria Integrações suportadas
Web ASGI, WSGI, FastAPI, Starlette, Flask, Django, GraphQL
HTTP e RPC requests, httpx, urllib3, aiohttp, gRPC
Bancos e cache SQLAlchemy, psycopg, psycopg2, PyMySQL, mysqlclient, MongoDB, Redis
Mensageria Celery, Kafka Python, Pika, boto3 SQS
Runtime asyncio, threading e logging

Quando usar este guia

Use este guia quando:

  • a aplicação Python roda como Cloud Run Service ou Cloud Run Job;
  • o código já é empacotado em uma imagem OCI;
  • existe conectividade HTTPS entre o Cloud Run e o Collector;
  • o objetivo é instrumentar sem alterar os handlers da aplicação;
  • logs, métricas e traces devem compartilhar a mesma identidade de serviço.

Não use este guia quando:

  • o workload roda em GKE, VM ou função com outro modelo de bootstrap;
  • já existe outro opentelemetry-instrument, agent ou bootstrap de logging no mesmo processo;
  • o serviço não consegue alcançar o endpoint OTLP aprovado;
  • o runtime é diferente de Python.

Atenção: Não combine elven-unified-observability -- ... com opentelemetry-instrument, LOGS_AUTO_INIT=true ou outro agente de startup. Mantenha uma única autoridade de instrumentação para evitar spans, handlers e métricas duplicados.

Arquitetura

O caminho recomendado envia os três sinais diretamente para um Collector remoto. A biblioteca fica dentro da imagem da aplicação; o backend e suas credenciais permanecem atrás do Collector.

Fluxo recomendado: requisição HTTPS → Cloud Run Service → aplicação Python → Elven Unified Observability → OpenTelemetry Collector → Loki, Tempo e Mimir.

Collector remoto

Use esta topologia como padrão:

  • uma única imagem de aplicação;
  • URL-base OTLP HTTPS fornecida no onboarding;
  • autenticação injetada pelo Secret Manager;
  • menor consumo de CPU e memória por instância;
  • operação do Collector separada do ciclo de vida do Cloud Run.

Collector sidecar

Use um Collector sidecar somente quando houver requisito de isolamento de credenciais, processamento local ou endpoint remoto inacessível diretamente pela aplicação.

Fluxo com sidecar: aplicação Python → Collector em localhost:4318 → Collector ou gateway Elven.

Nesta topologia:

  • a aplicação usa http://localhost:4318 sem token do backend;
  • somente o sidecar recebe os secrets de exportação;
  • o sidecar precisa de startup probe na porta de health check;
  • a aplicação deve depender do sidecar na ordem de inicialização;
  • a imagem do Collector deve ser fixada por digest.

Cuidado: Não use tags mutáveis como latest em produção. A referência <COLLECTOR_IMAGE>@sha256:<DIGEST> deve ser substituída por um artefato aprovado e informado no onboarding.

Pré-requisitos

Google Cloud

  • projeto e região definidos;
  • API do Cloud Run, Cloud Build e Artifact Registry habilitadas; Secret Manager somente se o gateway exigir autenticação na entrada;
  • Artifact Registry criado para a imagem da aplicação;
  • service account do Cloud Run com acesso ao secret de autenticação, quando o gateway exigir headers;
  • conectividade de saída HTTPS para o Collector;
  • VPC Connector e regras de firewall quando o Collector for privado.

Aplicação

  • Python 3.10 ou superior;
  • imagem Linux amd64 compatível com o contrato do Cloud Run;
  • processo HTTP ouvindo em 0.0.0.0 na porta configurada, 8080 por padrão;
  • dependências resolvidas sem conflito;
  • apenas um processo Python por instância como baseline recomendado.

O exemplo foi preparado para a imagem python:3.11.16-slim-bookworm.

Elven Observability

Solicite no onboarding:

  • URL-base OTLP HTTP aprovada para os três sinais; se o gateway usar rotas distintas, solicite endpoints completos para cada sinal;
  • headers exigidos pelo gateway ou Collector;
  • nome do tenant;
  • convenção esperada para service.name, ambiente e namespace.

Cuidado: Nunca grave tokens, headers de autenticação ou tenants privados no Dockerfile, repositório, imagem ou arquivo de configuração versionado.

Quick Start

Este fluxo instrumenta um Cloud Run Service com Collector remoto.

1. Adicione a dependência

No requirements.txt, fixe a versão da aplicação:

elven-unified-observability-py[full]==0.1.22

O extra full adiciona suporte opcional a loguru, structlog e serialização otimizada. Ele não instala FastAPI, Flask, Django ou o servidor da aplicação.

Nota: Se a aplicação usa somente o logging padrão, elven-unified-observability-py==0.1.22 é suficiente.

2. Use o bootstrap zero-code na imagem

Exemplo para FastAPI e Uvicorn:

FROM python:3.11.16-slim-bookworm

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PORT=8080

WORKDIR /app

RUN groupadd --system app && useradd --system --gid app --home-dir /app app

COPY requirements.txt ./
RUN python -m pip install --no-cache-dir --upgrade pip \
    && python -m pip install --no-cache-dir -r requirements.txt \
    && python -m pip check

COPY . .
RUN chown -R app:app /app

USER app
EXPOSE 8080

CMD ["elven-unified-observability", "--", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080", "--workers", "1"]

Altere app.main:app para o import path real da aplicação.

3. Crie o arquivo de variáveis não sensíveis

Crie cloudrun.env.yaml sem tokens:

UO_ENABLED: "true"
UO_LOAD_DOTENV: "false"
UO_LOGGING_INTEGRATIONS: "auto"

OTEL_SERVICE_NAME: "orders-api"
OTEL_SERVICE_VERSION: "1.0.0"
OTEL_SERVICE_NAMESPACE: "commerce"
OTEL_DEPLOYMENT_ENVIRONMENT: "production"
OTEL_RESOURCE_ATTRIBUTES: "cloud.provider=gcp,cloud.platform=gcp_cloud_run,team=commerce"

OTEL_EXPORTER_OTLP_ENDPOINT: "https://collector.example.com"
OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf"
OTEL_TRACES_EXPORTER: "otlp"
OTEL_METRICS_EXPORTER: "otlp"

Substitua a URL-base e a identidade pelos valores aprovados para o ambiente. UO_ENABLED=true habilita os três sinais por padrão; os toggles individuais são opcionais. A wrapper deriva /v1/logs para logs, enquanto traces e métricas usam a mesma URL-base OTLP. Use endpoints separados somente quando as rotas do gateway forem diferentes. Para capturar também print(), adicione LOGS_INTERCEPT_CONSOLE=true como ajuste opcional de captura, não de transporte.

4. Cadastre a autenticação no Secret Manager, se exigida

Se o gateway exigir headers de entrada, crie o secret uma vez. Caso ele aceite OTLP sem autenticação na entrada, pule este passo e não configure OTEL_EXPORTER_OTLP_HEADERS:

gcloud secrets create elven-otlp-headers \
  --replication-policy=automatic

Adicione o valor sem colocá-lo no histórico do shell:

read -r -s -p "Headers OTLP: " ELVEN_OTLP_HEADERS
printf '%s' "${ELVEN_OTLP_HEADERS}" | \
  gcloud secrets versions add elven-otlp-headers --data-file=-
unset ELVEN_OTLP_HEADERS

O conteúdo exato do secret deve ser fornecido no onboarding. Exemplo de formato, sem credenciais reais:

Authorization=Bearer <TOKEN>,X-Scope-OrgID=<TENANT>

Conceda acesso à service account do serviço:

gcloud secrets add-iam-policy-binding elven-otlp-headers \
  --member="serviceAccount:<SERVICE_ACCOUNT>" \
  --role="roles/secretmanager.secretAccessor"

5. Construa e publique a imagem

Use Cloud Build para produzir uma imagem compatível com o runtime Linux do Cloud Run:

export PROJECT_ID="<PROJECT_ID>"
export REGION="<REGION>"
export REPOSITORY="<ARTIFACT_REGISTRY_REPOSITORY>"
export SERVICE="orders-api"
export IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${SERVICE}:1.0.0"

gcloud builds submit --project "${PROJECT_ID}" --tag "${IMAGE}" .

6. Faça o deploy

gcloud run deploy "${SERVICE}" \
  --project "${PROJECT_ID}" \
  --region "${REGION}" \
  --image "${IMAGE}" \
  --port 8080 \
  --service-account "<SERVICE_ACCOUNT>" \
  --env-vars-file cloudrun.env.yaml \
  --update-secrets "OTEL_EXPORTER_OTLP_HEADERS=elven-otlp-headers:1" \
  --no-cpu-throttling \
  --concurrency 40 \
  --max-instances 20

Fixe a versão do secret em produção. Atualize deliberadamente para uma nova versão durante a rotação.

Se o gateway não exigir headers de entrada, remova --update-secrets do comando de deploy. Isso não altera as credenciais upstream mantidas no Collector.

Importante: --no-cpu-throttling usa billing por instância e mantém CPU disponível para buffers e exportadores em background enquanto a instância existir. Avalie custo e capacidade antes do rollout.

Instalação passo a passo

Passo 1: Resolva as dependências

Instale a wrapper como dependência direta e deixe que ela resolva as versões compatíveis dos componentes:

python -m pip install "elven-unified-observability-py[full]==0.1.22"
python -m pip check

O release 0.1.22 exige:

  • elven-logs-interceptor-python>=0.1.17,<0.2.0;
  • elven-opentelemetry-instrumentation-py>=0.1.5,<0.2.0;
  • python-dotenv>=1.0.0,<2.0.0;
  • httpx>=0.24.1,<0.29.0 por meio do interceptor de logs.

Atenção: Um pin antigo de httpx ou httpcore pode tornar a resolução impossível. Não force versões incompatíveis com --no-deps. Ajuste o lock da aplicação e execute pip check na imagem final.

Passo 2: Escolha um único bootstrap

Use o modo zero-code como padrão:

elven-unified-observability -- uvicorn app.main:app --host 0.0.0.0 --port 8080

O comando injeta sitecustomize, inicializa a observabilidade antes dos imports da aplicação e substitui o próprio processo pelo servidor. Dessa forma, sinais do Cloud Run chegam diretamente ao processo Python e o shutdown coordenado recebe SIGTERM.

Use inicialização manual somente quando o comando de startup não puder ser alterado:

from elven_unified_observability import init_from_env

init_from_env()

from app.application import application

Inicialize antes de importar e criar a aplicação web.

Passo 3: Ajuste o comando por framework

FastAPI ou Starlette com Uvicorn

CMD ["elven-unified-observability", "--", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080", "--workers", "1"]

Flask com Gunicorn

CMD ["elven-unified-observability", "--", "gunicorn", "--bind", "0.0.0.0:8080", "--workers", "1", "app:create_app()"]

Django com Gunicorn

CMD ["elven-unified-observability", "--", "gunicorn", "--bind", "0.0.0.0:8080", "--workers", "1", "project.wsgi:application"]

Dica: Comece com um worker por instância. O Cloud Run escala horizontalmente, e múltiplos processos no mesmo container mantêm filas e providers de métricas separados. Aumente workers somente após teste de carga e validação de cardinalidade.

Passo 4: Configure o transporte de logs

No caminho recomendado, não há configuração separada para transporte de logs:

OTEL_SERVICE_NAME: "orders-api"
OTEL_EXPORTER_OTLP_ENDPOINT: "https://collector.example.com"
OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf"

Sem LOGS_URL/LOKI_URL, a wrapper 0.1.22 seleciona OTLP e deriva https://collector.example.com/v1/logs. OTEL_EXPORTER_OTLP_LOGS_ENDPOINT é opcional: use uma URL completa somente quando os logs tiverem rota própria. Remova LOGS_TENANT e LOGS_TOKEN do caminho Collector; as credenciais e o tenant upstream ficam no Collector/gateway. Se a aplicação herdar OTEL_LOGS_EXPORTER=none, remova essa variável: nesta versão ela impede a autodetecção OTLP. Quando não puder removê-la, defina LOGS_EXPORTER=otlp como override explícito.

Para um Loki direto, use uma configuração separada e não a trate como padrão:

LOGS_EXPORTER: "loki"
LOGS_URL: "https://loki.example.com/loki/api/v1/push"
LOGS_TENANT: "<TENANT>"

Injete LOGS_TOKEN pelo Secret Manager.

Aviso: O caminho Loki direto cobre somente logs. Traces e métricas continuam exigindo endpoints OTLP.

Passo 5: Preserve os logs nativos do Cloud Run

Mantenha os handlers originais e o stdout ativo:

LOGS_INTERCEPT_CONSOLE: "true"
LOGS_PRESERVE_ORIGINAL_CONSOLE: "true"
UO_ENABLE_PYTHON_LOGGING_BRIDGE: "true"

Esse modo envia o log para a Elven Observability e mantém a cópia no Cloud Logging. A bridge do logging captura também loggers com configuração própria, incluindo Uvicorn e Gunicorn, respeitando os filtros de ruído.

Passo 6: Garanta shutdown dentro da janela do Cloud Run

O preload registra handlers para SIGTERM, SIGINT e atexit. No shutdown, os logs são descarregados antes de traces e métricas.

Use timeouts menores que a janela de encerramento da plataforma:

LOGS_TIMEOUT: "5000"
OTEL_EXPORTER_OTLP_TIMEOUT: "5000"
OTEL_METRIC_EXPORT_TIMEOUT: "5000"

Evite retries excessivos no shutdown. O processo precisa concluir antes do SIGKILL do Cloud Run.

Passo 7: Instrumente Cloud Run Jobs

Para jobs finitos, use o mesmo wrapper:

CMD ["elven-unified-observability", "--", "python", "-m", "app.jobs.reconcile"]

No modo manual, descarregue os sinais antes de retornar:

from elven_unified_observability import force_flush, init_from_env, shutdown

init_from_env()
try:
    run_job()
finally:
    force_flush()
    shutdown()

Passo 8: Configure um Collector sidecar opcional

No modo sidecar, a aplicação envia para loopback:

OTEL_EXPORTER_OTLP_ENDPOINT: "http://localhost:4318"
OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf"

Não configure LOGS_URL/LOKI_URL nem OTEL_LOGS_EXPORTER=none; os logs seguem para http://localhost:4318/v1/logs automaticamente.

Estrutura mínima do serviço:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: orders-api
spec:
  template:
    metadata:
      annotations:
        run.googleapis.com/container-dependencies: '{"app":["collector"]}'
    spec:
      containers:
        - name: app
          image: <APP_IMAGE>@sha256:<DIGEST>
          ports:
            - containerPort: 8080
        - name: collector
          image: <COLLECTOR_IMAGE>@sha256:<DIGEST>
          args:
            - --config=/etc/otelcol/config.yaml
          startupProbe:
            httpGet:
              path: /
              port: 13133
            periodSeconds: 2
            timeoutSeconds: 2
            failureThreshold: 30

O Collector precisa expor OTLP HTTP em 0.0.0.0:4318 e health check em 0.0.0.0:13133. Mantenha os secrets somente no container collector.

Configuração

Variáveis essenciais

Variável Default Descrição
UO_ENABLED habilitado Controle mestre da wrapper
UO_ENABLE_LOGGING herdado de UO_ENABLED Habilita logs
UO_ENABLE_METRICS herdado de UO_ENABLED Habilita métricas
UO_ENABLE_TRACING herdado de UO_ENABLED Habilita traces
UO_STRICT false Se true, falha o bootstrap quando um componente falha
UO_LOAD_DOTENV habilitado no preload Defina false no Cloud Run
OTEL_SERVICE_NAME unknown-service Identidade principal dos três sinais
OTEL_SERVICE_VERSION 1.0.0 Versão lógica da aplicação
OTEL_DEPLOYMENT_ENVIRONMENT development Ambiente de execução
OTEL_RESOURCE_ATTRIBUTES vazio Atributos adicionais separados por vírgula

Logs

O transporte OTLP é autodetectado a partir da URL-base. As variáveis abaixo são opcionais para rotas diferentes, ajustes de captura ou compatibilidade com Loki direto.

Variável Default Descrição
LOGS_EXPORTER autodetectado Não configure no padrão OTLP; use otlp para forçar OTLP quando houver env legada herdada, ou loki no modo direto
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT derivado do OTLP genérico Override opcional para uma rota de logs diferente; informe a URL completa
LOGS_INTERCEPT_CONSOLE false Captura saída de console como print(); logs do logging usam a bridge própria
LOGS_PRESERVE_ORIGINAL_CONSOLE true Preserva stdout e handlers originais
UO_LOGGING_INTEGRATIONS bridge logging Use auto para detectar loguru e structlog
LOGS_FILTER_LEVELS debug,info,warn,error,fatal Níveis enviados
LOGS_FILTER_SAMPLING_RATE 1.0 Amostragem de logs entre 0.0 e 1.0
LOGS_FILTER_SANITIZE true Mascara dados sensíveis
LOGS_FILTER_MAX_MESSAGE_LENGTH 8192 Tamanho máximo da mensagem
LOGS_BUFFER_MAX_SIZE 100 Quantidade de registros por lote
LOGS_BUFFER_FLUSH_INTERVAL 5000 Intervalo de flush em milissegundos
LOGS_TIMEOUT 10000 Timeout de envio em milissegundos
LOGS_MAX_RETRIES 3 Tentativas de reenvio
LOGS_SILENT_ERRORS false Se false, falhas aparecem no stderr

Use LOGS_FILTER_LEVELS=debug,info,warn,error,fatal somente quando o volume e a política de retenção permitirem. Em produção, comece com info,warn,error,fatal.

Traces

Variável Default Descrição
OTEL_TRACES_EXPORTER otlp Exporter de traces
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT derivado do endpoint genérico Endpoint completo /v1/traces
OTEL_TRACES_SAMPLER sampler compatível da distribuição Sampler OpenTelemetry
OTEL_TRACES_SAMPLER_ARG conforme sampler Razão do sampler
UO_TRACING_RATIO não definido Override de razão da wrapper
UO_TRACING_MAX_SPANS_PER_TRACE sem limite Limite defensivo de spans por trace
OTEL_BSP_SCHEDULE_DELAY default do SDK Intervalo do BatchSpanProcessor
OTEL_BSP_MAX_QUEUE_SIZE default do SDK Limite da fila de spans
OTEL_BSP_MAX_EXPORT_BATCH_SIZE default do SDK Tamanho máximo do lote

Use sampling 1.0 durante a validação inicial e reduza conscientemente em produção. O limite de spans protege o backend, mas não substitui a divisão correta de processos longos em traces independentes.

Métricas

Variável Default Descrição
OTEL_METRICS_EXPORTER otlp Exporter de métricas
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT derivado do endpoint genérico Endpoint completo /v1/metrics
OTEL_METRIC_EXPORT_INTERVAL 60000 Intervalo periódico em milissegundos
OTEL_METRIC_EXPORT_TIMEOUT 30000 Timeout de exportação em milissegundos
LOGS_ENABLE_METRICS true Métricas internas do interceptor de logs

UO_ENABLE_METRICS controla a telemetria unificada. LOGS_ENABLE_METRICS controla somente as métricas internas do logger.

Headers e secrets

Use um único secret quando logs, métricas e traces compartilham autenticação:

OTEL_EXPORTER_OTLP_HEADERS: "<INJETADO_PELO_SECRET_MANAGER>"

Para headers exclusivos de logs, use LOGS_OTLP_HEADERS. Não armazene o valor no arquivo cloudrun.env.yaml.

Privacidade e redaction

Logs são sanitizados por padrão. A regra mascara valores associados a chaves e padrões como:

  • password, passwd, pwd, senha;
  • token, jwt, secret, api_key, authorization;
  • cpf, cnpj, documento, passaporte e SSN;
  • cartão, CVV, conta bancária, IBAN e chave Pix;
  • e-mail e credenciais.

A mensagem ao redor do valor é preservada. Em objetos, somente os campos sensíveis são substituídos por [REDACTED].

LOGS_FILTER_SANITIZE: "true"
OTEL_PRIVACY_REDACT_DB_STATEMENT: "true"
OTEL_PRIVACY_HASH_USER_ID: "true"

Cuidado: LOGS_FILTER_SANITIZE=false desativa a proteção dos logs. Use somente após aprovação formal, com prazo definido e sem payloads reais de clientes.

Métricas e traces customizados

Use os proxies globais depois do bootstrap:

from elven_unified_observability import logger, metrics, tracer


def process_order(order_id: str) -> None:
    def run(span):
        span.set_attribute("order.id", order_id)
        logger.info("Processando pedido", context={"order_id": order_id})
        metrics.increment("orders.processed", 1, {"result": "success"})

    tracer.with_span("orders.process", run)

Não envie payloads, tokens, documentos ou dados pessoais como atributos de span, labels de métrica ou labels de log.

CPU e batching no Cloud Run

Os exportadores usam filas e threads em background. Há duas opções:

Billing Comportamento Recomendação
Request-based CPU pode ser limitada fora das requisições Aceite atraso entre requests ou faça flush explícito em jobs curtos
Instance-based CPU permanece alocada durante a vida da instância Recomendado para flush periódico previsível

Não use métricas de alta cardinalidade. IDs de usuário, pedido, requisição ou trace não devem ser labels de métricas.

Controle por revisão

Cada alteração de imagem, variável ou secret cria uma nova revisão do Cloud Run. Use revisões para rollout canário e rollback.

Publique sem tráfego

gcloud run deploy "${SERVICE}" \
  --project "${PROJECT_ID}" \
  --region "${REGION}" \
  --image "${IMAGE}" \
  --env-vars-file cloudrun.env.yaml \
  --update-secrets "OTEL_EXPORTER_OTLP_HEADERS=elven-otlp-headers:1" \
  --no-traffic

Envie tráfego canário

gcloud run services update-traffic "${SERVICE}" \
  --project "${PROJECT_ID}" \
  --region "${REGION}" \
  --to-revisions "<NOVA_REVISAO>=10,<REVISAO_ESTAVEL>=90"

Promova ou reverta

Promova somente depois de validar os três sinais:

gcloud run services update-traffic "${SERVICE}" \
  --project "${PROJECT_ID}" \
  --region "${REGION}" \
  --to-revisions "<NOVA_REVISAO>=100"

Para rollback, direcione 100% para a revisão estável anterior. Não altere simultaneamente imagem, endpoints, sampling e regras de rede durante o primeiro rollout.

Validação ponta a ponta

Valide separadamente instalação, runtime, transporte e persistência no backend.

1. Verifique a imagem

docker run --rm "${IMAGE}" python --version
docker run --rm "${IMAGE}" python -m pip check

Confirme Python 3.11.16 no caso do cliente que originou este guia.

2. Verifique a configuração da revisão

gcloud run services describe "${SERVICE}" \
  --project "${PROJECT_ID}" \
  --region "${REGION}" \
  --format export

Confirme:

  • comando iniciado por elven-unified-observability;
  • imagem correta;
  • endpoints corretos;
  • secret montado como OTEL_EXPORTER_OTLP_HEADERS;
  • CPU e concorrência conforme planejado;
  • ausência de LOGS_URL, LOKI_URL e OTEL_LOGS_EXPORTER=none para autodetecção OTLP; se alguma env legada for obrigatória, use LOGS_EXPORTER=otlp como override.

3. Verifique o startup

gcloud run services logs read "${SERVICE}" \
  --project "${PROJECT_ID}" \
  --region "${REGION}" \
  --limit 100

Não devem aparecer mensagens de componente ignorado, erro de configuração, 401, 404, timeout ou conflito de provider OpenTelemetry.

4. Gere tráfego conhecido

SERVICE_URL=$(gcloud run services describe "${SERVICE}" \
  --project "${PROJECT_ID}" \
  --region "${REGION}" \
  --format='value(status.url)')

curl --fail --show-error --silent "${SERVICE_URL}/health"
curl --fail --show-error --silent "${SERVICE_URL}/<ENDPOINT_DE_TESTE>"

Use um endpoint autorizado e sem dados sensíveis.

5. Verifique o trace no Tempo

Use TraceQL:

{ resource.service.name = "orders-api" }

Confirme rota, status HTTP, duração e spans filhos esperados.

6. Verifique logs no Loki

O label final depende do mapeamento do Collector. Tente a convenção configurada no ambiente:

{service_name="orders-api"}

Quando o pipeline mantém o label legado:

{app="orders-api"}

Confirme que um log emitido dentro da requisição contém trace_id e span_id não nulos e que o texto preserva campos não sensíveis.

7. Verifique métricas no Mimir

Use uma consulta inicial por identidade:

count({service_name="orders-api"})

Confirme métricas HTTP após gerar tráfego e uma métrica customizada conhecida quando a aplicação a emitir.

8. Verifique shutdown

Publique uma nova revisão ou reduza instâncias e acompanhe os logs. O processo deve receber SIGTERM, concluir o flush e sair sem traceback de shutdown.

Importante: Um 2xx do Collector comprova aceite do lote, não persistência no Loki, Tempo ou Mimir. A validação só termina quando os três sinais aparecem no backend correto.

Troubleshooting

O container não inicia ou não escuta a porta

Sintoma. A revisão não fica pronta e o Cloud Run informa que o container não escutou em PORT.

Causa provável. O servidor escuta em 127.0.0.1, usa outra porta ou o import path da aplicação está errado.

Fix.

  1. Configure --host 0.0.0.0 --port 8080.
  2. Confirme o containerPort da revisão.
  3. Rode a imagem localmente e teste /health.
  4. Verifique o traceback de import no Cloud Logging.

O comando elven-unified-observability não existe

Sintoma. O processo encerra com command not found.

Causa provável. A dependência não foi instalada na etapa final da imagem ou existe mais de um ambiente virtual.

Fix.

  1. Execute python -m pip show elven-unified-observability-py na imagem final.
  2. Execute python -m pip check.
  3. Confirme que pip e o comando do container pertencem ao mesmo Python.
  4. Evite criar um virtualenv depois de instalar as dependências globalmente.

O build falha por conflito de httpx ou httpcore

Sintoma. pip retorna ResolutionImpossible.

Causa provável. A aplicação fixa versões anteriores às aceitas pelo interceptor.

Fix.

  1. Garanta httpx>=0.24.1,<0.29.0.
  2. Remova o pin direto de httpcore quando ele não for uma dependência usada pela aplicação.
  3. Regenere o lock.
  4. Execute os testes da aplicação e pip check dentro da imagem.

Logs aparecem no Cloud Logging, mas não no Loki

Sintoma. stdout funciona, mas não existem logs no backend Elven.

Causa provável. Exporter incorreto, endpoint de logs ausente, autenticação inválida, filtro de nível ou CPU limitada antes do flush.

Fix.

  1. Confirme OTEL_EXPORTER_OTLP_ENDPOINT com a URL-base aprovada; a wrapper deriva /v1/logs. Se houver rota exclusiva para logs, confira OTEL_EXPORTER_OTLP_LOGS_ENDPOINT completo.
  2. Remova LOGS_URL, LOKI_URL e OTEL_LOGS_EXPORTER=none do caminho automático. Use LOGS_EXPORTER=otlp somente se não puder remover uma env legada herdada.
  3. Confirme o secret de headers e a permissão da service account.
  4. Mantenha LOGS_SILENT_ERRORS=false durante o diagnóstico.
  5. Teste --no-cpu-throttling e LOGS_BUFFER_FLUSH_INTERVAL=2000.

Uvicorn ou Gunicorn aparecem, mas logs da aplicação não

Sintoma. Só aparecem access logs ou mensagens de startup.

Causa provável. Bridge do Python desabilitada, nível filtrado ou inicialização depois dos imports da aplicação.

Fix.

  1. Use a CLI antes do servidor.
  2. Defina UO_ENABLE_PYTHON_LOGGING_BRIDGE=true.
  3. Confirme LOGS_FILTER_LEVELS.
  4. Para loguru ou structlog, use UO_LOGGING_INTEGRATIONS=auto e o extra [full].
  5. Não substitua o bootstrap pela simples importação de logger.

Logs aparecem duplicados

Sintoma. A mesma mensagem chega duas ou mais vezes.

Causa provável. Dois bootstraps, OpenTelemetry Logs ativo junto com o interceptor ou múltiplos handlers equivalentes.

Fix.

  1. Mantenha somente elven-unified-observability -- ....
  2. Não use OTEL_LOGS_EXPORTER=none no caminho automático: na wrapper 0.1.22 isso desativa a seleção OTLP. Remova outros agentes/exporters de logs duplicados ou force LOGS_EXPORTER=otlp se a env for herdada.
  3. Desative LOGS_AUTO_INIT quando estiver definido externamente.
  4. Remova opentelemetry-instrument do comando.
  5. Verifique quantos workers Python existem por instância.

Logs mostram [REDACTED] em campos sensíveis

Sintoma. Valores de token, CPF, e-mail ou credenciais aparecem mascarados.

Causa provável. Sanitização de segurança funcionando conforme configuração.

Fix. Não desative em produção. Se um campo não sensível estiver sendo classificado incorretamente, envie um exemplo sintético para ajuste da regra sem expor dados reais.

Traces não aparecem

Sintoma. Logs chegam, mas Tempo não retorna traces.

Causa provável. Endpoint incorreto, sampling baixo, framework importado antes do bootstrap ou conflito com outro provider.

Fix.

  1. Confirme UO_ENABLE_TRACING=true e OTEL_TRACES_EXPORTER=otlp.
  2. Valide o endpoint /v1/traces e os headers.
  3. Use temporariamente OTEL_TRACES_SAMPLER_ARG=1.0 em homologação.
  4. Confirme que a CLI precede o Uvicorn ou Gunicorn.
  5. Remova outro agente OpenTelemetry do processo.

Métricas chegam com atraso ou não aparecem quando o serviço está ocioso

Sintoma. Métricas são exportadas apenas depois de novas requisições.

Causa provável. Billing request-based limita CPU fora das requisições.

Fix.

  1. Use instance-based billing com --no-cpu-throttling quando a exportação periódica for obrigatória.
  2. Reduza OTEL_METRIC_EXPORT_INTERVAL sem criar volume excessivo.
  3. Para jobs curtos, chame force_flush() antes da saída.
  4. Confirme métricas no backend, não apenas resposta do Collector.

O Collector retorna 401, 404 ou timeout

Sintoma. O interceptor registra falha de exportação.

Causa provável. Header inválido, endpoint com caminho incorreto ou falta de conectividade.

Fix.

  1. 401: valide secret, versão do secret, tenant e contrato de autenticação.
  2. 404: valide os caminhos /v1/logs, /v1/traces e /v1/metrics.
  3. Timeout: valide DNS, egress, VPC Connector, firewall e rota de retorno.
  4. Não aponte a URL-base OTLP ou o endpoint de logs para /loki/api/v1/push; essa rota é exclusiva do modo Loki direto.

FAQ

A biblioteca roda como daemon separado no Cloud Run?

Não. Ela roda dentro de cada processo Python da aplicação. Threads internas cuidam de buffers e exportação enquanto a instância está ativa.

Python 3.11.16 é suportado?

Sim, está dentro do requisito publicado Python >=3.10. O Dockerfile deste guia usa python:3.11.16-slim-bookworm. Ainda assim, valide o conjunto completo de dependências da aplicação com pip check e testes de homologação.

Preciso alterar o código da aplicação?

Não no caminho zero-code. Instale o pacote, configure as variáveis e inicie o servidor com elven-unified-observability -- .... Alterações de código são necessárias somente para spans, métricas e logs de negócio customizados.

A biblioteca captura todos os logs do container?

Ela captura logs produzidos pelo processo Python por logging, print(), exceções não tratadas e bridges suportadas. Saída de binários externos, sidecars ou subprocessos não Python continua sob responsabilidade do Cloud Logging ou do Collector.

Preciso definir LOGS_EXPORTER ou LOGS_URL para enviar logs por OTLP?

Não. Com OTEL_EXPORTER_OTLP_ENDPOINT e sem LOGS_URL/LOKI_URL, a wrapper 0.1.22 autodetecta OTLP. Uma URL Loki herdada muda a seleção para Loki; remova-a ou force LOGS_EXPORTER=otlp. Também não defina OTEL_LOGS_EXPORTER=none no caminho automático.

Preciso manter uma instância mínima?

Não para serviços puramente orientados a requisição. Configure min-instances=1 somente quando houver requisito de baixa latência, processamento em background contínuo ou métricas periódicas durante ociosidade. Isso altera o custo.

Posso usar múltiplos workers?

Sim, mas cada worker mantém providers e buffers próprios. Comece com um worker por instância e aumente somente após teste de carga, validação de duplicidade e cardinalidade.

O que acontece se o Collector ficar indisponível?

O interceptor usa timeout, retry, circuit breaker e buffer limitado. Com UO_STRICT=false, uma falha de telemetria não deve impedir a aplicação de iniciar. Falhas continuam visíveis em stderr quando LOGS_SILENT_ERRORS=false.

Posso desativar a redaction?

Tecnicamente, LOGS_FILTER_SANITIZE=false desativa a sanitização dos logs. Não use essa opção em produção sem aprovação formal de segurança e privacidade.

Veja também