Логирование в JavaScript приложениях: лучшие практики на Node.js

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Логирование в JavaScript приложениях: лучшие практики на практике

Логирование — это не просто запись сообщений в консоль. В production-среде грамотно настроенное логирование становится главным инструментом мониторинга, отладки и аудита. Без него вы работаете вслепую. В этой статье мы разберем лучшие практики логирования для JavaScript-приложений на Node.js, от выбора библиотеки до настройки ротации файлов. Все примеры кода готовы к использованию.

1. Почему console.log — это плохо?

На этапе разработки мы все используем console.log. Это быстро и удобно. Но в продакшене такой подход опасен:

  • Нет уровней логирования. Вы не сможете отфильтровать ошибки от информационных сообщений.
  • Синхронная блокировка. Стандартный console.log может замедлить приложение при большом потоке запросов.
  • Отсутствие структуры. JSON-логи невозможно парсить без дополнительных костылей.
  • Нет ротации. Лог-файл будет расти до бесконечности, пока не заполнит диск.

Вместо этого используйте профессиональные библиотеки. Две самые популярные в Node.js — Winston и Pino.

2. Выбор библиотеки: Winston vs Pino

Обе библиотеки решают одну задачу, но имеют разные приоритеты.

Winston — универсальный и гибкий

Winston — это «швейцарский нож» логирования. Он поддерживает множество транспортов (файлы, консоль, HTTP, базы данных) и легко настраивается.

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
  ],
});

// В разработке добавляем вывод в консоль
if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple(),
  }));
}

logger.info('Сервер запущен на порту 3000');
logger.error('Ошибка подключения к БД', { db: 'postgres' });

Pino — скорость прежде всего

Pino позиционируется как самый быстрый логгер для Node.js. Он на 5-6x быстрее Winston благодаря использованию sonic-boom (асинхронная запись). Идеален для high-load проектов.

const pino = require('pino');

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  transport: {
    target: 'pino/file',
    options: { destination: './app.log' },
  },
});

logger.info({ user: 'admin' }, 'Пользователь вошел в систему');
logger.error({ err: new Error('Timeout') }, 'Запрос превысил лимит');

Совет: Для микросервисов и API с высокой нагрузкой выбирайте Pino. Для сложных конфигураций с несколькими транспортами — Winston.

3. Уровни логирования: не пишите всё в info

Используйте стандартную шкалу уровней (по возрастанию важности):

  • trace / debug — только для разработки. Детальная отладка.
  • info — нормальная работа приложения: запуск, успешные запросы, бизнес-события.
  • warn — потенциальные проблемы: медленные запросы, нехватка памяти, устаревшие методы.
  • error — исключения, сбои, падения. Должны быть всегда в отдельном файле или системе мониторинга.
  • fatal — критические ошибки, после которых приложение не может продолжить работу.

Настройте переменную окружения LOG_LEVEL, чтобы менять уровень фильтрации без перезапуска кода:

// Пример с Winston
const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  // ...
});

// В production: LOG_LEVEL=warn
// В development: LOG_LEVEL=debug

4. Структурированные логи: JSON вместо текста

Текстовые логи вида [INFO] User logged in — это прошлый век. Современные системы (ELK, Splunk, Grafana) ожидают JSON. Структурированные логи позволяют фильтровать по полям, строить дашборды и искать ошибки за секунды.

// Плохо
logger.info('Payment processed for user ' + userId + ' amount ' + amount);

// Хорошо
logger.info({
  event: 'payment.processed',
  userId: userId,
  amount: amount,
  currency: 'USD',
  timestamp: new Date().toISOString()
}, 'Payment processed');

Добавляйте обязательные поля в каждый лог: requestId (для трейсинга), serviceName, environment. Это спасет при дебаге распределенных систем.

5. Ротация логов и управление дисковым пространством

Если вы пишете логи в файлы, без ротации они займут весь диск. Используйте модуль winston-daily-rotate-file для Winston или встроенные возможности Pino.

// Winston + ротация
const DailyRotateFile = require('winston-daily-rotate-file');

const transport = new DailyRotateFile({
  filename: 'logs/application-%DATE%.log',
  datePattern: 'YYYY-MM-DD',
  maxSize: '20m',     // Максимальный размер файла
  maxFiles: '14d',    // Хранить логи 14 дней
  zippedArchive: true // Сжимать старые файлы
});

logger.add(transport);

Для Pino используйте pino-roll или настройте ротацию через систему (logrotate в Linux).

6. Безопасность: никогда не логируйте секреты

Пароли, токены, номера кредитных карт, персональные данные — всё это не должно попадать в логи. Один случайный console.log(req.body) может привести к утечке данных и штрафам по GDPR.

Используйте редикторы (redactors) — функции, которые заменяют чувствительные поля на звездочки:

// Пример для Pino
const logger = pino({
  redact: {
    paths: ['password', 'token', 'creditCard                

Блоги

Рекомендаций по книгам