Skip to main content
Операционная поверхность Omniflow намеренно мала: три неаутентифицированных HTTP-эндпоинта, один реестр Prometheus на процесс и структурированные логи в формате JSON в stdout. Всё на ней рассчитано на ваш оркестратор и ваш мониторинг, и ничему из этого не место на публичном хосте API — оба поставляемых примера прокси отдают 404 на /metrics и /readyz в домене API, потому что эти эндпоинты отвечают на вопросы о внутреннем устройстве установки любому желающему. См. Обратный прокси. Эта страница описывает то, что существует. Там, где метрика объявлена, но никогда не пишется, об этом сказано прямо, потому что оповещение, построенное на ряде, который никогда не выдаётся, — это оповещение, которое никогда не срабатывает.

Три эндпоинта

Они разделены, потому что отвечают на разные вопросы. Процесс, который работает, но не может достучаться до PostgreSQL, надо вывести из пула, а не убивать и перезапускать: перезапуск не вернёт базу данных, а цикл перезапусков превращает одну отказавшую зависимость в полное отсутствие мощности.
version всегда равна dev. Все три бинарника объявляют var version = "dev", и ни Dockerfile, ни релизный workflow не переопределяют её флагом компоновщика, поэтому выпущенные образы сообщают dev в /livez, в операторском диагностическом пакете и в анонимном телеметрическом сигнале. Опознавайте работающую сборку по тегу образа, а не по этому полю.

Где слушает каждый процесс

Из коробки у бота нет эндпоинта проб. Если в вашем регламенте написано «проверить /readyz на каждом процессе», сначала задайте APP_BOT_METRICS_ADDR. Она игнорируется, когда совпадает с APP_BOT_HTTP_ADDR, адресом слушателя вебхуков: публиковать метрики на хосте, до которого достаёт Telegram, запрещено, а не тихо разрешено.

Готовность

Готовность запускает по одной пробе на каждую зарегистрированную зависимость и отвечает 503, пока хоть одна из них не проходит.
error — это стабильная классификация, и она бывает только timeout (контекст пробы истёк) или unavailable. Собственное сообщение драйвера отбрасывается намеренно: ошибка pgx или HTTP рутинно содержит DSN, имя хоста или учётные данные, а оркестратор логирует это тело дословно. consecutiveFailures считает нездоровые замеры подряд и сбрасывается на первом успешном. Это тот же счётчик, серии которого отслеживает автоматический режим обслуживания, так что тело ответа готовности говорит вам, насколько близка зависимость к приостановке покупок. Результаты кешируются на 2 секунды, а каждая проба ограничена 3 секундами — обе величины вкомпилированы. Кеш существует для того, чтобы всплеск запросов готовности не превратился во всплеск обращений к базе данных. Проба Remnawave — это аутентифицированный вызов (ListUsers за одной записью), а значит, истёкший или отозванный токен панели отражается как недоступность, а не проходит как здоровая панель. См. Интеграция с Remnawave.
API, запущенный без APP_DATABASE_URL, не регистрирует ни одной пробы и отвечает 200 {"status":"ready","checks":[]}. Это зелёный свет для процесса, который обслуживает только четыре операционных эндпоинта и ничего больше. Если ошибка развёртывания обнулит URL базы данных, готовность этого не поймает — проверяйте заодно, что отвечает /v1/catalog.

Та же картина в панели

GET /v1/panel/overview/health (право доступа system.read) возвращает идентичный список checks, так что панель и балансировщик нагрузки не могут разойтись во мнении о том, работает ли PostgreSQL, плюс состояние платёжных провайдеров и провайдеров цифровых товаров, читаемое из базы данных. Вкладка System → Health отрисовывает это и обновляется каждые 30 секунд. См. Панель администратора.

Индикаторы уровня обслуживания

Каждый индикатор ниже опирается на метрику, реально существующую в таблице далее на этой странице. Там, где для поверхности нет метрики, на основе которой можно построить индикатор, это сказано прямо, а не прикрыто выдуманным числом.
У гистограммы бота omniflow_job_duration_seconds{kind="telegram.update"} нет соответствующего ряда счётчика omniflow_jobs_total, поэтому единственный способ получить из неё пропускную способность Telegram — это частота по собственному _count гистограммы — см. Что объявлено, но никогда не пишется.
Машиночитаемые правила оповещений и стартовый дашборд Grafana, построенные на этих же метриках, — в Надёжность и эксплуатация.

