---
title: "Prometheus e metrics em Rust: Axum em Produção | Rust Brasil"
url: "https://rustlang.com.br/blog/prometheus-metrics-rust-axum-producao-2026/"
markdown_url: "https://rustlang.com.br/blog/prometheus-metrics-rust-axum-producao-2026.MD"
description: "Guia prático de métricas Prometheus em Rust com Axum: counters, histograms, labels seguros, /metrics, SLIs e a escolha entre metrics e prometheus_client."
date: "2026-09-22"
author: "Equipe Rust Brasil"
---

# Prometheus e metrics em Rust: Axum em Produção | Rust Brasil

Guia prático de métricas Prometheus em Rust com Axum: counters, histograms, labels seguros, /metrics, SLIs e a escolha entre metrics e prometheus_client.


**Para instrumentar uma API [Axum](/ecossistema/axum/) com métricas que o Prometheus consegue scrapear, use o crate `metrics` com `metrics-exporter-prometheus`, exponha um `/metrics` protegido e registre counters, gauges e histograms com labels de baixa cardinalidade.** Isso responde perguntas de produção que logs sozinhos não resolvem: quantas requisições por segundo, qual a taxa de 5xx, qual o p95 por rota e se o pool do banco está saturando. Tracing e [OpenTelemetry](/blog/rust-opentelemetry-producao-2026/) continuam essenciais para investigar um caso; métricas sustentam SLIs, alertas e capacidade.

Este guia fecha o espaço entre o [artigo amplo de logging e observabilidade](/artigos/logging-observabilidade/) e o [projeto didático de coletor Prometheus](/projetos/metrics-collector/): aqui o foco é o caminho de produção em Rust — crates, Axum, labels seguros, histogramas, erros comuns e checklist.

## Resposta rápida: qual ferramenta para cada sinal

| Pergunta operacional | Sinal | Ferramenta típica em Rust |
|---|---|---|
| A API está saudável agora? | métricas | `metrics` + Prometheus/Grafana |
| Por que esta requisição falhou? | traces/logs | [tracing](/ecossistema/tracing/) + OTel |
| Qual o p95 da rota `/pedidos`? | histograma | `histogram!("http_request_duration_seconds")` |
| O pool do Postgres está no limite? | gauge | saturação de conexões / espera |
| Preciso alertar indisponibilidade? | SLI/SLO | taxa de sucesso + burn rate |
| Quero portabilidade entre backends | telemetria padrão | OpenTelemetry Metrics |
| Quero scrape clássico no cluster | exposição HTTP | `/metrics` + ServiceMonitor |

Regra prática: **métricas para tendência e alerta; traces para causa; logs para contexto**. Se você só tem `println!`, ainda não tem observabilidade de produção.

## O que Prometheus espera de um serviço Rust

Prometheus funciona por **pull**: um scraper consulta um endpoint texto, lê séries e armazena amostras. Seu binário Rust não “empurra” cada contador a cada request para o servidor (salvo Pushgateway em jobs curtos). O contrato é estável:

1. expor um endpoint HTTP, em geral `/metrics`;
2. devolver texto no formato Prometheus/OpenMetrics;
3. manter nomes estáveis, tipos corretos e labels controlados;
4. evitar cardinalidade explosiva;
5. instrumentar o que sustenta decisão operacional, não cada detalhe interno.

Em Rust, duas famílias aparecem com frequência:

- **`metrics` + `metrics-exporter-prometheus`**: API ergonômica (`counter!`, `histogram!`, `gauge!`) e exporter pronto. Bom padrão para Axum/Tokio.
- **`prometheus_client`**: registry explícito, controle fino do formato OpenMetrics e tipos encoding-first. Útil quando a equipe quer ownership total do modelo de métricas.

Para a maioria dos backends Axum em 2026, comece por `metrics`. Troque ou combine com `prometheus_client` só com motivo concreto (formato, restrições de encoding, integração interna já padronizada).

## Setup mínimo com Axum

Dependências típicas:

```toml
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal"] }
tower = "0.5"
tower-http = { version = "0.6", features = ["trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
metrics = "0.24"
metrics-exporter-prometheus = "0.16"
```

