# Як називати ваші метрики

LLMS index: [llms.txt](/llms.txt)

---

Метрики — кількісна основа спостережуваності, числа, що показують, як працюють наші системи. Це третя публікація в нашій серії про іменування в OpenTelemetry; раніше ми вже розглядали [як називати відрізки](/blog/2025/how-to-name-your-spans/) та [як збагачувати їх корисними атрибутами](/blog/2025/how-to-name-your-span-attributes/). Тепер розглянемо мистецтво називати вимірювання, які мають значення.

На відміну від відрізків, що розповідають історії про те, що сталося, метрики говорять про кількість: скільки, як швидко, наскільки. Але правильно їх називати так само важливо, як і відрізки, і принципи, які ми вивчили, також застосовні тут. "Хто" має знаходитись в атрибутах, а не в назвах.

## Приклад традиційних систем {#learning-from-traditional-systems}

Перш ніж заглиблюватись у практики OpenTelemetry, погляньмо, як традиційні системи обробляють іменування метрик. Наприклад, Kubernetes використовує патерни на кшталт:

- `apiserver_request_total`
- `scheduler_schedule_attempts_total`
- `container_cpu_usage_seconds_total`
- `kubelet_volume_stats_used_bytes`

Помітили патерн? Компонент + ресурс + дія + одиниця виміру. Назва сервісу знаходиться прямо в імені метрики. Такий підхід мав сенс у простіших моделях даних, де контекст зберігався обмежено.

Але це створює кілька проблем:

- **Захаращення бекенду спостережуваності**: кожен компонент отримує власний простір імен метрик, що ускладнює пошук потрібної метрики серед десятків або сотень метрик із подібними назвами.
- **Негнучка агрегація**: важко підсумовувати метрики для різних компонентів.
- **Привʼязування до постачальника**: імена метрик привʼязані до конкретної реалізації.
- **Витрати на підтримку**: додавання нових сервісів вимагає нових імен метрик.

## Основний антипатерн: імена сервісів у назвах метрик {#the-core-anti-pattern-service-names-in-metric-names}

Найважливіший принцип для метрик OpenTelemetry: **не додавайте імʼя сервісу в назву метрики**.

Припустимо, у вас є сервіс платежів. Можна спокуситися створити метрики:

- `payment.transaction.count`
- `payment.latency.p95`
- `payment.error.rate`

Не робіть цього. Імʼя сервісу вже є в ресурсному атрибуті `service.name`. Натомість використовуйте:

- `transaction.count` з `service.name=payment`
- `http.server.request.duration` з `service.name=payment`
- `error.rate` з `service.name=payment`

Чому це краще? Тому що тепер легко виконувати агрегацію по всіх сервісах:

```promql
sum(transaction.count)  // Всі транзакції по всіх сервісах
sum(transaction.count{service.name="payment"})  // Тільки транзакції сервісу payment
```

Якщо б кожен сервіс мав власне імʼя метрики, довелося б знати всі імена сервісів, щоб будувати корисні дашборди. З чистими іменами один запит працює для всього.

## Повноконтекстна модель OpenTelemetry {#opentelemetrys-rich-context-model}