Метрики

Каждый процесс строит собственный изолированный реестр Prometheus, отдаваемый по GET /metrics в текстовом формате экспозиции вместе со стандартными коллекторами Go и процесса. Каждый ряд несёт постоянную метку service со значением api, bot или worker. Кардинальность ограничена самой конструкцией. Маршруты записываются как шаблоны маршрутов chi, например /v1/admin/orders/{orderID}, и никогда как конкретные пути; HTTP-статус группируется в 2xx, 4xx, 5xx или unknown; несопоставленный запрос записывается как route="unmatched". Ни один идентификатор клиента, заказа или платежа никогда не становится меткой, потому что цель сбора хранит значения меток бессрочно, а эндпоинт метрик редко защищён так же хорошо, как API.

Что пишется

omniflow_jobs_total использует вид задачи, поэтому ряд выдачи доступа — это kind="fulfillment". fulfillment.operation — это имя спана, а не значение метки, и запрос по нему не находит ничего.

Что объявлено, но никогда не пишется

Четыре метрики появляются в выводе /metrics без замеров, потому что ничто в коде их никогда не выставляет: Ещё два пробела стоит знать, когда вы пишете дашборд. Задачи доставки цифровых товаров не инструментированы — omniflow_jobs_total{kind="goods_delivery"} и ряд её длительности не появляются никогда, поэтому застрявшая доставка видна на экранах магазина в панели, но не в Prometheus. А у omniflow_job_duration_seconds{kind="telegram.update"} нет парного ряда-счётчика, так что скорость по _count гистограммы — единственный способ получить из этой пары пропускную способность Telegram.

Защита /metrics на API

API отдаёт /metrics на том же слушателе, что и публичный API. Задайте APP_METRICS_TOKEN, и маршрут будет отвечать только на Authorization: Bearer <token> — укажите его как учётные данные authorization в задании сбора Prometheus. Без токена маршрут остаётся открытым, как в прежних выпусках, а API при старте пишет предупреждение; закройте путь на обратном прокси, как это делают оба поставляемых примера. Пробы /livez, /readyz и /healthz не закрываются никогда. Воркер и бот отдают метрики на собственных слушателях, которые не предназначены для публикации, и токен не читают.

Как выключить метрики

APP_METRICS_ENABLED по умолчанию равна true и читается api и worker: установка её в false оставляет объект метрик непостроенным, что убирает и маршрут /metrics, и middleware HTTP-инструментирования, тогда как пробы продолжают работать. Бот её игнорирует — он строит метрики безусловно и отдаёт /metrics всякий раз, когда APP_BOT_METRICS_ADDR задан и отличается от адреса вебхука. Объект метрик повсюду безопасен к nil: каждый вспомогательный метод записи выходит сразу же на nil-получателе, поэтому отключение метрик убирает инструментирование, а не добавляет путь к падению.

Оповещения, которые стоит настроить

Каждое из них построено на ряде, который действительно кто-то пишет:
  • omniflow_dependency_up == 0 дольше, чем APP_MAINTENANCE_FAILURE_STREAK × APP_MAINTENANCE_PROBE_SECONDS, — это тот момент, когда автоопределение приостановит покупки.
  • omniflow_maintenance_mode == 1 дольше запланированного окна. Сигнал — это автоопределение, открывшее окно, которого вы не планировали; инцидент — это то, что окно не закрывается.
  • rate(omniflow_jobs_total{kind="fulfillment",outcome="failed"}[5m]) выше нуля — заплативший клиент ждёт выдачи доступа, которая не доходит.
  • Растущая скорость omniflow_payments_total{classification="underpayment"} или {classification="wallet_unavailable"} — деньги пришли, а заказ не рассчитался.
  • Любое значение omniflow_provider_webhooks_total{outcome="signature_invalid"} — колбэк провайдера, который установка отклоняет, обычно из-за прокси, перекодирующего тело, или из-за смены секрета.
  • omniflow_http_requests_total{status="5xx"} на сервисе api.
