14. Логирование

Логирование — запись событий приложения. Без логов ты слепой: не знаешь что происходит, не можешь дебажить production.
Принципы хорошего логирования
Заголовок раздела «Принципы хорошего логирования»❌ Плохо:console.log('here')console.log('user', user)console.log(JSON.stringify(data))
✅ Хорошо:logger.info('User logged in', { userId: user.id, ip: req.ip })logger.error('Payment failed', { orderId, error: err.message, stack: err.stack })Правила:
- Структурированные логи (JSON) — не строки
- Уровни важности: error, warn, info, debug
- Контекст: кто, что, когда, откуда
- Не логировать секреты
- Не логировать бесполезный шум
Winston — популярный логгер
Заголовок раздела «Winston — популярный логгер»npm install winstonimport winston from 'winston';
const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), process.env.NODE_ENV === 'production' ? winston.format.json() // JSON в production : winston.format.combine( // Красивый вывод в dev winston.format.colorize(), winston.format.simple() ) ),
transports: [ new winston.transports.Console(),
// В production — пишем в файлы ...(process.env.NODE_ENV === 'production' ? [ new winston.transports.File({ filename: 'logs/error.log', level: 'error', maxsize: 5 * 1024 * 1024, // 5MB maxFiles: 5, tailable: true, }), new winston.transports.File({ filename: 'logs/combined.log', maxsize: 10 * 1024 * 1024, // 10MB maxFiles: 5, tailable: true, }), ] : []), ],
// Не крашиться при необработанных исключениях exceptionHandlers: [ new winston.transports.File({ filename: 'logs/exceptions.log' }), ], rejectionHandlers: [ new winston.transports.File({ filename: 'logs/rejections.log' }), ],});
export default logger;Pino — быстрый логгер
Заголовок раздела «Pino — быстрый логгер»npm install pino pino-prettyimport pino from 'pino';
const logger = pino({ level: process.env.LOG_LEVEL || 'info',
transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty', options: { colorize: true, translateTime: 'HH:MM:ss', ignore: 'pid,hostname', }, } : undefined,
redact: { paths: ['password', 'token', 'authorization', '*.password'], remove: true, },});
export default logger;Child loggers и контекст
Заголовок раздела «Child loggers и контекст»import logger from './logger';
// HTTP middleware — добавляем requestId к каждому запросуapp.use((req, res, next) => { req.log = logger.child({ requestId: crypto.randomUUID(), method: req.method, url: req.url, ip: req.ip, }); next();});
// В обработчикеapp.post('/api/orders', async (req, res) => { const log = req.log.child({ userId: req.user?.id });
log.info('Creating order', { items: req.body.items?.length });
try { const order = await createOrder(req.body); log.info('Order created', { orderId: order.id, total: order.total }); res.json(order); } catch (error) { log.error('Order creation failed', { error: error.message, stack: error.stack }); res.status(500).json({ error: 'Internal server error' }); }});Уровни логирования
Заголовок раздела «Уровни логирования»// Когда использовать каждый уровень:
logger.error('Database connection failed', { error }); // Критичная ошибкаlogger.warn('Rate limit approaching', { current, limit }); // Предупреждениеlogger.info('User registered', { userId }); // Важное событиеlogger.debug('Cache hit', { key, ttl }); // Детали для дебагаlogger.trace('SQL query', { query, params }); // Очень детальные логи# Управление уровнем через envLOG_LEVEL=debug npm start # видим всёLOG_LEVEL=info npm start # только info и выше (production)LOG_LEVEL=warn npm start # только warn и errorExpress/Fastify HTTP логирование
Заголовок раздела «Express/Fastify HTTP логирование»npm install morgan # для Expressimport morgan from 'morgan';
// Формат для production — JSONconst format = process.env.NODE_ENV === 'production' ? (tokens: any, req: any, res: any) => JSON.stringify({ method: tokens.method(req, res), url: tokens.url(req, res), status: Number(tokens.status(req, res)), duration: Number(tokens['response-time'](req, res)), size: tokens.res(req, res, 'content-length'), }) : 'dev'; // красивый вывод для разработки
app.use(morgan(format, { stream: { write: (message: string) => logger.http(message.trim()), }, skip: (req) => req.url === '/health', // не логировать health checks}));Логирование в Next.js
Заголовок раздела «Логирование в Next.js»// middleware.ts — логируем каждый запросimport { NextRequest, NextResponse } from 'next/server';
export function middleware(request: NextRequest) { const start = Date.now(); const response = NextResponse.next();
response.headers.set('x-request-id', crypto.randomUUID());
// Логируем после обработки (через waitUntil в edge) console.log(JSON.stringify({ level: 'info', method: request.method, url: request.url, duration: Date.now() - start, timestamp: new Date().toISOString(), }));
return response;}Централизованные логи (ELK Stack)
Заголовок раздела «Централизованные логи (ELK Stack)»# docker-compose.yml — ELK Stack для productionservices: elasticsearch: image: elasticsearch:8.12.0 environment: - discovery.type=single-node - xpack.security.enabled=false volumes: - es_data:/usr/share/elasticsearch/data
logstash: image: logstash:8.12.0 volumes: - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
kibana: image: kibana:8.12.0 ports: - "5601:5601" environment: - ELASTICSEARCH_HOSTS=http://elasticsearch:9200Просмотр логов в production
Заголовок раздела «Просмотр логов в production»# Journalctl (systemd)sudo journalctl -u myapp -f # followsudo journalctl -u myapp -n 100 # последние 100 строкsudo journalctl -u myapp --since "1 hour ago"
# Docker логиdocker logs myapp -fdocker logs myapp --tail 100docker logs myapp --since 2h
# Docker Composedocker compose logs app -fdocker compose logs --since 30m
# PM2pm2 logs myapppm2 logs myapp --lines 200
# Поиск в логахsudo journalctl -u myapp | grep ERRORsudo journalctl -u myapp | jq 'select(.level == "error")'Ключевые моменты
Заголовок раздела «Ключевые моменты»- Структурированные JSON логи — лучше строк (поиск и анализ)
- Pino — быстрый, Winston — популярный, оба хороши
- Child loggers с requestId — для трассировки запроса через систему
- Не логируй секреты — используй
redactв pino или фильтры - Уровни: error > warn > info > debug > trace
- В production — info уровень, в dev — debug
- Централизованные логи (ELK, Loki+Grafana) — для серьёзных проектов
Интерактивный пример
Заголовок раздела «Интерактивный пример»Уровни логирования и фильтрация: