Node.js日志记录最佳实践是什么
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
会自动使用异步写入,确保日志记录不影响请求处理性能。
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: Node.js日志记录最佳实践是什么
本文地址: https://pptw.com/jishu/724785.html