Логирование процесса валидации

При разработке приложений на JavaScript валидация данных редко ограничивается простым вызовом validator.isEmail() или validator.isURL(). В реальных системах требуется:

  • отслеживать причины неудачной проверки;
  • анализировать некорректные пользовательские данные;
  • выявлять ошибки бизнес-логики;
  • собирать диагностическую информацию;
  • строить аудит изменений;
  • упрощать отладку API и форм.

Библиотека Validator.js предоставляет только функции проверки значений. Логирование необходимо реализовывать самостоятельно.


Основные подходы к логированию

Логирование результата проверки

Самый базовый вариант — фиксировать успешную или неуспешную проверку.

const validator = require('validator');

function validateEmail(email) {
    const isValid = validator.isEmail(email);

    console.log({
        field: 'email',
        value: email,
        valid: isValid,
        timestamp: new Date().toISOString()
    });

    return isValid;
}

validateEmail('admin@example.com');
validateEmail('wrong-email');

Пример вывода:

{
  field: 'email',
  value: 'admin@example.com',
  valid: true,
  timestamp: '2026-05-11T10:00:00.000Z'
}

{
  field: 'email',
  value: 'wrong-email',
  valid: false,
  timestamp: '2026-05-11T10:00:01.000Z'
}

Логирование ошибок валидации

Чаще всего логируются только ошибки.

const validator = require('validator');

function validateUsername(username) {
    if (!validator.isLength(username, { min: 3, max: 20 })) {
        console.error('Ошибка валидации username:', username);

        return false;
    }

    return true;
}

Такой подход уменьшает объём логов.


Создание универсального логгера валидации

Базовая реализация

const validator = require('validator');

function logValidation(data) {
    console.log(JSON.stringify(data, null, 2));
}

function validateEmail(email) {
    const valid = validator.isEmail(email);

    logValidation({
        validator: 'isEmail',
        value: email,
        result: valid,
        date: new Date().toISOString()
    });

    return valid;
}

Расширенный логгер

function validationLogger({
    field,
    validatorName,
    value,
    result,
    options = null
}) {
    const payload = {
        field,
        validator: validatorName,
        value,
        result,
        options,
        timestamp: Date.now()
    };

    console.log(payload);
}

Использование:

const valid = validator.isLength(password, {
    min: 8
});

validationLogger({
    field: 'password',
    validatorName: 'isLength',
    value: password,
    result: valid,
    options: { min: 8 }
});

Логирование нескольких валидаторов

Последовательная проверка

function validatePassword(password) {
    const checks = [
        {
            name: 'length',
            result: validator.isLength(password, { min: 8 })
        },
        {
            name: 'uppercase',
            result: /[A-Z]/.test(password)
        },
        {
            name: 'number',
            result: /\d/.test(password)
        }
    ];

    checks.forEach(check => {
        console.log(check);
    });

    return checks.every(check => check.result);
}

Формирование полного отчёта

function validateUser(user) {
    const report = {
        email: validator.isEmail(user.email),
        login: validator.isLength(user.login, {
            min: 3,
            max: 20
        }),
        age: validator.isInt(String(user.age), {
            min: 18
        })
    };

    console.log(report);

    return report;
}

Вывод:

{
  email: true,
  login: false,
  age: true
}

Логирование с уровнями важности

Использование уровней

Разделение сообщений по степени важности упрощает анализ логов.

function log(level, message, meta = {}) {
    console.log({
        level,
        message,
        meta,
        timestamp: new Date().toISOString()
    });
}

Пример:

if (!validator.isEmail(email)) {
    log('warn', 'Некорректный email', {
        field: 'email',
        value: email
    });
}

Типичные уровни

Уровень Назначение
info Информация о проверке
warn Ошибки пользовательского ввода
error Критические ошибки
debug Отладочная информация

Интеграция с Winston

Установка

npm install winston

Настройка логгера

const winston = require('winston');

const logger = winston.createLogger({
    level: 'info',
    transports: [
        new winston.transports.Console(),
        new winston.transports.File({
            filename: 'validation.log'
        })
    ]
});

Использование вместе с Validator.js

const validator = require('validator');

function validateEmail(email) {
    const valid = validator.isEmail(email);

    if (!valid) {
        logger.warn('Ошибка email', {
            field: 'email',
            value: email
        });
    }

    return valid;
}

