Наблюдаемость
Три опоры: структурные логи с сквозным идентификатором, метрики Prometheus и журнал аудита. Решения — ADR-0010, ADR-0011, ADR-0012, ADR-0013.
Структурные логи и correlation-id
StructuredLoggerService(core/observability) пишет одну JSON-строку на событие в stdout и подключён глобально; уровень задаётLOG_LEVEL(error|warn|log|debug|verbose), по умолчанию — поNODE_ENV.- Correlation-id живёт в
AsyncLocalStorage(correlation.store.ts).traceIdMiddlewareберёт входящийX-Request-Id(допустимы[A-Za-z0-9._-]{1,128}) либо генерирует UUID и возвращает его в заголовке ответа; id автоматически попадает во все логи запроса. - Воркеры исполняют каждую джобу в собственном контексте (
correlation.run('job-<очередь>-<id>', …)), поэтому логи фоновых задач и cron-тиков трассируются так же, как HTTP. LoggingInterceptorпишет access-лог каждого HTTP-запроса.- Маскирование секретов (
redact.ts): значения ключейpassword,token,secret,authorization,cookie,apiKeyи подобных заменяются (регистр и разделители не важны). Контент промптов и ответов LLM в логи не попадает. - События сообщений следуют конвенции
домен.событие key=value, напримерrnp.recompute.enqueued account=… days=…,repricer.schedules.registered count=….
Метрики Prometheus
Эндпоинт GET /metrics (core/metrics, @willsoto/nestjs-prometheus):
| Метрика | Тип | Что показывает |
|---|---|---|
optima_http_request_duration_seconds | histogram | длительность HTTP по method, route (шаблон маршрута), status_code |
optima_queue_jobs | gauge | число джоб очереди по состояниям (queue, state) |
optima_queue_job_duration_ms | gauge | перцентили длительности последних завершённых джоб очереди |
optima_db_pool_connections | gauge | соединения пула PostgreSQL по состояниям |
optima_reaggregate_jobs_total, optima_reaggregate_duration_ms | counter / histogram | пересчёт дневных агрегатов по domain |
optima_rnp_sync_runs_total, optima_rnp_sync_rows_total | counter | прогоны и строки синков РНП (sync = ads/sections/finance/merge) |
optima_catalog_sync_runs_total, optima_catalog_sync_rows_total | counter | синхронизация каталога |
optima_digitization_feed_sync_* | counter / histogram | синк фидов оцифровки по ярусам (tier) |
Плюс стандартные процессные метрики prom-client (в тестах отключены). Метки не содержат
идентификаторов пользователей. В production /metrics закрыт HTTP Basic
(METRICS_USER + METRICS_PASSWORD); без этих переменных эндпоинт отдаёт 404 — Prometheus
настраивается на basic-auth в scrape-конфиге.
Наблюдаемость очередей
QueueDiscoveryServiceавтоматически находит все очереди BullMQ, зарегистрированные черезBullModule.registerQueue(перечень — Фоновые задачи).- Bull Board —
/admin/queues: просмотр, повтор и удаление джоб. Dev — открыт; в production — HTTP Basic при заданныхQUEUE_BOARD_USERиQUEUE_BOARD_PASSWORD, иначе не монтируется (в лог пишется предупреждение). - Неудачные джобы не удаляются (
removeOnFail: false), успешные хранятся 24 часа или до 1000 штук.
Health-проверки
| Эндпоинт | Назначение |
|---|---|
GET /health | readiness: проверка PostgreSQL и Redis; в production — только { status, timestamp }, детали уходят в лог |
GET /health/live | liveness процесса |
GET /version | версия приложения и git-SHA сборки (GIT_SHA) |
Журнал аудита
Модуль modules/audit-log (таблица audit_logs, записи иммутабельны):
- поля: тип и id актора (
user,system,agent),action,entityType/entityId,outcome(success|failure),before/after/metadata(JSONB),correlationId— по нему запись связывается с логами; - два пути фиксации: декоратор
@Audit({ action, entityType })сAuditInterceptorи явныйAuditService.record(); секреты маскируются, а сбой аудита не ломает бизнес-операцию; - пишут аудит: аутентификация (вход, блокировки, reuse refresh, отзыв сессий), кабинеты WB,
цены и репрайсер, автобиддер, автоответы, бухгалтерия, A/B-тесты, действия ИИ-агента
(
ai-agent.action.*); - чтение —
GET /audit-logs, только рольadmin, с фильтрами и пагинацией; в интерфейсе — «Администрирование → Журнал аудита» (описание).
Практики безопасности, связанные с логами и метриками, — Безопасность.