Метрики OpenTelemetry виграють від тієї самої
[повноконтекстної моделі](/docs/specs/otel/common/#attribute), про яку ми говорили в статті про атрибути відрізків. Замість того, щоб упаковувати все в імʼя метрики, у нас є кілька шарів, де може існувати контекст:

### Традиційний підхід (стиль Prometheus): {#traditional-approach-prometheus-style}

```promql
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"}
```

### Підхід OpenTelemetry: {#opentelemetry-approach}

```yaml
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
```

Ця трирівнева сепарація відповідає специфікації OpenTelemetry — модель Events → Metric Streams → Timeseries, де контекст проходить через кілька ієрархічних рівнів, замість того щоб бути запханим у назви.

## Одиниці виміру: теж не місце в іменах {#units-keep-them-out-of-names-too}

Як ми вже зʼясували з імен відрізків, назва сервісу не має бути в імені метрики, так само й одиниці виміру не повинні бути в імені.

Традиційні системи часто додають одиниці в назву через відсутність метаданих щодо одиниць виміру:

- `response_time_milliseconds`
- `memory_usage_bytes`
- `throughput_requests_per_second`

OpenTelemetry трактує одиниці виміру як метадані, окремі від назви:

- `http.server.request.duration` з одиницею виміру `ms`
- `system.memory.usage` з одиницею виміру`By`
- `http.server.request.rate` з одиницею виміру `{request}/s`

Цей підхід має кілька переваг:

1. **Чисті імена**: немає неприємних суфіксів у назвах.
2. **Стандартизовані одиниці виміру**: дотримуйтеся [Unified Code for Units of Measure (UCUM)](/docs/specs/semconv/general/metrics/#instrument-units).
3. **Гнучкість бекенду**: системи можуть автоматично конвертувати одиниці.
4. **Узгоджені домовленості**: відповідає [семантичним домовленостям](/docs/specs/semconv/general/metrics/) OpenTelemetry.

Специфікація рекомендує використовувати одиниці виміру без префіксів, такі як `By` (байти), замість `MiBy` (мебібайти), якщо немає технічних причин робити інакше.

## Практичні поради з іменування {#practical-naming-guidelines}

При створенні імен метрик застосовуйте принцип `{дія} {обʼєкт}`, схожий на той, що ми використовували для відрізків, де це доречно:

1. **Зосередьтеся на операції**: що вимірюється?
2. **Не на тому, хто це робить**: хто виконує вимірювання — вже є в атрибутах.
3. **Дотримуйтесь семантичних домовленостей**: використовуйте [встановлені патерни](/docs/specs/semconv/general/metrics/), коли вони доступні.
4. **Тримайте одиниці як метадані**: не додавайте суфікси з одиницями.

Ось приклади згідно з [семантичними домовленостями](/docs/specs/semconv/general/metrics/) OpenTelemetry:

- `http.server.request.duration` (не `payment_http_requests_ms`)
- `db.client.operation.duration` (не `user_service_db_queries_seconds`)
- `messaging.client.sent.messages` (не `order_service_messages_sent_total`)
- `transaction.count` (не `payment_transaction_total`)

## Приклади міграції в реальному світі {#real-world-migration-examples}

| Традиційно (контекст + одиниці в імені) | OpenTelemetry (чисте розділення)                                             | Чому це краще                                        |
| :-------------------------------------- | :--------------------------------------------------------------------------- | :--------------------------------------------------- |
| `payment_transaction_total`             | `transaction.count` + `service.name=payment` + unit `1`                      | Агрегується між сервісами                            |
| `user_service_auth_latency_ms`          | `auth.duration` + `service.name=user` + unit `ms`                            | Стандартна назва операції, правильно вказана одиниця |
| `inventory_db_query_seconds`            | `db.client.operation.duration` + `service.name=inventory` + unit `s`         | Відповідає семантичним домовленостям                 |
| `api_gateway_requests_per_second`       | `http.server.request.rate` + `service.name=api-gateway` + unit `{request}/s` | Чиста назва, правильна одиниця для rate              |
| `redis_cache_hit_ratio_percent`         | `cache.hit_ratio` + `service.name=redis` + unit `1`                          | Співвідношення без одиниць виміру                    |

## Переваги чистого іменування {#benefits-of-clean-naming}

Розділення контексту та назв метрик дає конкретні технічні переваги, що покращують як продуктивність запитів, так і операційні процеси. Перша перевага — агрегація між сервісами. Запит на кшталт `sum(transaction.count)` повертає дані зі всіх сервісів без потреби знати чи підтримувати список імен сервісів. У системі з 50 мікросервісами це означає один запит замість 50, і цей запит не зламається, коли зʼявиться 51-й сервіс.

Така послідовність дозволяє використовувати дашборди повторно для різних сервісів. Дашборд для моніторингу HTTP-запитів у службі автентифікації працюватиме без змін для сервісу платежів, інвентаризації чи будь-якого іншого компонента, що обслуговує HTTP. Ви пишете запит один раз, `http.server.request.duration`, фільтруєте за `service.name`, і застосовуєте його скрізь. Більше не потрібно підтримувати десятки майже однакових дашбордів. Деякі платформи спостережуваності йдуть ще далі й генерують дашборди автоматично на основі семантичних імен метрик, коли ваші сервіси надсилають `http.server.request.duration`, платформа знає, які візуалізації й агрегування мають сенс.

Чисте іменування також зменшує захаращення простору імен метрик. Уявіть платформу з десятками сервісів, кожен визначає власні метрики. З традиційним іменуванням в оглядачі метрик ви побачите сотні варіацій: `apiserver_request_total`, `payment_service_request_total`, `user_service_request_total`, `inventory_service_request_total` тощо. З чистими іменами у вас одна назва (`request.count`) із атрибутами, що описують контекст. Це спрощує пошук — знаходите вимірювання, а потім фільтруєте за сервісом.

Обробка одиниць вимірювання стає систематичною, якщо одиниці — метадані, а не суфікси в назві. Платформи можуть виконувати конвертацію одиниць автоматично — відображати ту саму метрику тривалості як мілісекунди в одному графіку і як секунди в іншому, залежно від необхідності. Метрика залишається `request.duration` з одиницею `ms`, а не двома окремими метриками `request_duration_ms` і `request_duration_seconds`.

Такий підхід також забезпечує сумісність між ручною та автоматичною інструменталізацією. Коли ви дотримуєтесь семантичних домовленостей, наприклад `http.server.request.duration`, ваші власні метрики узгоджуються з тими, що генеруються автоінструментуванням. Це створює єдину модель даних, де запити працюють як для ручної, так і для автоматичної інструменталізації, і інженерам не потрібно памʼятати, звідки походить метрика.

## Поширені помилки, яких слід уникати {#common-pitfalls-to-avoid}

Інженери часто включають у назви метрик розгортання чи інші специфічні дані, створюючи патерни на кшталт `user_service_v2_latency`. Це ламає все, коли зʼявляється версія 3 — доводиться змінювати кожен дашборд, алерт і запит, що посилаються на цю метрику. Та сама проблема з іменами на основі екземпляру, наприклад `node_42_memory_usage`. У кластері з динамічним масштабуванням ви отримаєте сотні різних імен метрик, що представляють одне й те саме вимірювання, і написати просту агрегацію стане неможливо.

Префікси, що вказують на середовище (`prod_`, `staging_`), створюють аналогічні проблеми. Метрики `prod_payment_errors` і `staging_auth_count` не дозволяють написати універсальний запит, що працює для всіх середовищ. Порівнювати метрики між середовищами, поширене завдання при налагодженні, і з такими назвами доведеться писати складні запити, що явно перелічують кожну назву метрики для кожного середовища.

Деталі стека технологій у назві метрики створюють проблеми при міграції. Метрика `nodejs_payment_memory` стає такою, що вводить в оману після переписування сервісу на Go. Аналогічно, `postgres_user_queries` потребуватиме перейменування при переході на іншу базу даних. Технологічні суфікси також перешкоджають написанню запитів, що працюють між сервісами, які виконують однакові бізнес-функції, але реалізовані різними технологіями.

Змішування бізнес-домену з інфраструктурними метриками порушує розділення обовʼязків. Метрика `ecommerce_cpu_usage` змішує бізнес-мету (електронна комерція) з технічним виміром (CPU). Це ускладнює повторне використання моніторингу інфраструктури між різними бізнес-напрямами і створює проблеми в багатокористувацьких середовищах.

Практика додавання одиниць вимірювання у назви метрик, `latency_ms`, `memory_bytes`, `count_total`, зараз є надлишковою, бо OpenTelemetry надає метадані одиниць виміру. Вона також заважає автоматичній конвертації одиниць вимірювання. Замість двох метрик `request_duration_ms` і `request_duration_seconds` краще мати одну `request.duration` з одиницею `ms`, а платформа вже покаже її як потрібно.

Патерн зрозумілий: контекст, що змінюється залежно від розгортання, екземпляру, середовища чи версії, має належати атрибутам, а не імені метрики. Назва метрики повинна ідентифікувати те, що вимірюється. Усе інше, хто вимірює, де, коли і як, живе в шарі атрибутів OpenTelemetry, де його можна фільтрувати, групувати та агрегувати.

## Створення кращих метрик {#cultivating-better-metrics}

Як і відрізки з нашої серії, правильно названі метрики — це подарунок вашому майбутньому «я» та команді. Вони дають зрозумілість під час інцидентів, дозволяють виконувати потужний крос-сервісний аналіз і роблять дані спостережуваності дійсно корисними, а не просто численними.

Ключова ідея та сама, що й для відрізків: розділення обовʼязків. Назва метрики описує, що ви вимірюєте. Контекст, хто вимірює, де, коли й як, знаходиться у повноцінному шарі атрибутів OpenTelemetry.

У наступному дописі ми детально розглянемо **атрибути метрик** — шар контексту, що робить метрики по-справжньому потужними. Ми поговоримо, як структурувати контекст, який не належить до імен, і як балансувати інформативність та проблеми кардинальності.

А поки памʼятайте: чисте імʼя метрики — як доглянута стежка в саду, веде вас саме туди, куди потрібно.
