Rust com OpenTelemetry: Traces, Métricas e Produção em 2026

Guia prático para instrumentar serviços Rust com OpenTelemetry em produção: tracing, métricas, contexto distribuído, custos, SLOs e carreira backend.

Por que OpenTelemetry virou assunto para dev Rust

Rust aparece cada vez mais em serviços onde latência, consumo de memória, confiabilidade e segurança operacional importam. Mas escrever um binário rápido não basta quando ele roda dentro de uma arquitetura real. Uma API Axum pode chamar PostgreSQL com SQLx, publicar eventos em Kafka, consultar Redis, conversar por gRPC com Tonic e depender de uma API externa. Quando algo fica lento, a pergunta deixa de ser “o código compila?” e vira: qual parte do caminho está degradada?

É aí que OpenTelemetry em Rust entra. OpenTelemetry não é apenas uma biblioteca de logs. É um padrão para representar traces, métricas, logs e contexto distribuído entre serviços. Ele permite seguir uma requisição desde a borda HTTP até chamadas internas, filas, banco de dados e workers. Em vez de cada serviço inventar seus próprios campos, você passa a trabalhar com nomes, atributos e protocolos que ferramentas de observabilidade entendem.

Para quem busca vagas Rust de backend, plataforma, infraestrutura ou SRE, esse conhecimento pesa porque empresas não contratam apenas alguém que sabe ownership. Elas querem gente capaz de operar sistemas. Rust ajuda a construir serviços previsíveis; OpenTelemetry ajuda a provar, medir e investigar essa previsibilidade em produção.

Tracing primeiro, OpenTelemetry depois

No ecossistema Rust, o caminho saudável começa com Tracing. A crate tracing organiza eventos e spans dentro da aplicação. Um span representa uma unidade de trabalho com contexto: uma requisição HTTP, uma query, uma etapa de processamento, uma chamada externa ou um job. Eventos vivem dentro desses spans e carregam campos estruturados.

OpenTelemetry entra como camada de exportação e interoperabilidade. Você pode instrumentar o serviço com tracing, configurar tracing-subscriber para logs locais e adicionar tracing-opentelemetry para transformar spans em traces exportáveis. Isso evita prender seu código de negócio a um fornecedor específico. Hoje o backend pode ser Jaeger; amanhã Grafana Tempo, Datadog, Honeycomb ou um coletor OTLP interno.

Essa separação importa para manutenção. Handlers, repositórios e workers devem criar spans úteis, não saber detalhes de endpoint OTLP, sampling ou autenticação do coletor. Configuração de exportação fica na borda da aplicação, junto de variáveis de ambiente, filtros e inicialização do subscriber.

O que instrumentar em uma API Rust

Um serviço Rust observável não precisa transformar tudo em trace gigante. Instrumente os pontos que ajudam a responder perguntas de produção:

  • entrada HTTP: método, rota normalizada, status, duração e request ID;
  • banco de dados: operação, tabela ou nome lógico, duração e classe de erro;
  • chamadas externas: host lógico, timeout, status e tentativa;
  • filas e eventos: event_id, tópico ou fila, tentativa, resultado e tempo de processamento;
  • decisões de resiliência: timeout, retry, rate limit, load shedding e fallback;
  • fronteiras de negócio: pedido criado, pagamento autorizado, arquivo processado ou job concluído.

Evite dois extremos. O primeiro é instrumentação pobre: apenas info!("erro"), sem campos, sem span e sem correlação. O segundo é instrumentação excessiva: criar span para cada função pequena, registrar payload inteiro, vazar dado sensível e multiplicar custo de armazenamento. Observabilidade boa é seletiva. Ela mostra o caminho do usuário e das dependências críticas, não cada detalhe irrelevante do processo.

Em Axum, o ponto natural é colocar uma camada de trace no roteador e criar spans adicionais dentro dos handlers quando a operação tem etapas importantes. Em Tokio, tasks em background precisam carregar contexto explicitamente quando fazem parte da mesma operação lógica. Se você cria uma task e perde o contexto da requisição, o trace fica quebrado exatamente onde a investigação precisaria continuar.

Métricas: traces não substituem SLO

Traces são excelentes para investigar uma requisição ou amostra de comportamento. Métricas são melhores para enxergar saúde agregada. Um serviço Rust em produção deveria expor ou exportar pelo menos taxa de requisições, taxa de erro, latência por percentil, saturação de pools, timeouts, retries, rejeições por rate limit e tamanho de filas quando houver processamento assíncrono.

OpenTelemetry Metrics ajuda a padronizar essa emissão, mas a decisão importante não é a crate: é o que você mede. Uma métrica como http_requests_total por rota e status ajuda. Uma métrica com user_id como label destrói cardinalidade e custo. O mesmo vale para email, cpf, pedido_id ou qualquer identificador quase único. Labels precisam ter cardinalidade controlada: rota normalizada, método, classe de erro, dependência lógica, ambiente e versão do serviço.

Para SLO, foque em indicadores que produto entende. Latência p95 de /checkout, erro 5xx em /api/pedidos, tempo de processamento de webhook, lag de consumer e taxa de dead letter dizem mais do que CPU média. Rust pode manter CPU baixa e ainda entregar uma experiência ruim se o banco está lento, se o pool está saturado ou se uma dependência externa virou gargalo.