Интеграция с Morgan в Express

Логирование HTTP-запросов и ошибок валидации

npm install express morgan

Пример middleware

const express = require('express');
const validator = require('validator');
const morgan = require('morgan');

const app = express();

app.use(express.json());
app.use(morgan('dev'));

app.post('/register', (req, res) => {
    const { email } = req.body;

    if (!validator.isEmail(email)) {
        console.error('Неверный email:', email);

        return res.status(400).json({
            error: 'Invalid email'
        });
    }

    res.json({
        success: true
    });
});

Middleware для логирования валидации

Универсальный middleware

function validationMiddleware(field, validatorFn) {
    return (req, res, next) => {
        const value = req.body[field];

        const result = validatorFn(value);

        console.log({
            field,
            value,
            result
        });

        if (!result) {
            return res.status(400).json({
                error: `Invalid ${field}`
            });
        }

        next();
    };
}

Использование:

app.post(
    '/users',
    validationMiddleware('email', validator.isEmail),
    (req, res) => {
        res.send('OK');
    }
);

Структурированное логирование

Почему структура важна

Неструктурированные сообщения:

Ошибка email admin@

Структурированные логи:

{
  "field": "email",
  "value": "admin@",
  "validator": "isEmail",
  "result": false
}

Структурированные данные:

  • проще фильтровать;
  • удобно анализировать;
  • легко отправлять в системы мониторинга;
  • подходят для Elasticsearch и Grafana.

JSON-формат

function logValidation(data) {
    console.log(JSON.stringify(data));
}

Логирование цепочек валидации

Пошаговый контроль

function validateRegistration(data) {
    const steps = [];

    const emailValid = validator.isEmail(data.email);

    steps.push({
        step: 'email',
        result: emailValid
    });

    const passwordValid = validator.isLength(
        data.password,
        { min: 8 }
    );

    steps.push({
        step: 'password',
        result: passwordValid
    });

    console.log(steps);

    return steps.every(step => step.result);
}

Логирование времени выполнения

Измерение производительности

function validateEmail(email) {
    const start = performance.now();

    const result = validator.isEmail(email);

    const end = performance.now();

    console.log({
        validator: 'isEmail',
        executionTime: `${end - start} ms`
    });

    return result;
}

Асинхронное логирование

Запись в файл

const fs = require('fs').promises;

async function logToFile(data) {
    await fs.appendFile(
        'validation.log',
        JSON.stringify(data) + '\n'
    );
}

Использование:

async function validate(email) {
    const valid = validator.isEmail(email);

    await logToFile({
        field: 'email',
        value: email,
        valid
    });

    return valid;
}

Скрытие чувствительных данных

Проблема безопасности

Нельзя записывать в логи:

  • пароли;
  • токены;
  • номера карт;
  • CVV;
  • персональные данные.

Плохой пример:

console.log(password);

Маскирование значений

function maskPassword(password) {
    return '*'.repeat(password.length);
}

Пример:

console.log({
    password: maskPassword('secret123')
});

Результат:

{
  password: '*********'
}

Частичное скрытие email

function maskEmail(email) {
    const [name, domain] = email.split('@');

    return `${name[0]}***@${domain}`;
}

Логирование ошибок API

Формирование объекта ошибки

function validationError(field, value) {
    return {
        type: 'ValidationError',
        field,
        value,
        timestamp: Date.now()
    };
}

Использование

if (!validator.isURL(url)) {
    const error = validationError('url', url);

    console.error(error);
}

Централизованная система логирования

Отдельный модуль

// logger.js

module.exports = {
    validation(field, value, result) {
        console.log({
            type: 'validation',
            field,
            value,
            result
        });
    }
};

Подключение

const logger = require('./logger');

logger.validation(
    'email',
    email,
    validator.isEmail(email)
);

Логирование в production

Ограничение объёма логов

В production-среде чрезмерное логирование приводит к:

  • росту файлов;
  • увеличению нагрузки;
  • снижению производительности;
  • усложнению поиска ошибок.

Использование NODE_ENV

if (process.env.NODE_ENV !== 'production') {
    console.log('Debug validation');
}

Отладочное логирование

Детализированные сообщения

function debugValidation(field, value, result) {
    console.debug({
        field,
        value,
        result,
        stack: new Error().stack
    });
}

