Como Nomear Suas Métricas

Métricas são a espinha dorsal quantitativa da observabilidade — os números que nos dizem como nossos sistemas estão se comportando. Esta é a terceira publicação em nossa série sobre nomenclatura do OpenTelemetry, onde já exploramos como nomear trechos e como enriquecê-los com atributos significativos. Agora, vamos abordar a arte de nomear as medições que realmente importam.

Ao contrário dos trechos (spans) que contam histórias sobre o que aconteceu, as métricas nos falam sobre quantidades: quais, quantos e quão rápido. Mas aqui está a questão — nomeá-las bem é tão crucial quanto nomear trechos, e os princípios que aprendemos também se aplicam aqui. O “quem” ainda pertence aos atributos, não aos nomes.

Aprendendo com sistemas tradicionais

Antes de mergulhar nas boas práticas do OpenTelemetry, vamos examinar como sistemas tradicionais de monitoramento lidam com a nomenclatura de métricas. Veja o Kubernetes, por exemplo. Suas métricas seguem padrões como:

  • apiserver_request_total
  • scheduler_schedule_attempts_total
  • container_cpu_usage_seconds_total
  • kubelet_volume_stats_used_bytes

Percebeu o padrão? Nome do componente + recurso + ação + unidade. O nome do serviço ou componente está embutido diretamente no nome da métrica. Essa abordagem fazia sentido em modelos de dados mais simples, onde havia opções limitadas para armazenar contexto.

Mas isso cria vários problemas:

  • Backend de observabilidade desordenado: Cada componente recebe seu próprio namespace de métricas, dificultando encontrar a métrica certa entre dezenas ou centenas de métricas com nomes semelhantes.
  • Agregação inflexível: Não é fácil somar métricas entre diferentes componentes.
  • Dependência de fornecedor: Os nomes das métricas ficam vinculados a implementações específicas.
  • Sobrecarga de manutenção: Adicionar novos serviços requer novos nomes de métricas.

O anti-padrão principal: Nomes de serviços em nomes de métricas

Aqui está o princípio mais importante para métricas no OpenTelemetry: Não inclua o nome do seu serviço no nome da métrica.

Digamos que você tenha um serviço de pagamentos. Pode ser tentador criar métricas como:

  • payment.transaction.count
  • payment.latency.p95
  • payment.error.rate

Não faça isso. O nome do serviço já está disponível como contexto através do atributo de recurso service.name. Em vez disso, use:

  • transaction.count com service.name=payment
  • http.server.request.duration com service.name=payment
  • error.rate com service.name=payment

Por que isso é melhor? Porque agora você pode facilmente agregar entre todos os serviços:

sum(transaction.count)  // Todas as transações em todos os serviços
sum(transaction.count{service.name="payment"})  // Apenas transações de pagamento

Se cada serviço tivesse seu próprio nome de métrica, você precisaria conhecer todos os nomes de serviços para construir dashboards significativos. Com nomes limpos, uma única consulta funciona para tudo.

O modelo de contexto rico do OpenTelemetry

As métricas do OpenTelemetry se beneficiam do mesmo modelo de contexto rico que discutimos em nosso artigo sobre atributos de trechos. Em vez de forçar tudo no nome da métrica, temos múltiplas camadas onde o contexto pode viver:

Abordagem tradicional (estilo Prometheus):

payment_service_transaction_total{method="credit_card",status="success"}
user_service_auth_latency_milliseconds{endpoint="/login",region="us-east"}
inventory_service_db_query_seconds{table="products",operation="select"}

Abordagem OpenTelemetry:

transaction.count
- Recurso: service.name=payment, service.version=1.2.3, deployment.environment.name=prod
- Escopo: instrumentation.library.name=com.acme.payment, instrumentation.library.version=2.1.0
- Atributos: method=credit_card, status=success

auth.duration
- Recurso: service.name=user, service.version=2.0.1, deployment.environment.name=prod
- Escopo: instrumentation.library.name=express.middleware
- Atributos: endpoint=/login, region=us-east
- Unidade: ms

db.client.operation.duration
- Recurso: service.name=inventory, service.version=1.5.2
- Escopo: instrumentation.library.name=postgres.client
- Atributos: db.sql.table=products, db.operation=select
- Unidade: s

Essa separação em três camadas segue o modelo da especificação do OpenTelemetry de Eventos → Fluxos de Métricas → Séries Temporais, onde o contexto flui através de múltiplos níveis hierárquicos em vez de ser amontoado nos nomes.

Unidades: Mantenha-as fora dos nomes também

Assim como aprendemos que nomes de serviços não pertencem a nomes de métricas, as unidades também não pertencem.

Sistemas tradicionais frequentemente incluem unidades no nome porque carecem de metadados de unidade adequados:

  • response_time_milliseconds
  • memory_usage_bytes
  • throughput_requests_per_second

O OpenTelemetry trata unidades como metadados, separados do nome:

  • http.server.request.duration com unidade ms
  • system.memory.usage com unidade By
  • http.server.request.rate com unidade {request}/s

Esta abordagem tem vários benefícios:

  1. Nomes limpos: Sem sufixos poluindo os nomes das suas métricas.
  2. Unidades padronizadas: Seguindo o Código Unificado para Unidades de Medida (UCUM).
  3. Flexibilidade do backend: Os sistemas podem lidar com conversão de unidades automaticamente.
  4. Convenções consistentes: Alinhado às convenções semânticas do OpenTelemetry.

A especificação recomenda usar unidades não prefixadas como By (bytes) em vez de MiBy (mebibytes), a menos que haja razões técnicas para fazer o contrário.

Diretrizes práticas de nomenclatura

Ao criar nomes de métricas, aplique o mesmo princípio {verbo} {objeto} que aprendemos para trechos, quando fizer sentido:

  1. Foque na operação: O que está sendo medido?
  2. Não no operador: Quem está fazendo a medição?
  3. Siga as convenções semânticas: Utilize padrões estabelecidos quando disponíveis.
  4. Mantenha unidades como metadados: Não adicione sufixos de unidades aos nomes.

Aqui estão exemplos seguindo as convenções semânticas do OpenTelemetry:

  • http.server.request.duration (não payment_http_requests_ms)
  • db.client.operation.duration (não user_service_db_queries_seconds)
  • messaging.client.sent.messages (não order_service_messages_sent_total)
  • transaction.count (não payment_transaction_total)

Exemplos reais de migração

Tradicional (Contexto + unidades no nome)OpenTelemetry (Separação limpa)Por que é melhor
payment_transaction_totaltransaction.count + service.name=payment + unidade 1Agregável entre serviços
user_service_auth_latency_msauth.duration + service.name=user + unidade msNome de operação padrão, metadados de unidade adequados
inventory_db_query_secondsdb.client.operation.duration + service.name=inventory + unidade sSegue convenções semânticas
api_gateway_requests_per_secondhttp.server.request.rate + service.name=api-gateway + unidade {request}/sNome limpo, unidade de taxa adequada
redis_cache_hit_ratio_percentcache.hit_ratio + service.name=redis + unidade 1Proporções são adimensionais

Benefícios da nomenclatura limpa

Separar contexto dos nomes de métricas fornece vantagens técnicas específicas que melhoram tanto o desempenho de consultas quanto os fluxos de trabalho operacionais. O primeiro benefício é a agregação entre serviços. Uma consulta como sum(transaction.count) retorna dados de todos os serviços sem exigir que você conheça ou mantenha uma lista de nomes de serviços. Em um sistema com 50 microsserviços, isso significa uma consulta em vez de 50 — e essa consulta não quebra quando você adiciona o 51º serviço.

Essa consistência torna os dashboards reutilizáveis entre serviços. Um dashboard para monitorar requisições HTTP no seu serviço de autenticação funciona sem modificação para o serviço de pagamentos, serviço de inventário, ou qualquer outro componente que serve HTTP. Você escreve a consulta uma vez — http.server.request.duration filtrada por service.name — e aplica em todos os lugares. Não há mais manutenção de dezenas de dashboards quase idênticos. Alguns fornecedores de observabilidade levam isso além, gerando automaticamente dashboards baseados em nomes de métricas de convenções semânticas — quando seus serviços emitem http.server.request.duration, a plataforma já sabe exatamente quais visualizações e agregações fazem sentido para essa métrica.

A nomenclatura limpa também reduz a desordem do namespace de métricas. Considere uma plataforma com dezenas de serviços, cada um definindo suas próprias métricas. Com nomenclatura tradicional, seu navegador de métricas mostra centenas de variações específicas de serviços: apiserver_request_total, payment_service_request_total, user_service_request_total, inventory_service_request_total, e assim por diante. Encontrar a métrica certa se torna um exercício de rolagem e busca entre variações redundantes. Com nomenclatura limpa, você tem um nome de métrica (request.count) com atributos capturando o contexto. Isso torna a descoberta de métricas direta — você encontra a medição que precisa, então filtra pelo serviço que lhe interessa.

