1. 选择合适的日志库
根据应用场景(如性能需求、功能复杂度)选择日志库是基础。常用库包括:
- Winston:功能全面,支持多传输(控制台、文件、HTTP等)、自定义格式和日志级别,适合需要高度定制的项目;
- Pino:以高性能著称(异步写入、低CPU/内存占用),输出结构化JSON日志,适合高吞吐量应用;
- Morgan:专为Express设计,用于记录HTTP请求详情(如方法、URL、状态码),是API服务的常用中间件;
- Bunyan:结构化JSON日志输出,支持日志过滤和格式化,适合需要跨环境一致日志的场景。
2. 配置环境适配的日志级别
根据环境调整日志详细程度,避免不必要的性能消耗:
- 开发环境:使用
debug或verbose级别,记录函数入口/出口、变量值等详细信息,便于调试; - 测试环境:使用
info或warn级别,记录关键业务流程和潜在问题; - 生产环境:使用
warn或error级别,仅记录错误和重要警告,减少日志体积和IO压力。
可通过process.env.NODE_ENV动态设置,例如:
const level = process.env.NODE_ENV === 'production' ? 'warn' : 'debug';
3. 使用结构化日志(JSON格式)
结构化日志(如JSON)便于后续解析、分析和可视化,是现代日志管理的核心要求。相比纯文本,JSON日志可通过工具(如ELK、Grafana)快速提取字段(如event、userId、timestamp),支持聚合和过滤。示例如下:
logger.info({
event: 'user_login',
userId: '12345',
username: 'john_doe',
ip: '192.168.1.1',
userAgent: 'Chrome/120.0.0.0'
});
4. 实现日志轮转(避免文件过大)
当日志文件过大时,需通过轮转分割文件,防止磁盘空间耗尽。可使用winston-daily-rotate-file等工具,配置如下:
const DailyRotateFile = require('winston-daily-rotate-file');
const transport = new DailyRotateFile({
filename: 'application-%DATE%.log', // 按日期命名(如application-2025-10-12.log)
datePattern: 'YYYY-MM-DD', // 日期格式
zippedArchive: true, // 压缩旧日志
maxSize: '20m', // 单个文件最大20MB
maxFiles: '14d' // 保留14天内的日志
});
const logger = winston.createLogger({
transports: [transport]
});
5. 敏感信息脱敏
日志中避免记录用户密码、信用卡号、身份证号等敏感数据,防止泄露。可通过以下方式处理:
- 使用
sanitize-html、lodash.omit等工具过滤请求体中的敏感字段; - 在日志记录前对敏感数据进行替换(如用
*****代替密码)。示例如下:
const sanitize = require('sanitize-html');
app.post('/login', (req, res) => {
const sanitizedBody = sanitize(req.body, {
allowedTags: [],
allowedAttributes: {}
});
logger.info('Login attempt:', { userId: req.body.userId, password: '*****' });
});
6. 集成监控与报警
将日志与监控系统(如Prometheus、Grafana、Sentry)集成,实现实时监控和异常报警:
- Prometheus+Grafana:通过日志指标(如错误率、请求延迟)监控应用性能,设置阈值报警;
- Sentry:捕获并上报未处理的异常,提供错误堆栈和上下文信息,帮助快速定位问题。示例如下:
const Sentry = require('@sentry/node');
Sentry.init({ dsn: 'your-sentry-dsn' });
app.use((err, req, res, next) => {
logger.error('Unhandled error:', { error: err.message, stack: err.stack });
Sentry.captureException(err); // 上报到Sentry
res.status(500).send('Internal Server Error');
});
7. 全链路日志追踪(Request ID)
在微服务或分布式系统中,使用唯一requestId标记每个请求,跟踪其在整个链路中的流动(如从API网关到数据库)。通过requestId可快速关联不同服务的日志,定位跨服务问题。示例如下:
const { v4: uuidv4 } = require('uuid');
app.use((req, res, next) => {
const requestId = req.headers['x-request-id'] || uuidv4(); // 获取或生成requestId
req.requestId = requestId;
logger.info(`Request started: ${requestId}`, { method: req.method, url: req.url });
res.on('finish', () => {
logger.info(`Request completed: ${requestId}`, { status: res.statusCode });
});
next();
});
8. 异步日志记录(避免阻塞主线程)
日志写入是IO密集型操作,应使用异步方式避免阻塞应用主线程。多数日志库(如Winston、Pino)默认支持异步,例如Winston的transports.File会自动使用异步写入,确保日志记录不影响请求处理性能。
以上就是关于“Node.js日志记录最佳实践是什么”的相关介绍,筋斗云是国内较早的云主机应用的服务商,拥有10余年行业经验,提供丰富的云服务器、租用服务器等相关产品服务。云服务器资源弹性伸缩,主机vCPU、内存性能强悍、超高I/O速度、故障秒级恢复;电子化备案,提交快速,专业团队7×24小时服务支持!
简单好用、高性价比云服务器租用链接:https://www.jindouyun.cn/product/cvm