日志是应用程序运行过程中产生的记录,用于追踪事件、诊断问题、监控行为和审计。在生产环境中,日志是不可或缺的工具。本章将介绍 Node.js 中日志记录的最佳实践,包括选择合适的日志库、配置日志级别、结构化日志输出,以及与 Express 等框架集成。
日志的主要用途包括:
相比简单的 console.log,专业的日志库提供了日志级别、格式化、输出目标(文件、控制台、远程服务)等功能。
日志级别用于区分日志的重要性,常见的级别从低到高依次为:
通过设置日志级别,可以控制输出的信息量,例如生产环境通常只记录 warn 及以上级别。
社区中有多个成熟的日志库,各有特点。以下是最常用的三个:
Winston 是最受欢迎的日志库之一,功能丰富,支持多种传输(transport),包括控制台、文件、HTTP、数据库等。它具有可扩展的架构和强大的日志级别管理。
安装: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.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
logger.info('应用启动');
logger.error('发生错误');
Pino 以性能著称,非常适合对性能要求高的场景。它生成 JSON 格式的日志,并通过与传输(如 pino-pretty)结合提供可读性。
安装:npm install pino
const pino = require('pino');
const logger = pino({
level: 'info',
transport: {
target: 'pino-pretty',
options: {
colorize: true
}
}
});
logger.info('应用启动');
生产环境通常直接输出 JSON,由日志收集系统处理。
log4js 是 Apache log4j 的 JavaScript 移植,支持多 Appender(输出目标)和布局配置,适合习惯 Java 日志风格的开发者。
安装:npm install log4js
const log4js = require('log4js');
log4js.configure({
appenders: {
out: { type: 'stdout' },
app: { type: 'file', filename: 'application.log' }
},
categories: {
default: { appenders: ['out', 'app'], level: 'info' }
}
});
const logger = log4js.getLogger();
logger.info('应用启动');
将日志集成到 Express 中可以记录每个请求的详细信息,便于调试和监控。可以使用 morgan 中间件记录访问日志,并与自定义日志库结合。
const express = require('express');
const morgan = require('morgan');
const winston = require('winston');
const app = express();
// 创建 Winston 日志实例
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [new winston.transports.Console()]
});
// 使用 morgan 记录 HTTP 请求,输出到 winston
app.use(morgan('combined', {
stream: { write: message => logger.info(message.trim()) }
}));
app.get('/', (req, res) => {
logger.info('处理根路由');
res.send('Hello World');
});
app.listen(3000);
结构化日志(通常为 JSON)便于机器解析,可被日志聚合工具(如 ELK Stack、Splunk)高效索引和查询。建议生产环境使用 JSON 格式输出。
logger.info({ event: 'user_login', userId: 123, ip: req.ip });
Pino 默认输出 JSON,Winston 可通过配置实现。
如果不加管理,日志文件会无限增长,耗尽磁盘空间。日志轮转(log rotation)可以按大小或时间分割日志文件,并删除旧文件。对于 Winston,可以使用 winston-daily-rotate-file 传输。
npm install winston-daily-rotate-file
const DailyRotateFile = require('winston-daily-rotate-file');
const transport = new DailyRotateFile({
filename: 'application-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d'
});
const logger = winston.createLogger({
transports: [transport]
});
对于直接文件输出的日志,也可使用操作系统工具如 logrotate 进行轮转。
在分布式系统或微服务架构中,需要将各服务的日志集中收集和分析。常见方案包括:
这些工具通常接收 JSON 格式日志,并提供强大的搜索和可视化能力。
以下示例结合了 Winston、morgan 和日志轮转,实现了结构化日志、请求记录和错误日志分离。
const express = require('express');
const morgan = require('morgan');
const winston = require('winston');
const DailyRotateFile = require('winston-daily-rotate-file');
const app = express();
// 配置 Winston
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
defaultMeta: { service: 'my-app' },
transports: [
new winston.transports.Console({
format: winston.format.simple()
}),
new DailyRotateFile({
filename: 'logs/error-%DATE%.log',
level: 'error',
datePattern: 'YYYY-MM-DD',
maxFiles: '30d'
}),
new DailyRotateFile({
filename: 'logs/combined-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxFiles: '30d'
})
]
});
// 将 morgan 的流指向 logger.info
const morganStream = {
write: (message) => logger.info(message.trim())
};
app.use(morgan('combined', { stream: morganStream }));
// 请求级别添加 requestId
app.use((req, res, next) => {
req.id = require('crypto').randomBytes(16).toString('hex');
res.setHeader('X-Request-ID', req.id);
next();
});
// 路由
app.get('/', (req, res) => {
logger.info({ reqId: req.id, msg: '处理根路由' });
res.send('Hello World');
});
// 错误处理中间件记录错误
app.use((err, req, res, next) => {
logger.error({ reqId: req.id, err: err.stack });
res.status(500).send('Something broke!');
});
app.listen(3000, () => {
logger.info('应用启动成功');
});
日志记录是应用可观测性的基石。通过选择合适的日志库、合理配置日志级别和输出,并遵循最佳实践,可以显著提高故障排查效率和应用稳定性。在生产环境中,建议结合集中式日志系统,将日志转化为可量化的监控数据。下一章我们将学习 Node.js 的性能优化技巧。