Node.jsМодуль 7: Тестирование и деплой
Логирование
Winston, Morgan, уровни логов
Цель урока
В этом уроке ты научишься:
- Настраивать Winston
- Логировать HTTP запросы с Morgan
- Структурировать логи
Winston
Установка
npm install winstonБазовая настройка
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
new winston.transports.File({ filename: 'logs/combined.log' })
]
});
// В development добавляем консоль
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
}));
}
module.exports = logger;Использование
const logger = require('./logger');
logger.error('Ошибка!', { error: err.message });
logger.warn('Предупреждение');
logger.info('Информация', { userId: 123 });
logger.debug('Отладка');Уровни логов
| Уровень | Приоритет | Использование |
|---|---|---|
| error | 0 | Ошибки |
| warn | 1 | Предупреждения |
| info | 2 | Важная информация |
| http | 3 | HTTP запросы |
| verbose | 4 | Подробности |
| debug | 5 | Отладка |
| silly | 6 | Всё подряд |
Morgan (HTTP логи)
Установка
npm install morganИнтеграция с Winston
const morgan = require('morgan');
const logger = require('./logger');
// Создаём stream для Winston
const stream = {
write: (message) => logger.http(message.trim())
};
// Middleware
app.use(morgan('combined', { stream }));
// Или кастомный формат
app.use(morgan(':method :url :status :response-time ms', { stream }));Продвинутая конфигурация
const winston = require('winston');
const path = require('path');
const logDir = 'logs';
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: { service: 'my-api' },
transports: [
// Ошибки в отдельный файл
new winston.transports.File({
filename: path.join(logDir, 'error.log'),
level: 'error',
maxsize: 5242880, // 5MB
maxFiles: 5
}),
// Все логи
new winston.transports.File({
filename: path.join(logDir, 'combined.log'),
maxsize: 5242880,
maxFiles: 5
})
],
exceptionHandlers: [
new winston.transports.File({
filename: path.join(logDir, 'exceptions.log')
})
],
rejectionHandlers: [
new winston.transports.File({
filename: path.join(logDir, 'rejections.log')
})
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.printf(({ level, message, timestamp, ...meta }) => {
return `${timestamp} [${level}]: ${message} ${
Object.keys(meta).length ? JSON.stringify(meta) : ''
}`;
})
)
}));
}
module.exports = logger;Логирование в middleware
// Middleware для логирования запросов
function requestLogger(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
logger.http('Request', {
method: req.method,
url: req.originalUrl,
status: res.statusCode,
duration: `${duration}ms`,
ip: req.ip,
userAgent: req.get('user-agent')
});
});
next();
}
app.use(requestLogger);Логирование ошибок
// Error handler с логированием
app.use((err, req, res, next) => {
logger.error('Error', {
message: err.message,
stack: err.stack,
path: req.path,
method: req.method,
body: req.body,
user: req.user?.id
});
res.status(err.status || 500).json({
error: process.env.NODE_ENV === 'production'
? 'Internal Server Error'
: err.message
});
});Практика
Задание: Логгер для API
Задача: Настрой логирование для Express API.
Решение:
// logger.js
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'logs/app.log' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
module.exports = logger;
// app.js
const logger = require('./logger');
const morgan = require('morgan');
app.use(morgan('combined', {
stream: { write: (msg) => logger.http(msg.trim()) }
}));Проверь себя
- Какие уровни логов существуют?
- Зачем нужен Morgan?
- Как логировать ошибки?