PromQL na prática: guia de queries para Prometheus

Se você já olhou para uma query de PromQL aninhando agregações e funções de histograma e teve a sensação de estar lendo grego, este guia é para você. PromQL é a linguagem que transforma o Prometheus de um coletor de métricas em um motor de observabilidade capaz de responder perguntas de negócio em segundos.

O problema é que a maior parte da documentação brasileira sobre o tema é superficial ou genérica demais. Já os guias internacionais pulam nuances que fazem diferença no dia a dia de quem opera infraestrutura crítica. Aqui, vamos fechar essa lacuna com um material de referência que serve tanto para quem está começando quanto para quem já escreve alertas em produção.

Ao final do artigo você terá domínio dos quatro tipos de dados da linguagem, dos operadores de label, das funções essenciais, dos joins entre vetores e verá mais de quinze queries reais prontas para colar em dashboards Grafana ou regras de alerta do Alertmanager.

O que é PromQL?

PromQL, sigla de Prometheus Query Language, é a linguagem funcional de consulta criada pelo projeto Prometheus para selecionar, agregar e transformar séries temporais em tempo real. Ela nasceu em 2012 junto com o próprio Prometheus, hoje um projeto graduado da Cloud Native Computing Foundation.

Quem vem do mundo de banco de dados relacional tende a procurar similaridades com SQL, mas o paralelo é enganoso. SQL opera sobre tabelas com linhas estáticas. PromQL opera sobre séries temporais multidimensionais, onde cada ponto carrega um timestamp, um valor numérico e um conjunto arbitrário de labels que identificam de qual instância, serviço ou região aquele dado veio.

Essa estrutura multidimensional é o que permite, em uma única expressão, filtrar todas as requisições HTTP de um ambiente de produção, agrupar por endpoint e calcular o percentil 99 da latência dos últimos cinco minutos. Em SQL, isso seria dezenas de linhas. Em PromQL, cabe em uma linha só.