Bootstrap do exporter e do servidor:

```rust
use axum::{
    extract::State,
    http::{Request, StatusCode},
    middleware::{self, Next},
    response::{IntoResponse, Response},
    routing::get,
    Router,
};
use metrics::{counter, histogram};
use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle};
use std::time::Instant;
use tower_http::trace::TraceLayer;

#[derive(Clone)]
struct AppState {
    metrics: PrometheusHandle,
}

async fn health() -> &'static str {
    "ok"
}

async fn metrics_handler(State(state): State<AppState>) -> impl IntoResponse {
    state.metrics.render()
}

async fn http_metrics(req: Request<axum::body::Body>, next: Next) -> Response {
    let method = req.method().as_str().to_owned();
    let route = req
        .uri()
        .path()
        .trim_end_matches('/')
        .to_owned();
    let started = Instant::now();

    let response = next.run(req).await;
    let status = response.status().as_u16().to_string();
    let elapsed = started.elapsed().as_secs_f64();

    // Em produção, normalize a rota para templates (/users/:id), não path bruto.
    counter!(
        "http_requests_total",
        "method" => method.clone(),
        "route" => route.clone(),
        "status" => status
    )
    .increment(1);

    histogram!(
        "http_request_duration_seconds",
        "method" => method,
        "route" => route
    )
    .record(elapsed);

    response
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt()
        .with_env_filter("info")
        .init();

    let metrics = PrometheusBuilder::new()
        .install_recorder()
        .expect("falha ao instalar recorder Prometheus");

    let state = AppState { metrics };

    let app = Router::new()
        .route("/health", get(health))
        .route("/metrics", get(metrics_handler))
        .layer(middleware::from_fn(http_metrics))
        .layer(TraceLayer::new_for_http())
        .with_state(state);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .expect("bind");
    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal())
        .await
        .expect("serve");
}

async fn shutdown_signal() {
    let _ = tokio::signal::ctrl_c().await;
}
```

O exemplo acima é didático. Em produção, separe a porta de métricas da porta pública, normalize rotas e evite label com path cru. O [tower-http](/blog/tower-http-middleware-producao-axum-2026/) já cobre `TraceLayer`, timeouts e request ID; métricas entram como sinal agregado ao lado desses middlewares.

## Counters, gauges e histograms na prática

### Counter

Use para eventos que só aumentam: requisições, erros, timeouts, retries, jobs concluídos.

```rust
counter!("payments_authorized_total", "provider" => "stripe").increment(1);
counter!("db_query_errors_total", "operation" => "insert_order").increment(1);
```

Nunca use counter para valor que sobe e desce. Reinício do processo zera o counter; o Prometheus lida com isso via `rate()`/`increase()`.

### Gauge

Use para nível atual: conexões ativas, tamanho de fila, goroutines equivalentes (tasks), memória estimada de um cache local, tickets disponíveis de um semáforo.

```rust
use metrics::gauge;

gauge!("db_pool_connections_in_use").set(12.0);
gauge!("queue_depth", "queue" => "email").set(340.0);
```

Gauge é o sinal certo para saturação. Se o pool do [SQLx](/ecossistema/sqlx/) passa muito tempo no máximo, o problema aparece aqui antes do timeout virar incidente.

### Histogram

Use para distribuição: duração de request, tempo de query, tamanho de payload, lag de consumo. Em SLOs de latência, histograma (ou summary, com trade-offs) é o instrumento central.

```rust
histogram!(
    "db_query_duration_seconds",
    "operation" => "select_order"
)
.record(elapsed);
```

Defina buckets alinhados ao orçamento do produto. Buckets genéricos demais escondem regressões; buckets absurdamente finos aumentam custo. Para APIs web, faixas em torno de 5ms–5s costumam ser um ponto de partida — ajuste com dados reais.

## Labels seguros: a diferença entre insight e incidente de custo

Labels transformam uma métrica em várias séries. Isso é poder e risco.

**Faça**

- `method`: `GET`, `POST`
- `route`: `/users/:id`, `/orders`
- `status_class`: `2xx`, `4xx`, `5xx` (ou status code se o conjunto for pequeno)
- `dependency`: `postgres`, `redis`, `stripe`
- `outcome`: `success`, `timeout`, `error`

