Что такое Winston и зачем он нужен?
Winston — это мощная и гибкая библиотека для логирования в среде Node.js. Она была создана Charlie Robbins и является одной из самых популярных библиотек для логирования в экосистеме JavaScript. Winston позволяет разработчикам записывать сообщения с различными уровнями важности (info, warn, error, debug и т.д.), настраивать форматы вывода (JSON, простой текст, с метками времени), а также направлять логи в разные места (консоль, файлы, базы данных, внешние сервисы).
Зачем нужен Winston? В любом серьёзном приложении логирование — это не роскошь, а необходимость. Оно помогает:
- Отлаживать приложение: Логи позволяют понять, что происходило в системе в момент ошибки.
- Мониторить производительность: Можно записывать время выполнения запросов и выявлять узкие места.
- Анализировать поведение пользователей: Логи помогают понять, как пользователи взаимодействуют с приложением.
- Сохранять историю: Все события можно хранить в файлах или базах данных для последующего анализа.
- Обеспечивать безопасность: Логи могут фиксировать подозрительные действия и попытки взлома.
Winston решает эти задачи элегантно и эффективно, предоставляя единый интерфейс для работы с логами.
Установка Winston
Установка Winston осуществляется через менеджер пакетов npm. Откройте терминал в корневой папке вашего проекта и выполните команду:
npm install winston
Если вы используете yarn:
yarn add winston
После установки вы можете импортировать Winston в свой проект:
const winston = require('winston');
Для проектов, использующих ES-модули (type: "module" в package.json):
import winston from 'winston';
Быстрый старт с примером кода
Давайте создадим простой логгер, который выводит сообщения в консоль и в файл:
// logger.js
const winston = require('winston');
// Создаём логгер с двумя транспортами: консоль и файл
const logger = winston.createLogger({
level: 'info', // Минимальный уровень логирования
format: winston.format.json(), // Формат вывода — JSON
transports: [
new winston.transports.Console(), // Вывод в консоль
new winston.transports.File({ filename: 'combined.log' }) // Запись в файл
]
});
// Используем логгер
logger.info('Приложение запущено');
logger.warn('Предупреждение: низкий уровень памяти');
logger.error('Ошибка подключения к базе данных');
console.log('Логи записаны. Проверьте файл combined.log');
После запуска этого кода вы увидите логи в консоли, а также найдёте файл combined.log в корневой папке проекта.
Основные методы и функции Winston
Winston предоставляет богатый API. Рассмотрим 10 ключевых методов и функций с примерами.
1. winston.createLogger(options)
Что делает: Создаёт новый экземпляр логгера с заданной конфигурацией. Это основной способ создания логгера.
Параметры:
options.level(string): Минимальный уровень логирования (например, 'info', 'debug', 'error').options.format(Format): Формат вывода логов (например, winston.format.json(), winston.format.simple()).options.transports(Transport[]): Массив транспортов, определяющих, куда направлять логи.options.defaultMeta(Object): Метаданные, которые будут добавляться к каждому сообщению.options.exitOnError(boolean): Если true, процесс завершится при ошибке логирования (по умолчанию true).
Пример:
const logger = winston.createLogger({
level: 'debug',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
defaultMeta: { service: 'user-service' },
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' })
]
});
2. logger.log(level, message, meta)
Что делает: Универсальный метод для записи лога с указанным уровнем.
Параметры:
level(string): Уровень логирования ('error', 'warn', 'info', 'verbose', 'debug', 'silly').message(string): Текст сообщения.meta(Object, optional): Дополнительные метаданные (объект).
Пример:
logger.log('info', 'Запрос обработан', { userId: 123, duration: 45 });
3. logger.info(message, meta)
Что делает: Записывает лог с уровнем 'info'. Используется для стандартных информационных сообщений.
Параметры:
message(string): Текст сообщения.meta(Object, optional): Дополнительные метаданные.
Пример:
logger.info('Сервер запущен на порту 3000');
4. logger.error(message, meta)
Что делает: Записывает лог с уровнем 'error'. Используется для критических ошибок.
Параметры:
message(string): Текст сообщения об ошибке.meta(Object, optional): Дополнительные метаданные (часто передают объект ошибки).
Пример:
try {
// какой-то код
} catch (err) {
logger.error('Ошибка при обработке запроса', { error: err.message, stack: err.stack });
}
5. logger.warn(message, meta)
Что делает: Записывает лог с уровнем 'warn'. Используется для предупреждений.
Параметры:
message(string): Текст предупреждения.meta(Object, optional): Дополнительные метаданные.
Пример:
logger.warn('Дисковое пространство заканчивается', { freeSpace: '500MB' });
6. logger.debug(message, meta)
Что делает: Записывает лог с уровнем 'debug'. Используется для отладочной информации, которая обычно не нужна в продакшене.
Параметры:
message(string): Отладочное сообщение.meta(Object, optional): Дополнительные метаданные.
Пример:
logger.debug('Параметры запроса: %j', req.query);
7. logger.child(options)
Что делает: Создаёт дочерний логгер, который наследует конфигурацию родительского, но может добавлять свои метаданные или переопределять настройки.
Параметры:
options(Object): Объект с настройками для дочернего логгера (например, defaultMeta).
Пример:
const childLogger = logger.child({ requestId: 'abc-123' });
childLogger.info('Обработка запроса начата');
// В логе будет поле requestId: 'abc-123'
8. winston.format.combine(formats)
Что делает: Комбинирует несколько форматов в один. Позволяет применять несколько преобразований к сообщению.
Параметры:
formats(Format[]): Массив форматов, которые нужно применить последовательно.
Пример:
const myFormat = winston.format.combine(
winston.format.timestamp(),
winston.format.colorize(),
winston.format.printf(({ timestamp, level, message, ...meta }) => {
return `${timestamp} [${level}]: ${message} ${Object.keys(meta).length ? JSON.stringify(meta) : ''}`;
})
);
9. winston.transports.File(options)
Что делает: Создаёт транспорт для записи логов в файл. Это один из самых часто используемых транспортов.
Параметры:
options.filename(string): Имя файла для записи логов.options.level(string, optional): Уровень логирования для этого транспорта.options.maxsize(number, optional): Максимальный размер файла в байтах. При превышении создаётся новый файл.options.maxFiles(number, optional): Максимальное количество файлов для ротации.options.tailable(boolean, optional): Если true, при ротации файлы будут переименовываться, а не удаляться.
Пример:
const fileTransport = new winston.transports.File({
filename: 'logs/app.log',
maxsize: 5242880, // 5MB
maxFiles: 5,
tailable: true
});
10. winston.addColors(colors)
Что делает: Добавляет пользовательские цвета для уровней логирования при использовании формата colorize.
Параметры:
colors(Object): Объект, где ключи — уровни логирования, а значения — цвета (строки).
Пример:
winston.addColors({
error: 'red',
warn: 'yellow',
info: 'green',
debug: 'blue'
});
const logger = winston.createLogger({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
),
transports: [new winston.transports.Console()]
});
Реальный практический пример использования
Давайте создадим полноценный логгер для веб-приложения на Express.js. Он будет записывать все HTTP-запросы, ошибки и предупреждения.
// logger.js
const winston = require('winston');
const path = require('path');
// Настраиваем форматы
const consoleFormat = winston.format.combine(
winston.format.colorize(),
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.printf(({ timestamp, level, message, ...meta }) => {
return `${timestamp} [${level}]: ${message} ${Object.keys(meta).length ? JSON.stringify(meta) : ''}`;
})
);
const fileFormat = winston.format.combine(
winston.format.timestamp(),
winston.format.json()
);
// Создаём логгер
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
transports: [
new winston.transports.Console({ format: consoleFormat }),
new winston.transports.File({
filename: path.join(__dirname, 'logs', 'error.log'),
level: 'error',
format: fileFormat,
maxsize: 5242880,
maxFiles: 5
}),
new winston.transports.File({
filename: path.join(__dirname, 'logs', 'combined.log'),
format: fileFormat,
maxsize: 5242880,
maxFiles: 10
})
],
exceptionHandlers: [
new winston