O contexto de uso também é diferente. PromQL é normalmente executada em três lugares: no próprio expression browser do Prometheus (http://<host>:9090/graph), dentro de painéis do Grafana como linguagem de data source e nas regras de alerta do Alertmanager. Entender essa tripla atende a maior parte da demanda operacional.

Como o Prometheus armazena métricas: a base para entender PromQL

Antes de escrever queries, vale um minuto para entender o modelo de dados. O Prometheus armazena cada métrica como uma série temporal única identificada pela combinação do nome e dos labels.

A série http_requests_total{method="GET", status="200", instance="api-01"} é diferente da série http_requests_total{method="GET", status="500", instance="api-01"} apesar de compartilharem o mesmo nome.

Cada série guarda uma sequência de amostras (timestamp + valor). O Prometheus identifica quatro tipos de métrica: counter (contador monotonicamente crescente, como total de requisições), gauge (valor que sobe e desce, como uso de memória), histogram (distribuição de observações em buckets) e summary (quantis pré-calculados no cliente).

Saber qual tipo você está consultando é crítico porque dita qual função PromQL faz sentido aplicar. Você nunca aplica rate() em um gauge, nem tenta fazer histogram_quantile() em um counter simples. Errar essa correspondência é uma das principais fontes de queries aparentemente “corretas” que retornam dados sem sentido.

Tipos de dados em PromQL: instant vector, range vector, scalar e string

PromQL tem exatamente quatro tipos de dados. Entender cada um destrava o resto da linguagem.

Um instant vector é um conjunto de séries temporais onde cada série contribui com uma única amostra, referente ao timestamp em que a query foi avaliada. É o tipo retornado quando você escreve apenas o nome da métrica: node_memory_MemAvailable_bytes. É o formato esperado pela maior parte dos painéis de Grafana.

Um range vector é um conjunto de séries onde cada série carrega várias amostras, cobrindo um intervalo de tempo. Escreve-se com colchetes logo depois do seletor: http_requests_total[5m]. Range vectors não podem ser plotados diretamente, pois representam uma janela histórica, mas são a entrada obrigatória de funções como rate() e increase().

Um scalar é um número simples em ponto flutuante, sem labels e sem variação no tempo. Aparece em expressões como 3.1415 ou como resultado de operações que reduzem o vetor a um valor único.

Um string é um valor textual literal, raramente usado em queries do dia a dia e mais comum em contextos internos de desenvolvimento da linguagem.

A regra mental é simples: painéis de dashboard esperam instant vectors; transformações com funções de taxa e incremento exigem range vectors como entrada; e o resultado dessas funções volta a ser um instant vector pronto para plotar.

Seletores de métrica e label matchers

Uma vez entendidos os tipos de dados, a próxima peça é o filtro. PromQL oferece quatro operadores para casar labels dentro das chaves do seletor.

O operador = exige igualdade exata: {status="500"} retorna apenas séries com status igual a 500. O operador != exclui: {status!="200"} retorna tudo menos o status 200.

O operador =~ faz casamento com expressão regular: {path=~"/api/v1/.*"} retorna todas as séries cujo path começa com /api/v1/. E o operador !~ é a negação do regex: {instance!~"prod-.*"} exclui todas as instâncias de produção.

Os label matchers podem ser combinados: http_requests_total{method="POST", status=~"5..", instance!="canary-01"}. Todos os filtros são aplicados com lógica AND. Uma ressalva importante vem da documentação oficial do Prometheus: pelo menos um matcher precisa casar com algo diferente de string vazia, caso contrário a query é rejeitada.

Funções essenciais: rate, irate, increase e quando usar cada uma

As três funções que mais aparecem em código de produção também são as que geram mais confusão. Todas operam sobre counters e exigem range vectors como entrada.

rate() calcula a taxa média de crescimento por segundo do counter ao longo da janela especificada. Para monitorar requisições por segundo da sua API, use rate(http_requests_total[5m]). A função lida com resets do counter automaticamente (quando a aplicação reinicia, o valor volta a zero) e é a escolha certa para dashboards e alertas na maior parte dos casos.

irate() calcula a taxa considerando apenas os dois últimos pontos da janela. É mais reativo e mostra picos bruscos que o rate() suaviza. Use para debugging em tempo real, nunca para alertas, pois a volatilidade gera falsos positivos.

increase() retorna o aumento absoluto do counter na janela. É equivalente a rate() × duração. Usar increase(http_errors_total[1h]) responde “quantos erros aconteceram na última hora”.

A regra prática: rate() para dashboards e alertas, irate() para investigação pontual, increase() quando a pergunta é contagem absoluta em um período.

Agregações com sum, avg, topk e o poder do by/without

Aqui mora boa parte do valor operacional da linguagem. Sem agregação, você recebe uma série por instância, por pod, por endpoint, o que rapidamente polui um dashboard.

Os operadores principais são sum, avg, max, min, count, topk, bottomk, stddev e stdvar. Todos aceitam dois modificadores de agrupamento: by especifica quais labels manter no resultado, without especifica quais descartar.

Exemplo clássico: total de requisições por status HTTP somando todas as instâncias.




query.promql
sum by (status) (
  rate(http_requests_total[5m])
)

O mesmo resultado com without funciona se você prefere listar os labels a descartar:




query.promql
sum without (instance, pod, container) (
  rate(http_requests_total[5m])
)

Para encontrar os cinco pods que mais consomem CPU em um cluster:




query.promql
topk(5,
  sum by (pod) (
    rate(container_cpu_usage_seconds_total[5m])
  )
)

A dica mental: escolha by quando souber exatamente que labels você quer ver no dashboard; escolha without quando quiser remover labels de alta cardinalidade (como instance ou pod) sem listar todos os outros. Essa diferença salva tempo quando as métricas de TI têm dezenas de labels.

Histogramas e histogram_quantile: medindo latência p95 e p99

Medir latência corretamente é onde PromQL realmente brilha. Histogramas do Prometheus armazenam a distribuição das observações em buckets. A função histogram_quantile() estima o percentil desejado a partir desses buckets.

A query de referência para p99 de latência HTTP:




dashboard.promql
histogram_quantile(0.99,
  sum by (le) (
    rate(http_request_duration_seconds_bucket[5m])
  )
)

Três pontos críticos. Primeiro, o label le (“less or equal”) precisa estar preservado na agregação: é ele que identifica cada bucket. Segundo, a função rate() é aplicada sobre os buckets antes da agregação. Terceiro, o resultado é uma estimativa, não um valor exato, pois depende da granularidade dos buckets definida no cliente.

Para separar latência por endpoint:




dashboard.promql
histogram_quantile(0.95,
  sum by (le, path) (
    rate(http_request_duration_seconds_bucket[5m])
  )
)

Aplicações sérias definem ao menos três percentis em seus dashboards: p50 (mediana, mostra usuário típico), p95 (usuário afetado) e p99 (pior caso que ainda importa). Monitorar apenas a média é a forma mais rápida de esconder problemas reais, porque a média absorve outliers e raramente dispara alertas, como explica a literatura clássica de engenharia de confiabilidade do Google.

Joins e vector matching: on, ignoring, group_left e group_right

Quando você precisa combinar duas métricas diferentes, entra o conceito de vector matching. Operações aritméticas entre vetores exigem que os labels casem. Para controlar esse casamento, PromQL oferece duas cláusulas específicas.

on(labels) restringe o casamento aos labels listados. ignoring(labels) ignora os labels listados durante o casamento. Exemplo: calcular porcentagem de erros HTTP por instância.




query.promql
sum by (instance) (rate(http_errors_total[5m]))
  /
sum by (instance) (rate(http_requests_total[5m]))

Funciona se os dois lados têm exatamente os mesmos labels. Quando um lado tem labels adicionais (casamento many-to-one), entram group_left e group_right. Eles indicam qual lado é “muitos” e qual é “um” no join.




query.promql
rate(container_cpu_usage_seconds_total[5m])
  * on(container, pod) group_left(node)
kube_pod_info

A expressão acima enriquece a métrica de CPU por container com o label node da métrica kube_pod_info. É prática comum em monitoramento de Kubernetes, onde informações sobre pods vivem em métricas separadas. Joins parecem intimidantes no começo, mas depois de três ou quatro queries em produção o padrão fica automático.

Queries prontas para os 4 sinais de ouro do SRE

Os 4 sinais de ouro do SRE (latência, tráfego, erros e saturação) dão um mapa direto de aplicação prática da linguagem. Aqui estão queries testadas para cada um deles.

Latência (Latency)

P99 de latência de API, por endpoint:




dashboard.promql
histogram_quantile(0.99,
  sum by (le, path) (
    rate(http_request_duration_seconds_bucket[5m])
  )
)

P95 de latência ignorando endpoints de healthcheck:




dashboard.promql
histogram_quantile(0.95,
  sum by (le) (
    rate(http_request_duration_seconds_bucket{path!~"/health.*|/ready.*"}[5m])
  )
)

Tráfego (Traffic)

Requisições por segundo por serviço:




dashboard.promql
sum by (service) (
  rate(http_requests_total[1m])
)

Taxa de transações por método HTTP:




dashboard.promql
sum by (method) (
  rate(http_requests_total[5m])
)

Erros (Errors)

Taxa de erros 5xx (como fração do total, ideal para SLO):




alert.promql
sum(rate(http_requests_total{status=~"5.."}[5m]))
  /
sum(rate(http_requests_total[5m]))

Total de erros acumulados na última hora por serviço:




alert.promql
sum by (service) (
  increase(http_requests_total{status=~"5.."}[1h])
)

Saturação (Saturation)

Uso percentual de CPU por nó:




slo.promql
100 - (
  avg by (instance) (
    rate(node_cpu_seconds_total{mode="idle"}[5m])
  ) * 100
)

Pressão de memória (percentual usado):




slo.promql
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)
  / node_memory_MemTotal_bytes
  * 100

