Свои метрики (Prometheus и JSON)

Свои метрики — это числовые показатели приложения, которых не видно по контейнерам, трассам и ошибкам: длина очередей, упавшие фоновые задачи, занятые соединения пула, заказы и платежи. Агент стека сам забирает их по HTTP с эндпоинта вашего приложения раз в интервал. Подходит любой клиент Prometheus или простой JSON — отдельный SDK Наблюдателя не нужен.

По метрикам строятся графики на вкладке «Метрики» стека и правила уведомлений: очередь выросла, воркер перестал выполнять задачи, заказов заметно меньше обычного.

Что стоит собирать

Что есть в проекте Что собирать Зачем
Очереди и брокеры: RabbitMQ, Kafka, Redis-очереди, BullMQ, Celery, Sidekiq, Hangfire сообщений ждёт, в работе, упавших; возраст самого старого сообщения; отставание консьюмера очередь растёт — обработчики не успевают или стоят
Фоновые задачи, cron, воркеры выполнено и упало (counter), время последнего успешного запуска задача тихо перестала выполняться
Пулы соединений (БД, HTTP-клиенты) занято, свободно, ждут соединения приложение упирается в пул, а не в базу
Кэш попадания и промахи, размер кэш перестал работать после релиза
Внешние API остаток квоты, ошибки по партнёру лимит кончится раньше, чем упадут запросы
Бизнес-показатели заказы, регистрации, платежи, активные сессии «всё зелёное», а заказов нет

Время ответа и ошибки HTTP-эндпоинтов уже есть в трассах, падения и перезапуски — у агента. Дублировать их метриками не нужно.

Как подключить источник метрик

  1. Добавьте в приложение эндпоинт метрик — отдельный реестр только с нужными рядами (примеры ниже).

  2. Проверьте, что эндпоинт открывается из сети агента:

    bash
    docker compose exec nabl-agent wget -qO- http://worker:9090/metrics
  3. Откройте стек → вкладка «Метрики» → кнопка «Источники» → «Добавить источник». Укажите:

    • Название — станет группой графиков на вкладке, например «Очереди»;
    • URL метрик — адрес из сети агента: в Docker Compose http://<имя сервиса>:<порт>/metrics, в Kubernetes http://<service>.<namespace>.svc:<порт>/metrics;
    • Интервал опроса — как у агента (около 30 с) или от 10 секунд до 5 минут.
  4. Через один–два интервала на вкладке появятся графики.

Опрашивает эндпоинт агент, а не облако Наблюдателя, поэтому снаружи он может быть недоступен. Нужен агент 0.6.0 или новее.

Важно. Агент делает обычный GET без заголовков и токенов, поэтому эндпоинт должен отвечать без авторизации. Отдавайте его во внутренней сети — на отдельном порту или пути, закрытом от внешнего прокси.

Чей это сервис. С агентом 0.12 и новее Наблюдатель сам находит, какой сервис стека отдаёт метрики: по адресу вроде http://worker:9090/metrics — сразу, по публичному адресу — по трассе сбора, если сервис трассируется. В карточке источника видно «сервис worker», а на карте сервисов при наведении на узел — какие метрики он отдаёт.

Форматы: Prometheus и JSON

Формат агент определяет сам — выбирать его не нужно.

Prometheus text — ответ любого клиента Prometheus:

text
# HELP queue_pending Писем в очереди
# TYPE queue_pending gauge
queue_pending{queue="emails"} 42
# HELP orders_created_total Оформлено заказов
# TYPE orders_created_total counter
orders_created_total 1201
  • gauge — значение показывается как есть.
  • counter — накопительный счётчик: на графике показывается прирост между опросами, то есть сколько событий было за интервал.
  • У histogram строки _bucket пропускаются, а _sum и _count считаются счётчиками.

JSON — плоский объект, все числовые поля считаются gauge:

json
{"queue_pending": 42, "active_sessions": 17}

или список с типом, метками, названием и единицей:

json
{"metrics": [
  {"name": "queue_pending", "value": 42, "kind": "gauge", "labels": {"queue": "emails"}, "title": "Писем в очереди"},
  {"name": "orders_created_total", "value": 1201, "kind": "counter", "title": "Оформлено заказов"},
  {"name": "basket_value", "value": 3400, "title": "Средняя корзина", "unit": "rub"}
]}

Вложенные объекты и строковые значения игнорируются.

Метки и пределы

Метки делят одну метрику на ряды: queue_pending{queue="emails"} и queue_pending{queue="sms"}. Используйте метки с небольшим числом значений — queue, state, partner. Не кладите в метки id пользователей и заказов или адреса с id.

Предел Значение
Размер ответа эндпоинта до 1 МиБ
Значений за один опрос до 200 (каждое сочетание меток — отдельное значение), лишние отбрасываются
Имён метрик на стек до 200 из всех источников; новые имена сверх предела не сохраняются
Интервал опроса от 5 секунд до 1 часа
Хранение 30 дней