**Não faça**

- `user_id`, `email`, `cpf`, `order_uuid`
- path bruto `/users/a1b2c3...`
- mensagem de erro completa
- hostname de pod com cardinalidade alta se não for necessário
- versionagem fina demais em todo sinal

Uma regra útil: se o valor do label pode crescer sem limite com o tráfego de usuários, ele não pertence à métrica. Coloque esse identificador em [tracing](/ecossistema/tracing/) pontual, não em série temporal global.

## SLIs que um backend Rust deveria expor cedo

Não comece pelo dashboard mais bonito. Comece pelas perguntas de plantão:

1. **Disponibilidade**: proporção de respostas não-5xx em janela móvel.
2. **Latência**: p95/p99 por rota crítica.
3. **Saturação**: pool de banco, tamanho de fila, tasks bloqueadas, uso de memória do processo.
4. **Erros por dependência**: Postgres, Redis, HTTP externo, fila.
5. **Trabalho assíncrono**: jobs processados, retries, dead letters.

Esses cinco grupos já sustentam alertas acionáveis. Depois você refina por tenant, região ou feature flag — com parcimônia.

Para quem opera serviços async, combine com [tokio-console](/blog/tokio-console-debug-tasks-async-rust-2026/) no diagnóstico e com métricas estáveis no dia a dia. Console explica o runtime numa sessão; Prometheus mostra a tendência na semana.

## Métricas, Tracing e OpenTelemetry: como encaixar

O site já cobre os outros vértices:

- [Logging e observabilidade](/artigos/logging-observabilidade/) — panorama e base com `tracing`
- [Tracing vs log](/artigos/tracing-vs-log/) — quando cada sinal responde melhor
- [OpenTelemetry em produção](/blog/rust-opentelemetry-producao-2026/) — exportação e correlação
- [Tracing no ecossistema](/ecossistema/tracing/) — spans e eventos na aplicação

O desenho saudável em Rust costuma ser:

1. instrumentar a aplicação com `tracing` nos limites úteis;
2. expor métricas Prometheus dos SLIs;
3. exportar traces (e, se fizer sentido, metrics) via OTel para o backend da empresa;
4. manter configuração de exportação na borda, longe do domínio.

Não force um único fornecedor no meio do handler. O código de negócio deve dizer “autorização concluída” ou “query lenta”; a infraestrutura decide se isso vira série Prometheus, span Tempo ou ambos.

## Protegendo `/metrics` e o runtime

`/metrics` não é página de marketing. Ele revela superfície interna: nomes de rotas, dependências, throughput, erros e às vezes topologia. Práticas mínimas:

- porta separada (`:9090` admin vs `:3000` público);
- NetworkPolicy / security group restrito aos scrapers;
- em desenvolvimento local, bind em `127.0.0.1` quando possível;
- graceful shutdown para não cortar scrapes no meio do deploy;
- evitar alocações pesadas no caminho quente só para telemetria;
- não registrar valores sensíveis em labels ou em exemplos de documentação interna.

Se o serviço usa [Moka](/blog/moka-cache-rust-producao-2026/) ou Redis, exponha hit/miss e evictions com labels curtos. Cache sem métrica vira achismo.

## Erros comuns

### Path bruto como label

Transforma cada ID em série nova. Normalize para o template da rota Axum.

### Medir só contagem e esquecer latência

Um serviço pode estar “up” e mesmo assim inútil com p95 de 8s.

### Misturar domínio e infraestrutura na mesma métrica sem legenda

Separe `http_requests_total` de `payments_authorized_total`. Alertas ficam mais claros.

### Cardinalidade “só temporária”

Série temporal rara quase nunca é temporária no Prometheus. Remover depois não apaga o custo histórico de imediato.

### Instrumentar tudo na primeira semana

Comece pelos SLIs. Adicione métricas quando uma pergunta operacional real aparecer duas vezes.

### Expor `/metrics` público

É detalhe de segurança e de abuso. Trate como endpoint administrativo.

### Achar que OTel elimina Prometheus

