Rastreabilidade métricas e logs com Loki e VictoriaMetrics

Rastreabilidade métricas e logs com Loki e VictoriaMetrics: aprenda um contrato de etiquetas e uso de trace_id para ir do alerta à linha de log em minutos.

Avatar de Danrley Pereira
Danrley Pereira
Rastreabilidade métricas e logs com Loki e VictoriaMetrics

Um alerta dispara às três da manhã: a taxa de erros do serviço de checkout subiu. O gráfico mostra que algo quebrou, mas não qual requisição, qual cliente nem qual linha de código. Rastreabilidade com Loki e VictoriaMetrics é a prática de ligar essas duas pontas: partir de uma métrica agregada e chegar, sem adivinhação, às linhas de log das requisições que causaram o problema.

Imagem de topo: a ponte entre um gráfico de métricas e linhas de log
Imagem de topo: a ponte entre um gráfico de métricas e linhas de log

Rastreabilidade entre métricas, logs e requisições no checkout
Rastreabilidade entre métricas, logs e requisições no checkout

Neste guia, você vai montar essa ponte com três peças open source: VictoriaMetrics para as métricas, Loki para os logs e Grafana Alloy como coletor único. O segredo não está em nenhuma ferramenta isolada, e sim em um contrato simples: as mesmas etiquetas e o mesmo identificador de requisição nos dois lados.

Por que métricas e logs sozinhos não bastam

Métricas são baratas e rápidas. Uma série como http_requests_total{service="checkout", code="500"} responde em milissegundos quantas requisições falharam nos últimos cinco minutos. Mas ela é agregada por natureza: não sabe nada de uma requisição específica.

Logs são o oposto. Cada linha conta a história de um evento, com detalhes, mas procurar "o erro certo" em milhões de linhas sem um ponto de partida é lento e frustrante.

A rastreabilidade nasce quando você consegue fazer a pergunta nas duas direções:

  • Da métrica para o log: "este pico de erros 500 no checkout, entre 03:02 e 03:07, veio de quais requisições?"
  • Do log para a métrica: "este erro de timeout no banco é um caso isolado ou faz parte de uma tendência?"

Para isso, métricas e logs precisam falar a mesma língua.

O contrato: etiquetas comuns e um identificador por requisição

Antes de configurar qualquer ferramenta, combine com o time um contrato mínimo. Ele tem duas partes.

1. Etiquetas de baixa cardinalidade, iguais nos dois lados. Escolha poucas etiquetas que identificam de onde veio o dado, e use exatamente os mesmos nomes e valores em métricas e logs:

Etiqueta Exemplo Para que serve
service checkout Qual aplicação
env prod Qual ambiente
instance checkout-7f9c Qual réplica ou host

Se a métrica diz service="checkout" e o log diz app="Checkout", a ponte cai. Padronize os nomes uma vez e aplique no coletor, não em cada aplicação.

2. Um trace_id em toda linha de log. Cada requisição recebe um identificador único na entrada do sistema e o propaga para os serviços seguintes. O padrão W3C Trace Context (o cabeçalho traceparent) já resolve isso, e as bibliotecas do OpenTelemetry o propagam automaticamente. A aplicação só precisa escrever esse identificador nos logs, de preferência em JSON:

{"ts":"2026-11-02T03:04:11Z","level":"error","service":"checkout","trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","msg":"payment gateway timeout","duration_ms":5003}

Note a diferença de papel: service é etiqueta (poucos valores), trace_id é dado da linha (um valor por requisição). Essa distinção é o que mantém o Loki rápido, como você vai ver a seguir.

Rastreabilidade com Loki e VictoriaMetrics na prática

A arquitetura fica assim: a aplicação expõe métricas no formato Prometheus e escreve logs em JSON. O Alloy coleta os dois, aplica o contrato de etiquetas e envia métricas para o VictoriaMetrics (via remote write) e logs para o Loki. O Grafana consulta os dois.

Métricas no VictoriaMetrics

O VictoriaMetrics é compatível com o ecossistema Prometheus: aceita remote write, entende PromQL e oferece o MetricsQL, um superconjunto com funções extras. No Alloy, a coleta e o envio ficam assim:

prometheus.scrape "checkout" {
  targets = [{
    "__address__" = "checkout:8080",
    "service"     = "checkout",
    "env"         = "prod",
  }]
  forward_to = [prometheus.remote_write.victoria.receiver]
}

prometheus.remote_write "victoria" {
  endpoint {
    url = "http://victoriametrics:8428/api/v1/write"
  }
}

Com isso, a taxa de erros por serviço é uma consulta direta:

sum by (service) (rate(http_requests_total{env="prod", code=~"5.."}[5m]))

Logs no Loki, com o trace_id no lugar certo

O Loki indexa apenas as etiquetas de cada fluxo de log; o conteúdo da linha não é indexado. Por isso, colocar o trace_id como etiqueta seria um erro clássico: cada requisição criaria um fluxo novo, a cardinalidade explodiria e o Loki ficaria lento e caro.

O lugar certo é o structured metadata, disponível no Loki 3: um campo anexado à linha, filtrável nas consultas, mas fora do índice de fluxos. No Alloy:

loki.process "checkout" {
  stage.json {
    expressions = { trace_id = "trace_id", level = "level" }
  }
  stage.labels {
    values = { level = "" }
  }
  stage.structured_metadata {
    values = { trace_id = "" }
  }
  stage.static_labels {
    values = { service = "checkout", env = "prod" }
  }
  forward_to = [loki.write.default.receiver]
}

loki.write "default" {
  endpoint {
    url = "http://loki:3100/loki/api/v1/push"
  }
}

Repare que service e env têm os mesmos nomes e valores usados nas métricas. É aqui que o contrato vira configuração.

Do pico no gráfico à linha de log

Com tudo no lugar, a investigação do alerta das três da manhã vira um roteiro curto:

  1. No painel de métricas, a consulta de erros mostra o pico em service="checkout" entre 03:02 e 03:07.
  2. No Explore do Grafana, com a fonte Loki e o mesmo intervalo de tempo, você filtra pelas mesmas etiquetas:

logql {service="checkout", env="prod", level="error"} | json

  1. Uma linha chama a atenção: payment gateway timeout. Ela traz o trace_id. Agora você filtra por ele, sem precisar indexá-lo:

logql {env="prod"} | trace_id="4bf92f3577b34da6a3ce929d0e0e4736"

A consulta devolve todas as linhas daquela requisição, em todos os serviços que ela atravessou, em ordem. Essa é a rastreabilidade de fato: a história completa de uma requisição.

  1. De volta às métricas, você confirma se o problema é sistêmico. A latência do gateway subiu para todo mundo ou só para essa requisição?

promql histogram_quantile(0.99, sum by (le) (rate(gateway_request_duration_seconds_bucket{service="checkout"}[5m])))

Para encurtar ainda mais o caminho, configure no Grafana um link de dados nos painéis de métricas que abra o Explore do Loki com as mesmas etiquetas e o mesmo intervalo. E, se você também guarda traces em um backend dedicado, use os derived fields da fonte Loki para transformar o trace_id de cada linha em um link clicável para o trace.

Alertas que já apontam para os logs

O último passo é fazer o alerta carregar a ponte consigo. Com o vmalert (o avaliador de regras do VictoriaMetrics), as anotações da regra podem incluir a consulta de logs já pronta:

groups:
  - name: checkout
    rules:
      - alert: CheckoutErrorRateHigh
        expr: sum(rate(http_requests_total{service="checkout", env="prod", code=~"5.."}[5m])) > 1
        for: 5m
        labels:
          severity: page
        annotations:
          summary: "Erros 5xx elevados no checkout"
          logs: '{service="checkout", env="prod", level="error"} | json'

Quem recebe o alerta já sabe por onde começar, sem montar a consulta na pressa.

Armadilhas comuns

  • Etiquetas divergentes. service de um lado, app do outro. Padronize no coletor e revise quando um serviço novo entrar.
  • trace_id como etiqueta no Loki. Explode a cardinalidade. Use structured metadata ou deixe o campo no JSON e extraia com | json.
  • Logs sem trace_id em parte do caminho. Basta um serviço que não propaga o traceparent para a história ficar pela metade. Trate a propagação como requisito, não como detalhe.
  • Relógios desalinhados. Se os hosts não sincronizam o horário (NTP), a janela de tempo do gráfico não bate com a dos logs.
  • Etiquetas de alta cardinalidade nas métricas. Identificador de usuário ou de pedido em métrica é o mesmo erro do trace_id no Loki, só que no VictoriaMetrics. Esses dados pertencem aos logs.

Próximos passos

Comece pequeno: escolha um serviço crítico, aplique o contrato de etiquetas, garanta o trace_id nos logs e monte o roteiro "do gráfico à linha de log" para um alerta real. Quando ele funcionar de ponta a ponta, replique o padrão para os outros serviços. A ferramenta mais importante dessa rastreabilidade não é o Loki nem o VictoriaMetrics: é o contrato que faz os dois conversarem.

Gostou do artigo?

Compartilhe com seus amigos e ajude a espalhar conhecimento!