Cómo nombrar tus métricas
Las métricas son la columna vertebral cuantitativa de la observabilidad: los números que nos dicen cómo están funcionando nuestros sistemas. Este es el tercer artículo de nuestra serie sobre nombres en OpenTelemetry, donde ya hemos explorado cómo nombrar spans y cómo enriquecerlos con atributos significativos. Ahora abordemos el arte de nombrar las mediciones que importan.
A diferencia de los spans, que cuentan historias sobre lo que ocurrió, las métricas nos hablan de cantidades: cuántos, qué tan rápido, cuánto. Pero aquí está el detalle: nombrarlas bien es tan crucial como nombrar spans, y los principios que ya aprendimos se aplican aquí también. El “quién” sigue perteneciendo a los atributos, no a los nombres.
Aprendiendo de los sistemas tradicionales
Antes de adentrarnos en las buenas prácticas de OpenTelemetry, veamos cómo los sistemas de monitoreo tradicionales manejan el nombrado de métricas. Tomemos Kubernetes, por ejemplo. Sus métricas siguen patrones como:
apiserver_request_total
scheduler_schedule_attempts_total
container_cpu_usage_seconds_total
kubelet_volume_stats_used_bytes
¿Notas el patrón? Nombre del componente + recurso + acción + unidad. El nombre del servicio o componente está incrustado directamente en el nombre de la métrica. Este enfoque tenía sentido en modelos de datos más simples, donde las opciones para almacenar contexto eran limitadas.
Pero esto genera varios problemas:
- Backend de observabilidad saturado: Cada componente obtiene su propio espacio de nombres de métricas, lo que dificulta encontrar la métrica correcta entre docenas o cientos de nombres similares.
- Agregación inflexible: No se pueden sumar fácilmente métricas entre diferentes componentes.
- Dependencia del proveedor: Los nombres de las métricas quedan ligados a implementaciones específicas.
- Sobrecarga de mantenimiento: Agregar nuevos servicios requiere nuevos nombres de métricas.
El anti-patrón principal: Nombres de servicio en las métricas
Aquí está el principio más importante para las métricas en OpenTelemetry: No incluyas el nombre de tu servicio en el nombre de la métrica.
Supongamos que tienes un servicio de pagos. Podrías sentir la tentación de crear métricas como:
payment.transaction.count
payment.latency.p95
payment.error.rate
No lo hagas. El nombre del servicio ya está disponible como contexto a través
del atributo de recurso service.name
. En su lugar, usa:
transaction.count
conservice.name=payment
http.server.request.duration
conservice.name=payment
error.rate
conservice.name=payment
¿Por qué es mejor? Porque ahora puedes agregar fácilmente entre todos los servicios:
sum(transaction.count) // Todas las transacciones en todos los servicios
sum(transaction.count{service.name="payment"}) // Solo transacciones del servicio de pagos
Si cada servicio tuviera su propio nombre de métrica, necesitarías conocer cada nombre de servicio para construir dashboards significativos. Con nombres limpios, una sola consulta funciona para todo.
El modelo de contexto enriquecido de OpenTelemetry
Las métricas en OpenTelemetry se benefician del mismo modelo de contexto enriquecido que discutimos en nuestro artículo sobre atributos de spans. En lugar de forzar todo en el nombre de la métrica, tenemos múltiples capas donde puede vivir el contexto:
Enfoque 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"}
Enfoque OpenTelemetry:
transaction.count
- Resource: service.name=payment, service.version=1.2.3, deployment.environment.name=prod
- Scope: instrumentation.library.name=com.acme.payment, instrumentation.library.version=2.1.0
- Attributes: method=credit_card, status=success
auth.duration
- Resource: service.name=user, service.version=2.0.1, deployment.environment.name=prod
- Scope: instrumentation.library.name=express.middleware
- Attributes: endpoint=/login, region=us-east
- Unit: ms
db.client.operation.duration
- Resource: service.name=inventory, service.version=1.5.2
- Scope: instrumentation.library.name=postgres.client
- Attributes: db.sql.table=products, db.operation=select
- Unit: s
Esta separación en tres capas sigue el modelo de especificación de OpenTelemetry: Eventos → Flujos de métricas → Series temporales, donde el contexto fluye a través de múltiples niveles jerárquicos en lugar de estar comprimido en los nombres.
Unidades: mantenlas fuera de los nombres también
Al igual que aprendimos que los nombres de servicio no pertenecen a los nombres de métricas, las unidades tampoco pertenecen ahí. Los sistemas tradicionales suelen incluir unidades en el nombre porque carecen de metadatos de unidad adecuados:
response_time_milliseconds
memory_usage_bytes
throughput_requests_per_second
OpenTelemetry trata las unidades como metadatos, separados del nombre:
http.server.request.duration
con unidadms
system.memory.usage
con unidadBy
http.server.request.rate
con unidad{request}/s
Este enfoque ofrece varios beneficios:
- Nombres limpios: Sin sufijos feos que saturen tus nombres de métricas.
- Unidades estandarizadas: Sigue el Código Unificado para Unidades de Medida (UCUM).
- Flexibilidad del backend: Los sistemas pueden manejar la conversión de unidades automáticamente.
- Convenciones consistentes: Se alinea con las convenciones semánticas de OpenTelemetry.
La especificación recomienda usar unidades sin prefijos como By
(bytes) en
lugar de MiBy
(mebibytes), a menos que haya razones técnicas para lo
contrario.
Guías prácticas para nombrar métricas
Al crear nombres de métricas, aplica el mismo principio ‘{verbo} {objeto}’ que aprendimos para los spans, cuando tenga sentido:
- Concéntrate en la operación: ¿Qué se está midiendo?
- No en el operador: ¿Quién está midiendo?
- Sigue las convenciones semánticas: Usa patrones establecidos cuando estén disponibles.
- Mantén las unidades como metadatos: No añadas sufijos de unidades en los nombres.
Aquí algunos ejemplos siguiendo las convenciones semánticas de OpenTelemetry:
http.server.request.duration
(nopayment_http_requests_ms
)db.client.operation.duration
(nouser_service_db_queries_seconds
)messaging.client.sent.messages
(noorder_service_messages_sent_total
)transaction.count
(nopayment_transaction_total
)
Ejemplos reales de migración
Tradicional (Contexto + Unidades en el nombre) | OpenTelemetry (Separación limpia) | Por qué es mejor |
---|---|---|
payment_transaction_total | transaction.count + service.name=payment + unidad 1 | Agregable entre servicios |
user_service_auth_latency_ms | auth.duration + service.name=user + unidad ms | Nombre estándar de operación, unidad correcta |
inventory_db_query_seconds | db.client.operation.duration + service.name=inventory + unidad s | Sigue convenciones semánticas |
api_gateway_requests_per_second | http.server.request.rate + service.name=api-gateway + unidad {request}/s | Nombre limpio, unidad de tasa adecuada |
redis_cache_hit_ratio_percent | cache.hit_ratio + service.name=redis + unidad 1 | Las razones no tienen unidad |
Beneficios de nombres limpios
Separar el contexto de los nombres de métricas proporciona ventajas técnicas que
mejoran tanto el rendimiento de consultas como los flujos operativos. El primer
beneficio es la agregación entre servicios. Una consulta como
sum(transaction.count)
devuelve datos de todos los servicios sin necesidad de
mantener una lista de nombres de servicio. En un sistema con 50 microservicios,
esto significa una sola consulta en lugar de 50, y esa consulta no se rompe
cuando agregas el servicio número 51.
Esta consistencia hace que los dashboards sean reutilizables entre servicios. Un
dashboard creado para monitorear peticiones HTTP en tu servicio de autenticación
funciona sin modificaciones para tu servicio de pagos, inventario o cualquier
otro componente HTTP. Escribes la consulta una vez —
http.server.request.duration
filtrado por service.name
— y la aplicas en
todas partes. Ya no necesitas mantener docenas de dashboards casi idénticos.
Algunos proveedores de observabilidad llevan esto aún más lejos, generando
dashboards automáticamente basados en los nombres de métricas de las
convenciones semánticas: cuando tus servicios emiten
http.server.request.duration
, la plataforma sabe exactamente qué
visualizaciones y agregaciones tienen sentido.
Los nombres limpios también reducen el desorden en los espacios de nombres de
métricas. Con docenas de servicios, el enfoque tradicional produce cientos de
variaciones como apiserver_request_total
, payment_service_request_total
,
user_service_request_total
, etc. Encontrar la métrica correcta se vuelve un
ejercicio de búsqueda tediosa. Con nombres limpios, tienes una sola métrica
(request.count
) con atributos para capturar el contexto. Esto simplifica la
descubribilidad.
El manejo de unidades también se vuelve sistemático cuando las unidades son
metadatos. Las plataformas pueden convertir unidades automáticamente — mostrando
una métrica de duración en milisegundos en un gráfico y en segundos en otro. La
métrica sigue siendo request.duration
con unidad ms
, no dos métricas
distintas.
El enfoque garantiza compatibilidad entre instrumentación manual y automática.
Cuando sigues convenciones semánticas como http.server.request.duration
, tus
métricas personalizadas se alinean con las generadas por librerías de
autoinstrumentación. Esto crea un modelo de datos coherente donde las consultas
funcionan en ambos casos.
Errores comunes a evitar
Los ingenieros suelen incrustar información de despliegue en los nombres de
métricas, como user_service_v2_latency
. Esto se rompe cuando se despliega la
versión 3. Lo mismo ocurre con nombres específicos de instancia como
node_42_memory_usage
. Con escalado dinámico, terminas con cientos de métricas
distintas que representan la misma medición.
Prefijos específicos de entorno como prod_payment_errors
y
staging_auth_count
causan problemas similares: no puedes escribir una consulta
única que funcione en todos los entornos. Lo mismo ocurre al incluir detalles de
tecnología en los nombres, como nodejs_payment_memory
, que deja de tener
sentido si migras el servicio a Go.
El patrón es claro: el contexto que varía por despliegue, instancia, entorno o versión pertenece a los atributos, no al nombre de la métrica. El nombre debe identificar qué estás midiendo. Todo lo demás vive en la capa de atributos.
Cultivando mejores métricas
Al igual que los spans, las métricas bien nombradas son un regalo para tu futuro tú y tu equipo. Proporcionan claridad durante incidentes, habilitan análisis poderoso entre servicios y hacen que tus datos de observabilidad sean realmente útiles.
La clave es la misma que aprendimos con los spans: separación de responsabilidades. El nombre describe lo que mides. El contexto —quién lo mide, dónde, cuándo y cómo— vive en la jerarquía de atributos de OpenTelemetry.
En nuestro próximo artículo, profundizaremos en atributos de métricas: la capa de contexto que hace realmente poderosas a las métricas. Exploraremos cómo estructurar la información contextual y cómo equilibrar la riqueza de datos con las preocupaciones de cardinalidad.
Hasta entonces, recuerda: un nombre de métrica limpio es como un sendero de jardín bien cuidado: te lleva exactamente a donde necesitas ir.