Установка

Диагностика

Что показывает /health, как читать каждое поле и куда смотреть при типичных симптомах.

На этой странице · 7

У стенда одна точка состояния — GET /health. Она не требует авторизации, отвечает одним JSON и используется и проверкой контейнера, и индикатором «сервис работает» в кабинете.

/health

curl -s localhost:8080/health
{
  "ok": true,
  "services": ["bitrix24", "ozon", "wildberries"],
  "methods": 2421,
  "logBuffer": { "pending": 0, "written": 0, "dropped": 0, "sampledOut": 0, "sampleRate": 1 },
  "responseCache": { "size": 1, "maxSize": 50000, "hits": 0, "misses": 1, "hitRate": 0 },
  "keyCache": { "size": 0, "maxSize": 20000, "hits": 93, "misses": 3, "hitRate": 0.96875 },
  "keyUsage": { "pendingKeys": 0 },
  "mgmtRate": { "subjects": 2 },
  "tunnelSessions": 0,
  "memoryMb": 148,
  "retention": { "retentionDays": 30, "lastRun": { "at": "2026-09-09T09:43:16.459Z", "deleted": 0 } }
}

Это снимок живого стенда, а не образец: числа зависят от каталога и нагрузки.

Поля ответа

ПолеЧто означает
okсервер отвечает. Никакой другой проверки за этим полем нет — база в него не входит
servicesсервисы, чьи файлы каталога движок нашёл при старте. Пустой массив — каталог не загрузился
methodsсколько методов суммарно в загруженном каталоге
logBuffer.pendingстрок журнала ждёт записи в базу. Пишутся пачкой раз в секунду или по 200 строк
logBuffer.writtenзаписано с момента старта процесса
logBuffer.droppedпотеряно из-за переполнения буфера (потолок — 20 000 строк)
logBuffer.sampledOutуспешных запросов не записано из-за прореживания
logBuffer.sampleRateдоля записываемых успешных запросов: 1 — пишутся все
responseCacheкеш тел ответов движка: размер, потолок 50 000, попадания, промахи, доля попаданий
keyCacheкеш разбора ключей API: то же самое, потолок 20 000
keyUsage.pendingKeysключей ждёт записи счётчика использования
mgmtRate.subjectsсубъектов в счётчиках лимита Management API
tunnelSessionsоткрытых WebSocket-туннелей apistend listen
memoryMbRSS процесса
retention.retentionDaysсрок хранения журнала
retention.lastRunкогда уборка отработала в последний раз и сколько удалила. null — ещё ни разу: первый прогон через 30 секунд после старта, дальше раз в час

Счётчики попаданий и промахов считаются с момента старта процесса и после перезапуска обнуляются.

Кабинет открывается, но каталога нет

Смотрите services и methods. Пустой список означает, что движок не нашёл packages/mock-engine/generated/<сервис>.catalog.json — отсутствующий сервис он молча пропускает, потому что между волнами пополнения это нормальное состояние.

При старте сервер печатает то же самое в лог:

Каталог: 2421 методов, сервисы: bitrix24, ozon, wildberries

Пустой список сервисов лечится pnpm ingest. В контейнере такого быть не должно: каталог собран в репозитории и попадает в образ.

Вебхуки не доходят

Проверяйте по порядку, начиная с самого частого:

  1. 1

    Адрес получателя не приватный

    Публичную доставку выполняет сервер, и на NODE_ENV=production приватные адреса запрещены. Отказ виден в тексте: «Адрес … внутренний. Для доставки на свою машину используйте локальный получатель и apistend listen». Переключатель — WEBHOOK_ALLOW_PRIVATE_TARGETS.

  2. 2

    localhost внутри контейнера — это сам контейнер

    Приложение на хосте доступно как host.docker.internal. Либо пользуйтесь apistend listen: туннель инициирует агент, и адрес получателя вообще не проверяется на публичность.

  3. 3

    Туннель открыт

    tunnelSessions в /health показывает число живых сессий. Ноль при запущенном apistend listen означает, что агент до API не дошёл — смотрите прокси и заголовки Upgrade.

  4. 4

    Ответ вашего приложения считается успехом

    Это свойство сервиса, а не транспорта: Ozon считает доставку успешной только при 200 и теле {"result": true}, Wildberries — строго при 200, Bitrix24 — при любом 2xx и повторов не делает вовсе.

Ключ работает после отзыва

Разбор ключей кешируется на 30 секунд, а отзыв и ротация сбрасывают кеш сразу. Поэтому в обычном стенде отозванный ключ перестаёт работать мгновенно.

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

В журнале не все запросы

Смотрите logBuffer.sampleRate и sampledOut. Выше APISTEND_LOG_SAMPLE_RPS (по умолчанию 300 запросов в секунду) успешные запросы прореживаются: синхронная запись каждой строки упирается в базу раньше, чем сам шлюз. Ошибки не прореживаются никогда — ради них журнал и открывают, — а доля выборки показывается и здесь, и в интерфейсе.

Растущий dropped — другое: это переполнение буфера, то есть база не успевает принимать записи вовсе.

Записи старше retention.retentionDays удаляются уборкой — по умолчанию через 30 дней.

Кабинет не пускает

  • Отказ по origin в консоли браузера. WEB_ORIGIN не совпадает с адресом, с которого открыт кабинет. Это единственный origin, которому разрешён CORS с cookie.
  • Вход проходит, но данные не грузятся. Проверьте NEXT_PUBLIC_API_URL: он вкомпилирован в кабинет на этапе сборки, и после смены адреса образ надо пересобрать.
  • Все сессии разом перестали работать, ключи «неизвестны». Сменился JWT_SECRET: им подписаны сессии и им же хешируются ключи API. Вернуть прежний секрет — единственный способ вернуть и то, и другое.

Стенд не поднимается

  • api перезапускается по кругу — почти всегда упавшая миграция: точка входа выполняет prisma migrate deploy до старта сервера и при ошибке завершается. Смотрите docker compose logs api.
  • web не стартует — он ждёт, пока api ответит на /health, и без здорового api не поднимется по определению.
  • Порт занят: 8080, 3100 и 5433 публикуются на хост. Порт базы выбран не 5432 как раз ради совместимости с локально установленным PostgreSQL.