Skip to content

Логирование (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):

kotlin
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.