Перейти к содержимому

Метрики (custom time-series)

Метрики — это лёгкий способ слать в Notifly произвольные числовые значения (стоимость запроса к LLM, длину очереди, RPS, остаток на балансе, температуру датчика) и получать из них временные ряды с графиками и пороговыми алёртами.

Вы создаёте источник метрик (metric source) — он получает публичный токен с префиксом M — и шлёте на POST /metric/<token> числа с произвольными именами. Метрики создаются автоматически при первом поступлении: заранее ничего объявлять не нужно. Каждая точка раскладывается в три pre-агрегированных бакета (1m, 1h, 1d), которые потом отдаются для графиков.

Удобно для:

  • учёта расходов на AI (llm_cost_total, tokens_total) прямо из кода;
  • бизнес-метрик (заказы, регистрации, выручка) без отдельной BI-системы;
  • IoT / телеметрии (температура, влажность, заряд) с дешёвым HTTP-ingest;
  • лёгкой замены Prometheus push-gateway для pet-проектов и cron-скриптов.
  1. Откройте app.notifly.ru → Мониторинг → Метрики.
  2. Нажмите «Создать источник метрик», заполните:
    • Название — для отображения, например «LLM-расходы» или «Прод-телеметрия».
    • Канал уведомлений — куда слать алёрты по метрикам этого источника (свой канал или расшаренный с правом отправки). Можно оставить «—»: точки будут приниматься, но алёртам некуда будет уходить.
  3. После создания токен (M…) и готовые примеры интеграции видны в строке источника — токен понадобится для ingest.
Окно терминала
curl -X POST "$NOTIFLY_URL/metric-source" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <api-token>" \
-d '{
"name": "LLM-расходы",
"channelId": 12345
}'

В ответе придёт объект источника с публичным token (префикс M и 40 hex-символов):

{
"id": 7,
"token": "M7c2a8f3b1e0d4a6c8e9f2b1d4c7e0a9b2f5d8c1e",
"channelId": 12345,
"name": "LLM-расходы",
"created": "2026-06-23T10:11:12Z",
"metricCount": 0,
"appName": "Marketing"
}

Полный URL для ingest — ${NOTIFLY_URL}/metric/M7c2a8f3b1e0d4a6c8e9f2b1d4c7e0a9b2f5d8c1e.

Публичный эндпоинт без авторизации (аутентификация — сам токен в URL). Принимает три формата тела (application/json) — выбирайте удобный.

{"name": "queue_depth", "value": 42}

Карта «имя → значение». За один запрос обновляете сразу несколько метрик:

{
"values": {
"llm_cost_total": 0.42,
"tokens_total": 1230,
"queue_depth": 17
}
}

Поле values толерантно к двум формам JSON: можно прислать объект (как выше) или массив [{"name":"cost","value":0.42}, {"name":"tokens","value":1230}] — элементы без name игнорируются, при повторе имени побеждает последнее значение.

Для отправки исторических точек или дозаписи задним числом. Каждая точка может нести свой ts:

{
"points": [
{"name": "temperature", "value": 21.4, "ts": 1748174400},
{"name": "temperature", "value": 21.7, "ts": 1748174460},
{"name": "humidity", "value": 55}
]
}

Поле ts — Unix-время числом, секунды или миллисекунды (определяется автоматически по величине):

  • Unix-секунды — целое < 1e12, например 1748174400;
  • Unix-миллисекунды — целое >= 1e12, например 1748174400000.

Если ts не указан, берётся текущее время сервера (UTC).

Все три формата можно комбинировать в одном запросе — точки из name/value, values и points объединяются в общий список.

Эндпоинт возвращает 204 No Content при успехе. Если в теле нет точек (например {}) или токен неизвестен — тоже 204 (тихо игнорируется). При превышении дневной квоты событий — 429 (точки не записываются, владельцу приходит уведомление о квоте); 429 с заголовком Retry-After возможен и при слишком частых запросах. Невалидный JSON, битое имя метрики, нечисловое значение или больше 500 точек дают 400.

ОграничениеЗначение
Максимум точек в одном запросе500
Размер тела запросадо 64 КиБ
Максимум уникальных метрик в источнике100 (точки новых метрик сверх лимита тихо пропускаются)
Имя метрикиregex ^[a-zA-Z0-9_\-\.]{1,64}$ — латиница, цифры, _, -, ., до 64 символов
Значениеконечное число (не NaN, не ±Inf)

Запрос с битым именем метрики или нечисловым значением целиком отклоняется с 400 и понятным описанием ошибки.

Окно терминала
# curl — одно значение
curl -X POST "$NOTIFLY_URL/metric/M7c2a8f3b1e0d4a6c8e9f2b1d4c7e0a9b2f5d8c1e" \
-H "Content-Type: application/json" \
-d '{"name":"llm_cost_total","value":0.0042}'
# curl — батч
curl -X POST "$NOTIFLY_URL/metric/M7c2a8f3b1e0d4a6c8e9f2b1d4c7e0a9b2f5d8c1e" \
-H "Content-Type: application/json" \
-d '{"values":{"orders":12,"revenue":3490.5}}'
# Python
import requests
requests.post(
"https://api.notifly.ru/metric/M7c2a8f3b1e0d4a6c8e9f2b1d4c7e0a9b2f5d8c1e",
json={"values": {"llm_cost_total": 0.0042, "tokens_total": 1230}},
timeout=5,
)
// JavaScript / Node
await fetch('https://api.notifly.ru/metric/M7c2a8f3b1e0d4a6c8e9f2b1d4c7e0a9b2f5d8c1e', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({name: 'queue_depth', value: 42}),
});
// Go
body := strings.NewReader(`{"name":"queue_depth","value":42}`)
http.Post("https://api.notifly.ru/metric/M7c2a8f3b1e0d4a6c8e9f2b1d4c7e0a9b2f5d8c1e",
"application/json", body)

У каждой метрики есть тип, влияющий на визуализацию по умолчанию:

ТипСмыслКак рисуется
gaugeмоментальное значение, которое колеблется вверх-вниз (температура, длина очереди, баланс)по last / avg бакета
counterмонотонно растущий счётчик (всего запросов, суммарная стоимость)акцент на sum за период

Тип определяется автоматически по имени при создании метрики (конвенция в духе Prometheus): если имя оканчивается на _total, _count, _sum или _counter — это counter, иначе gauge. Например llm_cost_total → counter, queue_depth → gauge.

Если авто-определение ошиблось, тип можно переопределить через API — PATCH /metric-source/:id/metrics/:mid:

Окно терминала
curl -X PATCH "$NOTIFLY_URL/metric-source/7/metrics/31" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <api-token>" \
-d '{"type":"gauge"}'

Допустимы только значения gauge и counter — иначе 400.

Каждая точка при поступлении раскладывается сразу в три бакета — 1m, 1h, 1d. В каждом бакете накапливаются sum, count, min, max и last (последнее по времени значение). У бакетов разный срок хранения (TTL):

БакетГранулярностьХранится
1mпоминутно24 часа
1hпочасово30 дней
1dпосуточно365 дней

Страница источника показывает его метрики (последнее значение, время, число точек) и график выбранной метрики за период от 10 минут до года (10m … 1y; для 10m и 1h есть живой режим Live). Отметьте две и больше метрики — появится общий график для сравнения.

Окно терминала
curl "$NOTIFLY_URL/metric-source/7/metrics/31/series?bucket=1h&from=2026-06-22T00:00:00Z&to=2026-06-23T00:00:00Z" \
-H "X-Notifly-Key: <api-token>"

Параметры:

ПараметрЗначениеПо умолчанию
bucket1m, 1h или 1d1h
from, toграницы интервала, RFC3339последние 24 часа

Неизвестный bucket даёт 400; нераспознанные from/to молча заменяются значениями по умолчанию.

Ответ — массив бакетов; фронт сам решает, что рисовать (last для gauge, sum для counter):

[
{"ts": "2026-06-22T10:00:00Z", "sum": 4.2, "count": 60, "min": 0.01, "max": 0.21, "last": 0.07},
{"ts": "2026-06-22T11:00:00Z", "sum": 3.9, "count": 58, "min": 0.01, "max": 0.18, "last": 0.05}
]

Если запрошенный бакет пуст (например, 1d ещё не успел накопиться), но поминутные данные есть, Notifly агрегирует на лету из 1m-бакетов.

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

Поля алёрта:

ПолеОписание
metricNameимя метрики, к которой применяется правило (обязательное, иначе 400)
operatorоператор сравнения: >, >=, <, <=, == (обязательное)
thresholdпороговое значение (число)
titleзаголовок уведомления; если пусто — генерируется автоматически (⚠️ <metric> <op> <threshold>)
cooldownMinutesминимальный интервал между уведомлениями этого правила; 0 → 60 минут

Канал в алёрте не задаётся — он берётся из источника метрик (поле channelId источника); присланный в теле channelId игнорируется. Канал фиксируется в правиле при его создании и сохранении: если потом сменить канал источника, пересохраните алёрты. У источника без канала алёрты не доставляются.

В админке алёрт добавляется на странице источника («Добавить алерт»), имя метрики выбирается из уже поступивших. Через API правило можно завести и заранее, на метрику, которой ещё нет.

Окно терминала
# Алёрт: расходы на LLM за сутки превысили $50
curl -X POST "$NOTIFLY_URL/metric-source/7/alerts" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <api-token>" \
-d '{
"metricName": "llm_cost_total",
"operator": ">",
"threshold": 50,
"title": "LLM-расходы превысили $50",
"cooldownMinutes": 360
}'

При срабатывании в канал источника прилетит обычное сообщение с текстом вида:

llm_cost_total = 51.3
Источник: LLM-расходы
Порог: > 50

Приоритет сообщения — приоритет канала по умолчанию. Как и любое сообщение канала, оно эскалируется по настроенным методам доставки, подчиняется тихим часам и в активном окне обслуживания канала приходит беззвучно.

Публичный ingest-эндпоинт не требует авторизации (токен источника в URL). Все остальные требуют API-токен (X-Notifly-Key); операции записи — с правом write.

Метод и путьАвторизацияНазначение
POST /metric/:tokenпубличный (токен источника)отправить точки (три формата)
GET /metric-sourceAPI-токенсписок источников
POST /metric-sourceAPI-токен (write)создать источник
PUT /metric-source/:idAPI-токен (write)обновить (название, канал)
DELETE /metric-source/:idAPI-токен (write)удалить источник
GET /metric-source/:id/metricsAPI-токенсписок метрик источника
PATCH /metric-source/:id/metrics/:midAPI-токенсменить тип (gauge/counter)
DELETE /metric-source/:id/metrics/:midAPI-токен (write)удалить метрику
GET /metric-source/:id/metrics/:mid/seriesAPI-токенвременной ряд (бакеты)
GET /metric-source/:id/alertsAPI-токенсписок алёртов
POST /metric-source/:id/alertsAPI-токен (write)создать алёрт
PUT /metric-source/:id/alerts/:aidAPI-токен (write)обновить алёрт
DELETE /metric-source/:id/alerts/:aidAPI-токен (write)удалить алёрт

Отправка точек и управление источниками, метриками и алёртами есть и в Go и Python SDK (PushMetric/PushMetrics, push_metric/push_metrics).

Смежные возможности: Активные мониторы, Heartbeat.