Логирование пользовательских валидаторов

Собственная функция проверки

function isStrongPassword(password) {
    return (
        validator.isLength(password, { min: 8 }) &&
        /[A-Z]/.test(password) &&
        /\d/.test(password)
    );
}

Добавление логирования

function isStrongPassword(password) {
    const result = (
        validator.isLength(password, { min: 8 }) &&
        /[A-Z]/.test(password) &&
        /\d/.test(password)
    );

    console.log({
        validator: 'isStrongPassword',
        result
    });

    return result;
}

Логирование исключений

Обработка try/catch

function validateJson(json) {
    try {
        const parsed = JSON.parse(json);

        console.log('JSON валиден');

        return parsed;
    } catch (error) {
        console.error({
            type: 'JSON_ERROR',
            message: error.message
        });

        return null;
    }
}

Хранение истории ошибок

Сохранение в массив

const validationErrors = [];

function addError(field, value) {
    validationErrors.push({
        field,
        value,
        date: Date.now()
    });
}

Использование

if (!validator.isEmail(email)) {
    addError('email', email);
}

Логирование статистики

Подсчёт ошибок

const stats = {
    success: 0,
    failed: 0
};

function validate(email) {
    const result = validator.isEmail(email);

    result
        ? stats.success++
        : stats.failed++;

    return result;
}

Вывод статистики

console.log(stats);

Использование Map для хранения результатов

const validationMap = new Map();

function validate(field, value, validatorFn) {
    const result = validatorFn(value);

    validationMap.set(field, result);

    return result;
}

Логирование с уникальными идентификаторами

Trace ID

const crypto = require('crypto');

function createTraceId() {
    return crypto.randomUUID();
}

Использование идентификатора

const traceId = createTraceId();

console.log({
    traceId,
    field: 'email',
    result: false
});

Формирование полноценного audit trail

Аудит изменений

function auditValidation({
    userId,
    field,
    value,
    result
}) {
    console.log({
        userId,
        field,
        value,
        result,
        timestamp: Date.now()
    });
}

Практический пример комплексного логирования

Полная система проверки регистрации

const validator = require('validator');

function log(data) {
    console.log(JSON.stringify(data, null, 2));
}

function validateRegistration(data) {
    const traceId = crypto.randomUUID();

    const checks = {
        email: validator.isEmail(data.email),
        password: validator.isLength(
            data.password,
            { min: 8 }
        ),
        age: validator.isInt(
            String(data.age),
            { min: 18 }
        )
    };

    log({
        traceId,
        checks,
        timestamp: new Date().toISOString()
    });

    return Object.values(checks)
        .every(Boolean);
}

validateRegistration({
    email: 'admin@test.com',
    password: 'secret123',
    age: 25
});

Типичные ошибки при логировании

Логирование секретных данных

console.log(req.body.password);

Ошибка безопасности.


Чрезмерное количество логов

console.log('Validation step 1');
console.log('Validation step 2');
console.log('Validation step 3');

Создаёт шум и затрудняет анализ.


Отсутствие структуры

console.log('Error');

Не содержит полезной информации.


Отсутствие временных меток

Без timestamp сложно анализировать последовательность событий.


Рекомендации по организации логирования

Лучшие практики

  • использовать JSON-формат;
  • разделять уровни логов;
  • не хранить чувствительные данные;
  • использовать trace ID;
  • выносить логирование в отдельный модуль;
  • ограничивать debug-логи в production;
  • хранить единый формат сообщений;
  • добавлять timestamp;
  • фиксировать имя валидатора;
  • логировать только значимые события.

Архитектура системы логирования

Рекомендуемая структура проекта

project/
├── validators/
├── middleware/
├── services/
├── logs/
├── logger/
│   ├── index.js
│   ├── validationLogger.js
│   └── errorLogger.js

Пример специализированного validation logger

class ValidationLogger {
    static log(field, validatorName, value, result) {
        console.log({
            type: 'validation',
            field,
            validator: validatorName,
            value,
            result,
            timestamp: Date.now()
        });
    }

    static error(field, value) {
        console.error({
            type: 'validation_error',
            field,
            value
        });
    }
}

Использование класса

const valid = validator.isEmail(email);

ValidationLogger.log(
    'email',
    'isEmail',
    email,
    valid
);