Essas oito queries cobrem mais de 80% da necessidade operacional de um time que monitora APIs e infraestrutura Linux. Cole-as em painéis do Grafana ou em regras de alertas de TI e adapte os nomes de métrica ao seu stack.

Armadilhas comuns e boas práticas de performance

Três problemas aparecem recorrentemente em queries de produção e raramente são bem tratados pela documentação.

A primeira armadilha é alta cardinalidade. Cada combinação única de labels cria uma série. Quando você adiciona um label com valores ilimitados (user ID, request ID, URL completa), o Prometheus explode em consumo de memória e as queries ficam lentas. A regra: labels devem ter cardinalidade bounded e baixa. Status HTTP, método, endpoint normalizado sim. User ID, UUID, timestamp não.

A segunda armadilha é o alcance pequeno demais em rate(). Se o range vector cobre menos que quatro vezes o intervalo de scrape, você pode pegar zero ou um ponto. O resultado vira NaN. Regra prática: use rate()[5m] para scrape intervals de até 1 minuto. Aumente proporcionalmente para scrapes mais lentos.

A terceira armadilha é aplicar rate() depois de sum(). Em séries com reset de counter, a soma pode incluir momentos do reset e o rate resultante fica distorcido. A ordem correta é sempre sum(rate(...)), nunca rate(sum(...)).