Muitas empresas ainda scrapam Prometheus. OTel e Prometheus convivem; a escolha é de arquitetura, não de moda.

### Usar Pushgateway para API longa

Pushgateway é para jobs batch/efêmeros. API Axum estável deve ser scrapada.

## Checklist de produção

1. Escolha `metrics` + exporter Prometheus ou `prometheus_client` com motivo explícito.
2. Exponha `/metrics` em superfície administrativa.
3. Registre request rate, error rate e duração por rota normalizada.
4. Adicione saturação de pool, fila e timeouts.
5. Revise labels contra explosão de cardinalidade.
6. Defina buckets de histograma a partir do orçamento de latência.
7. Crie alertas para burn de SLO, não só para CPU alta.
8. Correlacione com tracing/OTel nos incidentes.
9. Documente no README como scrapear localmente e no cluster.
10. Teste reinício do processo: counters zeram, dashboards com `rate()` continuam corretos.
11. Inclua métricas no smoke de deploy (scrape ok + série esperada).
12. Revise trimestralmente séries mortas e labels inúteis.

## Prometheus como evidência de carreira

Em [vagas Rust](/vagas/) de backend, plataforma e SRE, “sei Rust” pesa menos que “operei um serviço Rust”. Um repositório bom mostra:

- API Axum com middleware de métricas;
- `/metrics` scrapável;
- dashboard Grafana exportado em JSON;
- alerta de taxa de erro e latência;
- README com SLIs e como reproduzir carga;
- comparação antes/depois de uma otimização com p95.

Isso conversa com [empresas que usam Rust](/empresas/), com o nicho de [backend web](/carreira/nicho-web-backend/) e com trilhas de [DevOps/infra](/carreira/nicho-devops-infra/). Se quiser praticar o formato Prometheus do zero, o [projeto de coletor de métricas](/projetos/metrics-collector/) continua útil como laboratório; este guia é o passo seguinte, plugado em Axum de verdade.

## Perguntas frequentes

### Qual crate usar para métricas Prometheus em Rust?

Para a maioria das APIs Axum, `metrics` com `metrics-exporter-prometheus` resolve rápido e com boa ergonomia. Escolha `prometheus_client` quando precisar de registry/encoding mais explícitos ou quando o padrão interno da empresa já for esse.

### Métricas substituem tracing e OpenTelemetry?

Não. Métricas agregam saúde e alimentam SLO. Tracing explica o caminho de uma requisição. OpenTelemetry padroniza a exportação. Os três se complementam.

### Onde expor o endpoint `/metrics`?

Em porta administrativa ou rede restrita aos scrapers. Evite deixá-lo aberto na mesma borda pública da API de usuários sem controle de acesso.

### Quais métricas uma API Rust precisa no começo?

Taxa de requisições, erros 5xx, latência por rota normalizada, saturação de pool, timeouts/retries e profundidade de fila quando houver workers assíncronos.

### Por que cardinalidade de labels é perigosa?

Porque cada combinação vira série armazenada e consultada. IDs de usuário, UUIDs e paths parametrizados fazem o custo crescer com o tráfego e degradam consultas.

### Quando preferir OpenTelemetry Metrics em vez de Prometheus direto?

Quando a organização já centraliza telemetria via OTLP e quer um pipeline único. Se o padrão local é Prometheus + Grafana e o serviço é um binário Rust simples, o exporter direto costuma ser o caminho mais curto.

## Conclusão

Prometheus em Rust deixa de ser “detalhe de DevOps” quando o serviço precisa provar comportamento sob carga. Com `metrics`, histograms bem escolhidos, labels disciplinados e um `/metrics` protegido, uma API Axum ganha SLIs reais — e o time deixa de depurar produção no escuro. Continue com [OpenTelemetry em produção](/blog/rust-opentelemetry-producao-2026/), [tower-http](/blog/tower-http-middleware-producao-axum-2026/), [Tokio](/ecossistema/tokio/) e o panorama de [logging e observabilidade](/artigos/logging-observabilidade/). Para aplicar no mercado, acompanhe as [vagas](/vagas/) e as [empresas](/empresas/) que já operam Rust em produção no Brasil.
