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

Метрики (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: <client-token>" \
-d '{
"name": "LLM-расходы",
"channelId": 12345
}'

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

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

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

Публичный эндпоинт без авторизации (аутентификация — сам токен в 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. Невалидный JSON, битое имя метрики или нечисловое значение дают 400.

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

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

Окно терминала
# curl — одно значение
curl -X POST "$NOTIFLY_URL/metric/M7c2a8f3b1e0d4a6c8e9f2b1d" \
-H "Content-Type: application/json" \
-d '{"name":"llm_cost_total","value":0.0042}'
# curl — батч
curl -X POST "$NOTIFLY_URL/metric/M7c2a8f3b1e0d4a6c8e9f2b1d" \
-H "Content-Type: application/json" \
-d '{"values":{"orders":12,"revenue":3490.5}}'
# Python
import requests
requests.post(
"https://notifly.ru/metric/M7c2a8f3b1e0d4a6c8e9f2b1d",
json={"values": {"llm_cost_total": 0.0042, "tokens_total": 1230}},
timeout=5,
)
// JavaScript / Node
await fetch("https://notifly.ru/metric/M7c2a8f3b1e0d4a6c8e9f2b1d", {
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://notifly.ru/metric/M7c2a8f3b1e0d4a6c8e9f2b1d",
"application/json", body)

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

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

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

Если авто-определение ошиблось, тип можно переопределить вручную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: <client-token>" \
-d '{"type":"gauge"}'

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

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

БакетГранулярностьХранится
1mпоминутно24 часа
1hпочасово30 дней
1dпосуточно365 дней
Окно терминала
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: <client-token>"

Параметры:

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

Ответ — массив бакетов; фронт сам решает, что рисовать (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-бакетов.

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

Поля алёрта:

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

Канал не задаётся в алёрте — он всегда берётся из источника метрик (поле channelId источника), даже если прислать channelId в теле, оно игнорируется.

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

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

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

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

Метод и путьАвторизацияНазначение
POST /metric/:tokenпубличный (токен M)отправить точки (три формата)
GET /metric-sourceclient-tokenсписок источников
POST /metric-sourceclient-token (write)создать источник
PUT /metric-source/:idclient-token (write)обновить (название, канал)
DELETE /metric-source/:idclient-token (write)удалить источник
GET /metric-source/:id/metricsclient-tokenсписок метрик источника
PATCH /metric-source/:id/metrics/:midclient-tokenсменить тип (gauge/counter)
DELETE /metric-source/:id/metrics/:midclient-token (write)удалить метрику
GET /metric-source/:id/metrics/:mid/seriesclient-tokenвременной ряд (бакеты)
GET /metric-source/:id/alertsclient-tokenсписок алёртов
POST /metric-source/:id/alertsclient-token (write)создать алёрт
PUT /metric-source/:id/alerts/:aidclient-token (write)обновить алёрт
DELETE /metric-source/:id/alerts/:aidclient-token (write)удалить алёрт

Смежные возможности: Активные мониторы, Heartbeat, MCP для управления из AI-ассистента.