Поскольку датчик outbox никогда не пишется, за отставанием очереди приходится следить через GET /v1/admin/outbox или через вкладку Outbox в панели. Прочитайте Задачи и очереди, прежде чем решать, что означает растущий счётчик здесь.

Структурированное логирование

Все три процесса логируют JSON через log/slog без заданных опций обработчика, а это значит — на уровне Info по умолчанию: API и бот пишут в stdout, воркер — в stderr. Переменной окружения для уровня, формата или назначения логов нет нигде в репозитории, поэтому отладочное логирование требует изменения кода. Собирайте потоки средствами вашего контейнерного рантайма.

Редактирование

Всё, что может попасть в строку лога, в уведомление оператору или в диагностический пакет, сначала проходит через общий редактор. Он убирает:
  • токены ботов Telegram, а также значения bearer, basic, token, API-key, secret, password и authorization;
  • последовательности из 13–19 цифр, то есть похожие на карту;
  • пары hwid, device_id, charge_id и invoice_payload;
  • любой URL со схемой http, https, vless, vmess, trojan или ss — целиком, потому что даже хост идентифицирует установку, к которой подключается клиент.
Структурированные полезные нагрузки дополнительно редактируются по имени ключа, по совпадению с token, secret, password, authorization, signature, api_key, hwid, subscription_url, checkout_url, raw_body, payload, card, pan, cvv, iban, email и phone. Это применяется к полезным нагрузкам операторских уведомлений до записи строки и к последней ошибке задачи до того, как она покинет процесс. Редактор намеренно агрессивен: перередактированная строка лога — небольшое неудобство, а утёкшая ссылка на подписку или токен бота — инцидент безопасности.

Классификации вместо сообщений

Большинство операционных путей логируют стабильный код, а не строку ошибки, — именно это одновременно делает их безопасными для логирования и пригодными для поиска:
/readyz логирует readiness probe failed с числом проверок и никогда — с тем, какая именно не прошла. Чтобы узнать это, читайте тело ответа или собирайте omniflow_dependency_up.

Корреляция

X-Request-ID генерируется первым middleware в цепочке и возвращается в каждом ответе. Мутации в панели сохраняют его в событии аудита, которое пишут, так что разговор с поддержкой на тему «что именно изменил этот оператор» сводится к одному запросу. Бот логирует correlation_id на платёжных путях, а операции выдачи доступа и история их попыток несут тот же идентификатор, так что одно нажатие клиента можно проследить до вызова Remnawave, который оно вызвало.

Трассировка

Код Omniflow инструментирован OpenTelemetry в пяти точках: Атрибуты спанов подчиняются тому же правилу, что и метки метрик, и оно обеспечивается в единственной точке входа, так что ни одному вызывающему коду не приходится решать, что безопасно записывать. Спан Telegram несёт идентификатор обновления, который коррелирует с логами, но никогда не несёт аккаунт Telegram. Спаны запросов несут текст запроса и никогда — значения аргументов.
Ни один трейс никогда не экспортируется. Omniflow никогда не устанавливает провайдер трассировки, не импортирует ни одного экспортёра OTLP и не читает ни одной переменной OTEL_*. Глобальный провайдер является заглушкой, поэтому каждый описанный выше спан создаётся и выбрасывается. Установка OTEL_EXPORTER_OTLP_ENDPOINT не меняет ничего, и поддерживаемой конфигурации, включающей трассировку, сегодня не существует.
Если в форке это изменится, действуют ещё два ограничения: middleware Telegram выставляет спану статус Ok даже тогда, когда обработчик завершился ошибкой, поэтому неуспешное обновление не видно как ошибка спана, а воркер доставки цифровых товаров не открывает спан вообще. До тех пор корреляция между процессами делается по идентификатору запроса и идентификатору корреляции выдачи доступа в логах, а также через операторские поверхности, описанные в Операции администратора.

Связанные страницы

Задачи и очереди

Что на самом деле выполняет воркер и как читать представление недоставленных задач.

Режим обслуживания

К чему в итоге приводят серии отказов зависимостей в /readyz.

Диагностика неисправностей

Разбор от симптома, когда проба или метрика указывают в бесполезную сторону.

Анонимная телеметрия

Отдельный необязательный сигнал, который Omniflow отправляет о самом себе.