Логирование (Kotlin / Node.js)
Инструкция по правилам логирования в сервисах. Общие правила (формат, что писать/не писать, уровни, хранение) едины; ниже — реализация для Kotlin (SLF4J/Logback) и Node.js (Pino / Winston).
Соответствует разделу Логи в PPR.
Общий принцип
- Структурированность: везде использовать объект с полями (userId, orderId, размер, код ошибки и т.д.), а не только строку. Не сериализовать в лог целые ответы API или большие JSON.
- Секреты и PII: не помещать в объекты логов токены, пароли, полные телефоны/email; при необходимости — только маскированные значения (как
obfuscatePhoneв ventrago.ru) или ID. - Уровни: придерживаться общей шкалы (error/warn/info/debug/trace).
Формат лога
Обязательные элементы в каждой записи: дата/время, уровень, trace_id и span_id (из OpenTelemetry), идентификатор модуля/области (логгер или area), сообщение. Так логи связываются с трейсами и поиском по сервису.
Kotlin (Logback): типичный pattern:
%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(%5p [%X{trace_id:-},%X{span_id:-}]) [%thread] %logger{39} - %msg%nПоля: дата/время → уровень → [trace_id, span_id] из MDC (OTel) → поток → логгер (класс) → сообщение. trace_id и span_id должны попадать в MDC из OTel-контекста (instrumentation или явный MDC.put при обработке запроса).
Node.js: в проде эквивалент — вывод в ECS (Elastic Common Schema) с теми же полями (время, уровень, trace_id, span_id, область/logger, сообщение). Локально допустим читаемый формат (например, pino-pretty).
Что писать в лог
- Важные события — регистрация, старт/завершение обработки файла или задачи, завершение критичной операции.
- Параметры операций — не сырые данные, а характеристики: размер, тип, количество элементов (например: «файл 5 МБ, CSV, 1000 строк»).
- Идентификаторы —
userId,orderId,installationIdи т.п., чтобы связать логи по одной задаче. - Ошибки — с понятным контекстом: что пошло не так, в какой операции, какие ID при необходимости.
Примеры (Kotlin, SLF4J):
logger.info("Пользователь успешно авторизован, userId: {}, IP: {}", userId, clientIp)
logger.info("Начата обработка заказа, orderId: {}, количество позиций: {}", orderId, itemsCount)
logger.warn("Медленный запрос к БД, время: {} сек, userId: {}", durationSec, userId)
logger.error("Не удалось отправить email, orderId: {}, причина: {}", orderId, cause.message)Использовать параметризованное логирование ({} в Kotlin, объект с полями в Node.js), а не конкатенацию строк — так не создаются лишние строки при отключённом уровне.
Чего в лог не писать (обязательно)
- Секреты — пароли, токены, API-ключи, номера карт. Никогда.
- Персональные данные — ФИО в связке с телефоном/email и т.п. В идеале в логах только ID (см. PPR).
- Дампы — полные HTTP/SQL запросы и ответы, целиком тела, большие JSON/XML. Не на INFO и не на DEBUG в проде.
- Большие объёмы — не логировать весь ответ API или файл. Только размер, количество элементов, тип.
- Частые повторы — одно и то же сообщение каждую секунду создаёт шум; логировать изменения состояния и значимые события.
Полные запросы/ответы только на TRACE и только для отладки; в проде от такого лучше отказаться.
Плохо / хорошо:
| ❌ Плохо | ✅ Нормально |
|---|---|
| Лог с ФИО + телефоном | Только userId или installationId |
| Токен/пароль в сообщении | Вообще не логировать секреты |
logger.debug("Ответ API: $fullBody") | logger.debug("Размер ответа: {} байт, элементов: {}", size, count) |
| Дамп SQL/HTTP на DEBUG в проде | TRACE только при необходимости или только локально |
Уровни логов (log.level)
- ERROR — сломалось, нужно реагировать. Операция не выполнена, критичная ошибка.
- WARN — что-то не так, но работа продолжается. Медленный запрос, устаревший API, неожиданное значение.
- INFO — нормальные события: авторизация, создание заказа, старт задачи, старт сервиса.
- DEBUG — детали для разработки: вход в метод с параметрами, промежуточные значения, размеры данных. Не дампы.
- TRACE — максимальная детализация; при необходимости — полные запросы/ответы только для отладки.
Выбор уровня: «Насколько это важно в проде?» — ERROR → срочно, WARN → внимание, INFO → факт работы, DEBUG/TRACE → отладка.
Логирование в Kotlin
- Логгер:
private val logger = LoggerFactory.getLogger(javaClass)или вынести в companion. - Уровни:
logger.error(...),logger.warn(...),logger.info(...),logger.debug(...),logger.trace(...). - Параметры:
logger.info("orderId: {}, count: {}", orderId, count)— не"orderId: $orderId"при динамическом уровне. - Исключения:
logger.error("Описание контекста", throwable)— передавать исключение последним аргументом, если API логгера это поддерживает (так в Logback попадёт stack trace).
Логирование в Node.js
- Библиотека: жёсткого требования нет при соблюдении формата. Для новых проектов рекомендуется Pino +
@elastic/ecs-pino-format; в существующих встречается Winston +@elastic/ecs-winston-format. - Прод: вывод в формате ECS (Elastic Common Schema), чтобы логи корректно попадали в Kibana/ELK.
- Структура: структурированное логирование (объект с полями + сообщение), обязательная область —
areaили дочерний логгер с контекстом; ошибки передавать явно в объекте (полеerrorсо stack). - trace_id и span_id: обязательны — брать из OTel или request context и добавлять в каждую запись для связи логов с трейсами.
- Уровни: те же, что и для Kotlin — error, warn, info, debug, trace.