Трассировка вызовов в контексте Validator.js — это
процесс отслеживания последовательности выполнения проверок,
преобразований и пользовательских валидаторов. Такая техника
используется для:
При использовании Validator.js трассировка особенно
важна в крупных backend-проектах, где множество валидаторов вызываются
каскадно через:
Библиотека Validator.js предоставляет набор чистых
функций:
validator.isEmail(value)
validator.isURL(value)
validator.isUUID(value)
Каждая функция:
true или false.Простейшая цепочка вызова:
const validator = require('validator')
const result = validator.isEmail('admin@example.com')
console.log(result)
Трассировка здесь выглядит так:
main()
└── validator.isEmail()
├── assertString()
├── normalizeEmail()
└── regexp.test()
Самый распространённый способ трассировки — создание proxy-обёртки.
Пример:
const validator = require('validator')
function trace(fn, name) {
return function (...args) {
console.log(`[TRACE] ${name}`)
console.log(`[ARGS]`, args)
const result = fn(...args)
console.log(`[RESULT]`, result)
return result
}
}
const tracedIsEmail = trace(
validator.isEmail,
'validator.isEmail'
)
tracedIsEmail('admin@example.com')
Результат:
[TRACE] validator.isEmail
[ARGS] [ 'admin@example.com' ]
[RESULT] true
В больших приложениях вручную трассировать каждую функцию неудобно.
Подход с динамическим обходом:
const validator = require('validator')
const tracedValidator = {}
for (const key in validator) {
if (typeof validator[key] === 'function') {
tracedValidator[key] = (...args) => {
console.log(`[CALL] ${key}`)
console.log(`[INPUT]`, args)
const result = validator[key](...args)
console.log(`[OUTPUT]`, result)
return result
}
} else {
tracedValidator[key] = validator[key]
}
}
Использование:
tracedValidator.isEmail('root@example.com')
tracedValidator.isUUID('123')
Proxy позволяет динамически перехватывать обращения к
объекту.
Пример:
const validator = require('validator')
const tracedValidator = new Proxy(validator, {
get(target, prop) {
const original = target[prop]
if (typeof original !== 'function') {
return original
}
return (...args) => {
console.log(`[TRACE] ${prop}`)
console.log(`[ARGS]`, args)
const result = original(...args)
console.log(`[RETURN]`, result)
return result
}
}
})
Использование:
tracedValidator.isURL('https://google.com')
Преимущества:
Иногда требуется узнать, откуда был вызван валидатор.
Node.js позволяет получить стек:
function getStack() {
const err = new Error()
return err.stack
}
Интеграция:
const validator = require('validator')
function traced(fn, name) {
return (...args) => {
console.log(`[CALL] ${name}`)
console.log(getStack())
return fn(...args)
}
}
Результат:
Error
at getStack
at traced
at validateUser
at controller
Часто поверх Validator.js создаются собственные
проверки.
Пример:
const validator = require('validator')
function validateUserEmail(email) {
console.log('[VALIDATE USER EMAIL]')
if (!validator.isEmail(email)) {
throw new Error('Invalid email')
}
return true
}
Трассировка:
validateUserEmail()
└── validator.isEmail()
Реальные системы используют каскадную валидацию.
Пример:
function validatePassword(password) {
if (!validator.isLength(password, { min: 8 })) {
return false
}
if (!validator.matches(password, /[A-Z]/)) {
return false
}
if (!validator.matches(password, /\d/)) {
return false
}
return true
}
Трассировка:
validatePassword()
├── validator.isLength()
├── validator.matches()
└── validator.matches()
В Express можно централизовать трассировку.
Пример:
function validationLogger(req, res, next) {
console.log(`[REQUEST] ${req.method} ${req.url}`)
next()
}
Интеграция:
app.use(validationLogger)
Некоторые пользовательские валидаторы выбрасывают ошибки.
Пример:
function validateUUID(id) {
if (!validator.isUUID(id)) {
throw new Error('Invalid UUID')
}
return true
}
Трассировка:
try {
validateUUID('123')
} catch (err) {
console.error('[VALIDATION ERROR]')
console.error(err.message)
}
Validator.js синхронен, однако пользовательские
валидаторы часто асинхронны.
Пример:
async function validateUser(data) {
console.log('[START VALIDATION]')
const emailValid = validator.isEmail(data.email)
console.log('[EMAIL]', emailValid)
const exists = await database.findUser(data.email)
console.log('[USER EXISTS]', exists)
return !exists
}
Для оценки производительности используется
performance.now().
Пример:
const { performance } = require('perf_hooks')
function tracePerformance(fn, name) {
return (...args) => {
const start = performance.now()
const result = fn(...args)
const end = performance.now()
console.log(`${name}: ${end - start}ms`)
return result
}
}
Использование:
const tracedEmail = tracePerformance(
validator.isEmail,
'isEmail'
)
tracedEmail('admin@example.com')
Полезно логировать не только значения, но и типы.
Пример:
function debugArgs(args) {
args.forEach((arg, index) => {
console.log({
index,
value: arg,
type: typeof arg
})
})
}
Интеграция:
function traced(fn, name) {
return (...args) => {
console.log(name)
debugArgs(args)
return fn(...args)
}
}
Библиотека содержит санитайзеры:
validator.trim()
validator.escape()
validator.normalizeEmail()
Их также полезно трассировать.
Пример:
function traceSanitizer(fn, name) {
return (value) => {
console.log(`[BEFORE] ${value}`)
const result = fn(value)
console.log(`[AFTER] ${result}`)
return result
}
}
Пример:
const email = validator.normalizeEmail(
validator.trim(input)
)
Трассировка:
trim()
└── normalizeEmail()
Расширенный вариант:
function pipeline(value, ...fns) {
return fns.reduce((acc, fn) => {
console.log(`[PIPE] ${fn.name}`)
console.log(`[VALUE]`, acc)
return fn(acc)
}, value)
}
Использование:
pipeline(
' ADMIN@EXAMPLE.COM ',
validator.trim,
validator.normalizeEmail
)
В production-среде вместо console.log используют
логгеры.
Пример:
const winston = require('winston')
const logger = winston.createLogger({
transports: [
new winston.transports.Console()
]
})
Трассировка:
logger.info('Validation started')
Пример:
const pino = require('pino')
const logger = pino()
Использование:
logger.info({
validator: 'isEmail',
value: 'admin@example.com'
})
При обработке множества запросов важно связывать логи.
Пример:
const traceId = crypto.randomUUID()
Использование:
logger.info({
traceId,
validator: 'isEmail'
})
express-validator использует Validator.js
внутри себя.
Пример:
body('email')
.isEmail()
Трассировка middleware:
app.use((req, res, next) => {
console.log(`[BODY]`, req.body)
next()
})
Иногда используется динамическая замена функций.
Пример:
const original = validator.isEmail
validator.isEmail = function (...args) {
console.log('[PATCHED isEmail]')
return original(...args)
}
Недостатки:
Полезно включать трассировку только в development-режиме.
Пример:
const DEBUG = process.env.DEBUG === 'true'
Использование:
if (DEBUG) {
console.log('[TRACE]', args)
}
Типичная схема:
| Уровень | Назначение |
|---|---|
| error | ошибки |
| warn | подозрительные данные |
| info | обычные проверки |
| debug | подробная трассировка |
| trace | полный стек вызовов |
Пример:
logger.debug('Validation started')
Прямая сериализация может приводить к ошибкам циклических ссылок.
Небезопасный код:
JSON.stringify(obj)
Безопасный вариант:
const util = require('util')
console.log(
util.inspect(obj, {
depth: 5,
colors: true
})
)
Пример:
const schema = {
email: validator.isEmail,
uuid: validator.isUUID
}
Трассировка:
for (const field in schema) {
console.log(`[FIELD] ${field}`)
const valid = schema[field](data[field])
console.log(`[VALID] ${valid}`)
}
Встроенная функция:
console.trace('Validation trace')
Результат:
Trace: Validation trace
at validateUser
at controller
Пример:
const obj = {}
Error.captureStackTrace(obj)
console.log(obj.stack)
В распределённых системах применяется OpenTelemetry.
Пример концепции:
const span = tracer.startSpan('validator.isEmail')
Завершение:
span.end()
Такая интеграция позволяет:
Чрезмерное количество логов:
Опасно логировать:
Небезопасно:
console.log(password)
Безопаснее:
console.log('[PASSWORD REDACTED]')
Пример:
function maskEmail(email) {
return email.replace(/(.{2}).+(@.+)/, '$1***$2')
}
Результат:
ad***@example.com
const validator = require('validator')
function trace(fn, name) {
return (...args) => {
console.log('================')
console.log(`[CALL] ${name}`)
console.log(`[ARGS]`, args)
const start = performance.now()
try {
const result = fn(...args)
const end = performance.now()
console.log(`[RESULT]`, result)
console.log(`[TIME] ${end - start}ms`)
return result
} catch (err) {
console.error(`[ERROR]`, err.message)
throw err
}
}
}
const isEmail = trace(
validator.isEmail,
'validator.isEmail'
)
const isUUID = trace(
validator.isUUID,
'validator.isUUID'
)
isEmail('admin@example.com')
isUUID('123')
Предпочтительно иметь:
Трассировка не должна смешиваться с бизнес-логикой.
Плохой подход:
function validate(data) {
console.log(data)
return validator.isEmail(data.email)
}
Лучше:
const validate = tracedValidator.isEmail
Лучше логировать JSON-структуры:
logger.info({
validator: 'isEmail',
success: true
})
Это упрощает:
Пример:
function validateUser(user) {
return (
validator.isEmail(user.email) &&
validator.isUUID(user.id) &&
validator.isLength(user.password, {
min: 8
})
)
}
Трассировка позволяет быстро определить: