Включение детального логирования

В библиотеке Class-validator процесс валидации не сопровождается встроенной системой логирования в привычном смысле. Результатом работы функций validate и validateOrReject выступает структура ошибок, которая содержит всю необходимую информацию для построения детализированных логов. Поэтому логирование реализуется поверх выходных данных валидатора и представляет собой отдельный слой инфраструктуры.

Основная цель детального логирования — восстановление полного контекста валидации: какое поле проверялось, какое значение поступило, какое ограничение нарушено и на каком уровне вложенности находится ошибка.

Ключевые источники информации для логирования:

  • массив объектов ValidationError
  • свойства property, value, constraints
  • рекурсивное поле children
  • дополнительные метаданные объекта валидации

Структура ValidationError как основа логирования

Каждая ошибка валидации представлена объектом следующей структуры:

  • property — имя поля, в котором возникла ошибка
  • value — фактическое значение, которое не прошло проверку
  • constraints — набор нарушенных правил в формате ключ → сообщение
  • children — вложенные ошибки для объектов и массивов
  • target — исходный объект (используется ограниченно, может быть исключён в логах)
  • contexts — дополнительные данные (если заданы кастомные валидаторы)

Особенность структуры заключается в рекурсивности: сложные DTO с вложенными объектами формируют дерево ошибок, которое требует обхода.


Базовое логирование результата validate

Функция validate возвращает массив ошибок. Отсутствие ошибок означает успешную валидацию.

Типовой подход к фиксации результата:

import { validate } from "class-validator";

async function logValidation(dto) {
  const errors = await validate(dto);

  if (errors.length > 0) {
    console.log("Validation failed");
    console.log(JSON.stringify(errors, null, 2));
  } else {
    console.log("Validation successful");
  }
}

Такой формат подходит только для первичной диагностики, так как структура содержит избыточные данные и плохо читается при большом количестве вложенных объектов.


Рекурсивное извлечение ошибок для подробного логирования

Для построения полноценного логирования требуется обход дерева ValidationError. Каждый узел может содержать собственные ошибки и вложенные структуры.

Базовый рекурсивный обход:

function flattenValidationErrors(errors, parentPath = "") {
  const result = [];

  for (const error of errors) {
    const currentPath = parentPath
      ? `${parentPath}.${error.property}`
      : error.property;

    if (error.constraints) {
      result.push({
        field: currentPath,
        messages: Object.values(error.constraints),
        value: error.value,
      });
    }

    if (error.children && error.children.length > 0) {
      result.push(
        ...flattenValidationErrors(error.children, currentPath)
      );
    }
  }

  return result;
}

Результат такой обработки:

  • плоский список ошибок
  • полные пути до полей
  • нормализованные сообщения
  • упрощение последующего логирования

Логирование через validateOrReject

Функция validateOrReject выбрасывает исключение при наличии ошибок, что удобно для централизованного логирования в обработчиках ошибок.

import { validateOrReject } from "class-validator";

async function process(dto) {
  try {
    await validateOrReject(dto);
  } catch (errors) {
    const formatted = flattenValidationErrors(errors);

    console.error("Validation error:");
    console.error(JSON.stringify(formatted, null, 2));
  }
}

Такой подход используется в архитектурах, где исключения являются основным механизмом управления потоком выполнения.


Форматирование логов для диагностических систем

Сырые объекты ошибок редко используются напрямую. Обычно применяется структурирование:

Рекомендуемый формат записи:

  • timestamp
  • имя DTO или контекста
  • поле ошибки
  • сообщение
  • значение
  • уровень вложенности

Пример преобразования:

function formatLogEntry(error) {
  return {
    field: error.field,
    message: error.messages.join("; "),
    value: error.value,
    level: error.field.split(".").length,
  };
}

Такой формат облегчает интеграцию с системами логирования и аналитики.


Контекстная информация в логах

Для сложных систем важно сохранять контекст выполнения:

  • идентификатор запроса
  • пользовательский идентификатор (если применимо)
  • источник данных (API, очередь, внутренний сервис)
  • тип DTO

Пример обогащённого логирования:

function logWithContext(errors, context) {
  const flat = flattenValidationErrors(errors);

  console.log(JSON.stringify({
    context,
    errors: flat,
    timestamp: new Date().toISOString(),
  }, null, 2));
}

Контекст позволяет сопоставлять ошибки с конкретными операциями системы.


Уровни логирования и фильтрация

При большом объёме данных логирование требует разделения на уровни:

  • debug — полная структура ValidationError
  • info — краткий список полей с ошибками
  • warn — бизнес-значимые ошибки
  • error — критические сбои валидации входных данных

Фильтрация часто строится на количестве ошибок или типе DTO.


Интеграция с внешними логгерами

Class-validator не привязан к конкретной системе логирования, поэтому часто используется интеграция с универсальными библиотеками:

  • структурированное логирование через pino
  • гибкие транспорты через winston

Пример с pino:

import pino from "pino";

const logger = pino();

function logValidationErrors(errors) {
  const flat = flattenValidationErrors(errors);

  logger.error({
    type: "validation_error",
    errors: flat,
  });
}

Такая модель обеспечивает:

  • JSON-структурированные логи
  • удобство анализа
  • совместимость с observability системами

Особенности логирования вложенных объектов

Вложенные DTO формируют дерево глубиной более одного уровня. Без рекурсивной обработки теряется информация о пути к ошибке.

Пример структуры:

  • user

    • address

      • city
      • zipCode

Ошибка на уровне zipCode должна логироваться как:

user.address.zipCode

Именно формирование полного пути является ключевым аспектом детального логирования.


Типичные проблемы при логировании валидации

При реализации встречаются характерные ошибки:

  • логирование target приводит к утечке чувствительных данных
  • отсутствие рекурсии скрывает ошибки вложенных DTO
  • сохранение необработанных ValidationError усложняет анализ
  • смешивание логики валидации и логирования нарушает разделение ответственности
  • отсутствие нормализации сообщений приводит к дублированию информации

Корректная архитектура предполагает отделение слоя валидации от слоя наблюдаемости и приведение ошибок к единому формату до записи в лог-систему