O tratamento de unidades torna-se sistemático quando as unidades são metadados em vez de sufixos. Plataformas de observabilidade podem converter unidades automaticamente — exibindo a mesma métrica de duração como milissegundos em um gráfico e segundos em outro, baseado no que faz sentido para a visualização. A métrica permanece request.duration com metadados de unidade ms, não duas métricas separadas request_duration_ms e request_duration_seconds.

A abordagem também garante compatibilidade entre instrumentação manual e automática. Quando você segue convenções semânticas como http.server.request.duration, suas métricas customizadas se alinham com aquelas geradas por bibliotecas de auto-instrumentação. Isso cria um modelo de dados consistente onde consultas funcionam tanto para serviços instrumentados manualmente quanto automaticamente, e engenheiros não precisam lembrar quais métricas vêm de qual fonte.

Armadilhas comuns a evitar

Engenheiros frequentemente incorporam informações específicas de deploy diretamente nos nomes de métricas, criando padrões como user_service_v2_latency. Isso quebra quando a versão 3 é implantada — todos os dashboards, alertas e consultas que referenciam esse nome de métrica devem ser atualizados. O mesmo problema ocorre com nomes específicos de instância, como node_42_memory_usage. Em um cluster com escalonamento dinâmico, você acaba com centenas de nomes distintos de métricas que representam a mesma medição, tornando impossível escrever consultas de agregação simples.

Prefixos específicos de ambiente causam problemas de manutenção similares. Com métricas nomeadas prod_payment_errors e staging_auth_count, você não pode escrever uma única consulta que funcione em todos os ambientes. Um dashboard que monitora produção não pode ser utilizado para staging sem modificação. Quando você precisa comparar métricas entre ambientes — uma tarefa comum de depuração (debugging) — é necessário escrever consultas complexas que referenciam explicitamente os nomes de métricas para cada ambiente.

Detalhes de stack tecnológica nos nomes de métricas criam dores de cabeça para migrações futuras. Uma métrica nomeada nodejs_payment_memory torna-se enganosa quando você reescreve o serviço em Go. Da mesma forma, postgres_user_queries precisa ser renomeada se você migrar para outro banco de dados. Esses nomes específicos de tecnologia também impedem consultas que funcionem em serviços usando diferentes stacks, mesmo quando eles executam a mesma função de negócio.

Misturar domínios de negócio com métricas de infraestrutura viola a separação entre o que um sistema faz e como ele faz. Uma métrica como ecommerce_cpu_usage confunde o propósito de negócio (e-commerce) com a medição técnica (uso de CPU). Isso torna mais difícil reutilizar monitoramento de infraestrutura através de diferentes domínios de negócio e complica deployments multi-tenant onde a mesma infraestrutura serve múltiplas funções.

A prática de incluir unidades nos nomes — latency_ms, memory_bytes, count_total — cria redundância agora que o OpenTelemetry fornece metadados de unidade adequados. Também impede conversão automática de unidades. Com request_duration_ms e request_duration_seconds como métricas separadas, você precisa de consultas diferentes para diferentes escalas de tempo. Com uma única métrica request.duration que inclui metadados de unidade, a plataforma de observabilidade lida com conversão automaticamente.

O padrão é claro: contexto que varia por deployment, instância, ambiente ou versão pertence aos atributos, não ao nome da métrica. O nome da métrica deve identificar o que você está medindo. Todo o resto — quem está medindo, onde está executando, qual versão — vai para a camada de atributos, onde pode ser filtrado, agrupado e agregado conforme necessário.

Cultivando melhores métricas

Assim como os trechos que abordamos anteriormente nesta série, métricas bem nomeadas são um presente para seu eu do futuro e sua equipe. Elas trazem clareza durante incidentes, permitem análises poderosas entre serviços e tornam seus dados de observabilidade realmente úteis - e não apenas volumosos.

O insight-chave é o mesmo que aprendemos com trechos: separação de responsabilidades. O nome da métrica descreve o que você está medindo. O contexto — quem está medindo, onde, quando e como — vive na rica hierarquia de atributos que o OpenTelemetry fornece.

Na próxima publicação, mergulharemos profundamente nos atributos de métricas — a camada de contexto que torna as métricas verdadeiramente poderosas. Vamos explorar como estruturar a informação contextual rica que não pertence aos nomes, e como equilibrar informatividade com preocupações de cardinalidade.

Até lá, lembre-se: um nome de métrica limpo é como um caminho de jardim bem cuidado — ele te leva exatamente onde você precisa ir.