Duas recomendações adicionais de performance: prefira janelas de range vector menores quando a granularidade de scrape permite (menos pontos processados). Evite também regex quando igualdade direta basta. Por exemplo, {path="/api/v1/users"} é mais rápido que {path=~"/api/v1/users"}. Em clusters grandes, essas otimizações derrubam o tempo de query de segundos para milissegundos, o que importa quando o painel recarrega a cada 15 segundos.

Vale ainda observar que o manual de SRE publicado pelo Google recomenda construir alertas sobre taxas e janelas longas em vez de valores instantâneos, justamente para reduzir ruído. Essa filosofia se traduz em PromQL como preferência por rate() e increase() em detrimento de leituras cruas de counters.

Observabilidade & OpenTelemetry

Logs, métricas e traces unificados para diagnóstico em profundidade.

Instrumentamos aplicações corporativas com OpenTelemetry para correlacionar eventos e acelerar a análise de causa raiz em produção.

Fale com um Especialista →

Conclusão

PromQL é uma linguagem pequena em superfície e profunda em possibilidades. Dominar os quatro tipos de dados, os quatro operadores de label, as três funções de taxa e o padrão sum by / histogram_quantile cobre a grande maioria das queries que você vai escrever no primeiro ano operando Prometheus.

O passo seguinte é praticar com dados reais. Monte um ambiente de teste, colete métricas do seu próprio stack, escreva queries para os golden signals e itere. A fluência vem do uso, não da leitura. Cada armadilha que você tropeça vira intuição na próxima vez.

Para quem está estruturando uma prática de observabilidade de ponta a ponta (não só coleta de métricas, mas também traces, logs e alertas conectados entre si), PromQL é apenas uma das peças. Desenhar a arquitetura completa, escolher retention policies, integrar com incident response e treinar o time exige experiência acumulada.

Se sua empresa precisa acelerar essa jornada com um parceiro que já implantou consultoria em observabilidade em ambientes corporativos complexos, fale com um especialista da OpServices e construa a base certa antes de escalar.

Perguntas Frequentes

O que é PromQL?
PromQL é a linguagem funcional de consulta do Prometheus, criada para selecionar e agregar séries temporais em tempo real. Diferente de SQL, ela opera sobre dados multidimensionais (métrica + labels + valor + timestamp) e é usada em dashboards Grafana, no expression browser do Prometheus e em regras de alerta. Sua sintaxe é compacta: uma linha pode filtrar, agregar e calcular percentis sobre milhões de pontos.
Qual a diferença entre rate e irate?
rate() calcula a taxa média de crescimento por segundo ao longo de toda a janela especificada, suavizando picos. É a escolha certa para dashboards e alertas. irate() considera apenas os dois últimos pontos da janela e mostra variações instantâneas, útil para debugging em tempo real. Use rate() em 95% dos casos; reserve irate() para investigação pontual, pois sua volatilidade gera falsos positivos em regras de alerta.
Como funciona o histogram_quantile em PromQL?
histogram_quantile estima um percentil (ex.: 0.99 para p99) a partir dos buckets de um histograma. A query típica é histogram_quantile(0.99, sum by (le) (rate(metric_bucket[5m]))). O label le precisa estar preservado na agregação. O rate é aplicado aos buckets antes do sum. O resultado é uma aproximação: quanto mais granular a definição dos buckets no cliente, mais precisa a estimativa.
Qual a diferença entre instant vector e range vector?
Um instant vector retorna uma única amostra por série, referente ao momento de avaliação da query. É o tipo esperado por painéis de dashboard. Um range vector retorna várias amostras por série, cobrindo um intervalo de tempo (ex.: [5m]). Range vectors não podem ser plotados diretamente, mas são a entrada obrigatória de funções como rate(), irate() e increase(), que transformam o histórico em um instant vector plotável.
Como evitar alta cardinalidade em queries PromQL?
Evite usar labels com valores ilimitados (user ID, request ID, URL completa com query string). Cada combinação única de labels cria uma série nova e explode o consumo de memória do Prometheus. Mantenha labels com cardinalidade bounded: status HTTP, método, endpoint normalizado. No design das métricas, aplique regras de agregação no lado do cliente e use relabeling para descartar dimensões desnecessárias antes da ingestão. Monitore a métrica prometheus_tsdb_symbol_table_size_bytes para detectar crescimento anômalo.

Trabalho há mais de 15 anos no mercado B2B de tecnologia e hoje atuo como Gerente de Marketing da OpServices e Líder em Projetos de Governança para Inteligência Artificial.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

plugins premium WordPress