Contexto distribuído em filas, gRPC e workers

Em serviços simples, a requisição entra por HTTP e termina ali. Em sistemas reais, ela dispara eventos, jobs e chamadas internas. OpenTelemetry usa propagação de contexto para conectar essas etapas. Em HTTP e gRPC, isso costuma passar por headers. Em filas, você precisa decidir como carregar traceparent ou metadados equivalentes na mensagem.

O cuidado é não tratar contexto como dado de negócio. Ele ajuda a depurar, mas não deve ser necessário para processar o evento. Se uma mensagem chega sem contexto, o worker deve criar um novo trace e seguir. Se chega com contexto inválido, registre e continue quando for seguro. Observabilidade não pode virar nova fonte de indisponibilidade.

No artigo sobre Rust para mensageria, a recomendação central é idempotência e dead letter bem explicada. OpenTelemetry complementa isso mostrando por que uma mensagem falhou, quantas tentativas aconteceram, qual dependência atrasou e como o erro se correlaciona com deploy ou pico de tráfego. Em serviços resilientes com Tower, Axum e Tokio, os eventos de timeout, retry e load shedding também deveriam aparecer como sinais observáveis.

Segurança, privacidade e custo

Observabilidade captura contexto de produção; por isso também pode capturar informação sensível. Nunca registre token, senha, documento, cartão, cookie completo, segredo de API ou payload bruto sem revisão. Em ambientes de fintech, saúde, RH ou governo, isso vira risco de compliance. Mesmo fora de áreas reguladas, vazar dado em log é incidente.

Use campos estáveis e seguros: route, method, status, dependency, error_class, attempt, tenant_tier, service_version. Para entidades, prefira identificadores internos quando necessário e pense em retenção. Se o time não precisa consultar traces de seis meses atrás, não armazene seis meses por padrão. Sampling também faz parte da arquitetura: talvez você colete todos os erros e apenas uma fração das requisições saudáveis.

Outro erro comum é exportar direto de cada aplicação para um fornecedor externo sem coletor intermediário. Em produção, um OpenTelemetry Collector local ou por cluster permite filtrar, amostrar, mascarar, rotear e trocar destino sem redeploy de todos os serviços. Para Rust, isso deixa a aplicação mais simples: ela fala OTLP para o coletor; o coletor decide o resto.

Projeto de portfólio: API observável em Rust

Um projeto forte para currículo é uma pequena API Axum com PostgreSQL, um worker e OpenTelemetry. Não precisa ser grande. O importante é mostrar decisões operacionais. A API recebe pedidos, grava no banco, publica um evento e um worker processa esse evento. Cada etapa cria spans com request_id, order_id mascarado ou sintético, event_id, duração e resultado. Métricas mostram latência da API, erros por classe, tempo do worker e quantidade de retries.

Inclua Docker Compose com API, PostgreSQL, OpenTelemetry Collector e um backend local como Jaeger ou Grafana Tempo. Documente como gerar carga, como simular falha do banco, como ver um trace quebrado, como identificar timeout e quais campos você evitou registrar por privacidade. Esse README demonstra mais maturidade do que apenas colocar opentelemetry no Cargo.toml.

Conecte o projeto a fundamentos que já aparecem no ecossistema Rust: Serde para contratos, SQLx para persistência, Tracing para spans, Tokio para concorrência, Tower para middleware, Cargo para organização e Clippy para qualidade. Se você também trabalha com Go, vale comparar a abordagem com materiais do Golang Brasil, porque muitas equipes brasileiras avaliam Rust e Go para plataforma, APIs internas e serviços de alta confiabilidade.

Checklist antes de chamar de produção

Antes de dizer que um serviço Rust está instrumentado para produção, revise:

  • spans têm nomes estáveis e úteis, não nomes aleatórios de função;
  • rotas aparecem normalizadas, sem IDs únicos no nome;
  • erros têm classe, causa segura e status coerente;
  • métricas evitam labels de alta cardinalidade;
  • dados sensíveis são mascarados ou não coletados;
  • contexto distribuído atravessa HTTP, gRPC e filas quando faz sentido;
  • falha no exportador de telemetria não derruba o serviço;
  • sampling e retenção estão documentados;
  • dashboards mostram SLOs reais, não apenas CPU e memória;
  • runbook explica como investigar um incidente usando trace, métrica e log.

Esse checklist é pragmático. OpenTelemetry não deixa uma arquitetura boa automaticamente. Ele expõe melhor o que já existe. Se o serviço não tem timeout, retry seguro, health check, idempotência e tratamento de erro, a telemetria só vai mostrar a bagunça com mais detalhes.

Conclusão

Rust com OpenTelemetry é uma combinação poderosa quando usada com sobriedade. A linguagem ajuda a construir serviços eficientes e previsíveis; a telemetria ajuda a entender esses serviços sob carga, falha e mudança. Para devs brasileiros, esse é um diferencial de carreira porque aproxima Rust do trabalho real de backend, plataforma e SRE.

Comece pequeno: tracing bem usado, logs estruturados, métricas básicas e um trace por operação importante. Depois adicione propagação distribuída, collector, sampling e dashboards ligados a SLO. O objetivo não é ter observabilidade bonita em demo. É conseguir responder rapidamente, em produção, o que quebrou, quem foi afetado, desde quando e qual próxima ação reduz o impacto.