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

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

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

Начать курс

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

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

1. Выбор библиотеки для логирования

Встроенный console.log хорош только для разработки. Для production-среды необходимы специализированные библиотеки, которые предоставляют уровни логирования, форматирование, ротацию файлов и интеграцию с внешними системами.

Рассмотрим три популярных варианта:

  • Winston — самая популярная библиотека. Гибкая, с поддержкой множества транспортов (файлы, консоль, HTTP, базы данных). Идеальна для сложных систем.
  • Pino — сверхбыстрый логгер. Рекомендуется для высоконагруженных приложений, где производительность критична.
  • Bunyan — генерирует логи в формате JSON. Удобен для машинной обработки.

Пример настройки Winston:

const winston = require('winston');

const logger = winston.createLogger({
    level: 'info',
    format: winston.format.json(),
    defaultMeta: { service: 'user-service' },
    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('Сервис запущен');
logger.error('Ошибка подключения к БД', { db: 'main' });

2. Использование уровней логирования

Уровни логирования помогают фильтровать сообщения по важности. Стандартная иерархия (от наименее к наиболее критичному):

  • debug — детальная отладочная информация (только для разработки).
  • info — стандартные события (запрос обработан, пользователь авторизован).
  • warn — потенциальные проблемы (низкий уровень памяти, медленный запрос).
  • error — критические ошибки (падение модуля, исключение).

Правило: В production используйте уровень info или выше. debug должен быть отключен, чтобы не забивать диски и не снижать производительность.

Пример динамической смены уровня:

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

if (process.env.LOG_LEVEL) {
    logger.level = process.env.LOG_LEVEL;
}

logger.debug('Это сообщение не появится в production');
logger.info('Пользователь залогинился');
logger.error('Не удалось сохранить данные');

3. Структурированные логи (JSON)

Текстовые логи сложно парсить. Современная практика — использовать структурированные логи в формате JSON. Это позволяет легко интегрироваться с системами анализа (ELK, Splunk, Grafana Loki).

Что должно быть в каждом логе:

  • timestamp — точное время события (ISO 8601).
  • level — уровень логирования.
  • message — человекочитаемое описание.
  • context — дополнительные данные (ID запроса, имя пользователя, код ошибки).

Пример с Pino:

const pino = require('pino');

const logger = pino({
    level: 'info',
    timestamp: pino.stdTimeFunctions.isoTime,
    formatters: {
        level(label) {
            return { level: label };
        },
    },
});

logger.info({ userId: 123, action: 'login' }, 'Пользователь вошел в систему');
// Вывод: {"level":30,"time":"2024-01-15T10:30:00.000Z","pid":1234,"hostname":"server1","userId":123,"action":"login","msg":"Пользователь вошел в систему"}

4. Централизованный сбор и ротация логов

В production-среде логи должны собираться в одном месте. Это упрощает мониторинг и поиск ошибок. Основные подходы:

  • Ротация файлов — библиотека winston-daily-rotate-file автоматически архивирует старые логи.
  • Отправка в stdout — контейнеризированные приложения (Docker) должны писать логи в stdout/stderr, а система оркестрации (Kubernetes) сама собирает их.
  • Внешние сервисы — Logstash, Datadog, Sentry. Winston и Pino имеют транспорты для прямой отправки.

Пример ротации файлов с Winston:

const winston = require('winston');
require('winston-daily-rotate-file');

const transport = new winston.transports.DailyRotateFile({
    filename: 'logs/application-%DATE%.log',
    datePattern: 'YYYY-MM-DD',
    maxSize: '20m',
    maxFiles: '14d',
});

const logger = winston.createLogger({
    level: 'info',
    format: winston.format.json(),
    transports: [transport],
});

logger.info('Лог с ротацией');

5. Логирование HTTP запросов

Для веб-приложений критично логировать входящие запросы. Популярные middleware:

  • morgan — простой и быстрый логгер для Express. Работает в формате Apache.
  • pino-http — интеграция Pino с HTTP-запросами.

Пример с morgan и Winston:

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

const app = express();
const logger = winston.createLogger({
    transports: [new winston.transports.Console()],
});

// Создаем поток для morgan в Winston
const stream = {
    write: (message) => logger.info(message.trim()),
};

app.use(morgan('combined', { stream }));

app.get('/', (req, res) => {
    res.send('Hello World');
});

app.listen(3000);

6. Чего следует избегать

Ошибки, которые часто допускают разработчики:

Блоги

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