Поэтому готовые наборы метрик целиком не подключайте: collectDefaultMetrics() в prom-client, /actuator/prometheus в Spring, postgres_exporter и redis_exporter отдают сотни рядов, и в Наблюдатель попадут случайные первые 200.

Названия и единицы

Единица берётся из окончания имени метрики — по соглашениям Prometheus. Благодаря ей на графиках «1,5 ГБ», «20 мс», «3,8 %» и «14 ч назад» вместо голых чисел.

Окончание Единица Пример
_seconds длительность queue_oldest_message_age_seconds
_milliseconds, _ms миллисекунды partner_api_latency_ms
_bytes байты cache_memory_bytes
_ratio доля 0..1 → проценты cart_conversion_ratio
_percent проценты 0..100 pool_used_percent
_timestamp_seconds момент времени, «N ч назад» cron_last_success_timestamp_seconds
_rub рубли payment_amount_rub_total

_total и _sum в конце не мешают. Рост метрик со словами failed, error, lag, latency, pending, dlq подсвечивается красным, рост success, orders, payment, conversion — зелёным.

Понятное название. По умолчанию метрика подписана техническим именем, например queue_pending{queue="emails"}. Понятное название — «Писем в очереди (emails)» — видно на вкладке «Метрики», на странице метрики, при создании правила и в уведомлениях. Значения меток дописываются в скобках сами. Откуда берётся название:

  1. Задано вручную — значок карандаша «Название и единица» на странице метрики. Здесь же можно выбрать единицу: как в приложении, без единицы или свою, например «шт».
  2. Прислано приложением — # HELP в Prometheus или title в JSON (агент 0.10.0+).
  3. Иначе — техническое имя.

Название — только подпись: история и правила остаются под техническим именем.

Вкладка «Метрики»

Наверху — переключатель групп («Все» и названия источников), выбор периода — 24 часа, 7 или 30 дней — и кнопка «Источники», где источники добавляются, редактируются, выключаются и удаляются.

Сводка — одна плитка на метрику. Ряды с метками сложены: сообщения и соединения суммируются, время и проценты — по максимуму. На плитке сразу видно проблемы: «на нуле» — у какой очереди ноль там, где ожидается хотя бы один, «молчат» — сколько рядов перестали приходить, и какая очередь больше всех. Если меток немного (например, состояния пула «активно», «свободно», «ожидают»), плитка показывает каждое значение отдельной линией. Счётчик подписан как «счётчик» с интервалом, gauge — как «мгновенное».

По объектам — таблица, где объекты (например, очереди) идут строками, а метрики источника — колонками. Проблемные строки сверху, сортировка по любой колонке. Объект, который перестал приходить, стоит внизу с пометкой «не приходит» и временем последнего значения, а не выглядит как ноль.

Страница метрики

Клик по плитке открывает страницу метрики:

  • любой период, сдвиг на длину периода назад и вперёд, приближение выделением участка графика;
  • отдельная линия на каждую метку с кнопками «Показать все», «Скрыть все» и «только эта»;
  • сравнение «Сравнить со вчера» или «С прошлой неделей»;
  • для счётчиков — переключение на «в минуту»;
  • таблица по меткам: сейчас, среднее, минимум, максимум, когда обновлено;
  • разрыв линии на графике — источник в это время не отвечал;
  • блок «Уведомления» — правила по этой метрике, их пороги и срабатывания прямо на графике.

Как удалить метрику или пропавшие ряды

Если приложение перестало присылать часть очередей (было 9, стало 4), старые ряды остаются с пометкой «не приходит» до конца хранения. Убрать их:

  • в таблице «По объектам» — кнопка удаления у строки или «удалить не приходящие» сразу для всех;
  • на странице метрики — «Удалить метрику» целиком: история, название и единица.

Удаление необратимо. Если приложение снова пришлёт удалённые значения, они появятся заново. Правила по удалённой метрике не удаляются, но перестают срабатывать.

Уведомления по метрикам

Правила создаются на странице метрики кнопкой «Создать правило» или в разделе «Правила» проекта — объект «Метрики приложения». Правило действует на уровне проекта: по умолчанию — на эту метрику во всех стеках. Подробно о правилах — в статье правила уведомлений.

Задача Как настроить
Очередь emails больше 100 дольше 5 минут порог «больше 100», метка queue=emails, агрегация «минимум» за 5 минут
Любая очередь больше 1000 порог без меток — каждая очередь оценивается и уведомляет отдельно
Больше 10 упавших задач за 15 минут порог на счётчик — сравнивается прирост за окно
Воркер встал: за 30 минут ни одной задачи счётчик выполненных задач «меньше 1» за 30 минут
Пропала метрика, хотя источник отвечает любое пороговое правило с включённой реакцией на отсутствие данных
Заказов заметно меньше обычного аномалия — без ручного порога
  • Агрегация для gauge: последнее значение, среднее за окно, минимум (значение держалось выше порога всё окно и не реагирует на одиночный всплеск) или максимум (хоть раз превысило). Для счётчика — прирост за окно.
  • Окно — от 1 минуты, но фактически не короче двух интервалов опроса.
  • Аномалия сравнивает последние 10 минут с обычным уровнем за 6 часов. Нужно около 2 часов истории, порог не задаётся — только чувствительность. Подходит для показателей с устойчивым уровнем (заказы, обработанные задачи), а не для того, что обычно стоит на нуле. Подробнее — в статье аномалии.

Когда значение вернулось в норму, приходит событие «Порог в норме» уровня «инфо» — по умолчанию каналы его не присылают.

«Источник метрик не отвечает»

Правило «Источник метрик не отвечает» заводится само при добавлении первого источника метрик в проекте. Оно срабатывает, если от источника нет ни одного значения дольше окна (не меньше трёх интервалов опроса): приложение упало, эндпоинт сломался или в адресе опечатка. Когда метрики снова пойдут, придёт «Источник метрик снова отвечает». Если молчит весь агент стека, придёт одно уведомление «агент не на связи», а не по каждому источнику. Если разом замолчали или снова заработали несколько источников стека, уведомление тоже одно — со списком источников, а в ленте стека видно событие по каждому. Пока молчит весь источник, отдельные уведомления «нет данных» по его метрикам не приходят.

Совет. В проектах, где источники были добавлены до появления этого правила, оно создано выключенным — включите его в разделе «Правила».

Примеры: мониторинг очередей

Значения очередей считаются в момент опроса — функцией сбора, а не хранятся в переменной.

Node.js, BullMQ и prom-client:

js
const client = require('prom-client');
const registry = new client.Registry(); // без collectDefaultMetrics — только свои ряды
new client.Gauge({
  name: 'queue_jobs', help: 'Задачи в очереди по состоянию',
  labelNames: ['queue', 'state'], registers: [registry],
  async collect() {
    for (const q of queues) { // массив Queue из bullmq
      const counts = await q.getJobCounts('waiting', 'active', 'delayed', 'failed');
      for (const [state, n] of Object.entries(counts)) this.set({ queue: q.name, state }, n);
    }
  },
});
app.get('/metrics', async (_req, res) => {
  res.type(registry.contentType).send(await registry.metrics());
});

Python, Celery на Redis и prometheus_client (FastAPI):

python
from prometheus_client import CollectorRegistry, Gauge, generate_latest, CONTENT_TYPE_LATEST
registry = CollectorRegistry()  # без process/platform-коллекторов
queue_pending = Gauge("queue_pending", "Задач ждёт в очереди", ["queue"], registry=registry)

@app.get("/metrics")
def metrics():
    for q in ("celery", "emails"):            # очередь Celery на Redis — список с именем очереди
        queue_pending.labels(q).set(redis_client.llen(q))
    return Response(generate_latest(registry), media_type=CONTENT_TYPE_LATEST)

Go, client_golang:

go
reg := prometheus.NewRegistry() // без Go/process-коллекторов
reg.MustRegister(prometheus.NewGaugeFunc(
	prometheus.GaugeOpts{Name: "outbox_pending", Help: "Неотправленных сообщений outbox"},
	func() float64 { return float64(countPendingOutbox()) },
))
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))

.NET, minimal API, JSON:

csharp
app.MapGet("/internal/metrics", async (AppDb db) => new {
    metrics = new object[] {
        new { name = "outbox_pending", kind = "gauge", title = "Неотправленных сообщений outbox",
              value = await db.Outbox.CountAsync(m => m.SentAt == null) },
    }
});

Java, Spring Boot: не подключайте /actuator/prometheus целиком — там сотни рядов jvm_* и http_server_*. Сделайте отдельный контроллер с JSON или оставьте в реестре только свои метрики: MeterFilter.denyUnless(id -> id.getName().startsWith("app.")).

RabbitMQ без кода в приложении: включите плагин rabbitmq_prometheus (rabbitmq-plugins enable rabbitmq_prometheus, порт 15692) и добавьте источник:

text
http://rabbitmq:15692/metrics/detailed?family=queue_coarse_metrics&vhost=/

Ряды rabbitmq_detailed_queue_messages_ready, _unacked и _messages с метками vhost и queue — три значения на очередь, поэтому влезает около 60 очередей. Основной /metrics RabbitMQ даёт только итоги по всему узлу.

Если метрик нет

  • Эндпоинт не открывается из сети агента: другая сеть compose или закрыт порт.
  • Эндпоинт отвечает не 200.
  • Формат не распознан — проверьте ответ командой wget из контейнера агента.

ИИ-ассистент может сам найти в коде проекта очереди и пулы, предложить список метрик, написать эндпоинт, добавить источник и настроить уведомления.

Что дальше

Попробуйте Наблюдатель

Первый сервер или стек подключается за пару минут. Бесплатно в раннем доступе.

Создать аккаунт

Нашли неточность или не хватает ответа? Напишите в поддержку