Node.js 日志与错误处理
良好的日志和错误处理机制是应用稳定性的基础。
日志级别
标准日志级别
| 级别 | 说明 | 场景 |
|---|---|---|
| error | 错误 | 异常、失败 |
| warn | 警告 | 潜在问题 |
| info | 信息 | 重要事件 |
| debug | 调试 | 详细信息 |
| trace | 追踪 | 最详细信息 |
console 日志
基本方法
JavaScript
console.log('普通日志');
console.info('信息日志');
console.warn('警告日志');
console.error('错误日志');
console.debug('调试日志');
结构化日志
JavaScript
// 格式化输出
console.log(JSON.stringify({
level: 'info',
message: '用户登录',
userId: '12345',
timestamp: new Date().toISOString()
}));
错误对象
Error 类型
JavaScript
// 基本错误
const err = new Error('发生错误');
console.log(err.message); // 错误消息
console.log(err.stack); // 堆栈追踪
// 内置错误类型
const typeErr = new TypeError('类型错误');
const rangeErr = new RangeError('范围错误');
const refErr = new ReferenceError('引用错误');
const syntaxErr = new SyntaxError('语法错误');
自定义错误
JavaScript
class AppError extends Error {
constructor(message, code) {
super(message);
this.name = 'AppError';
this.code = code;
this.timestamp = new Date().toISOString();
}
}
throw new AppError('用户不存在', 404);
同步错误处理
try-catch
JavaScript
function divide(a, b) {
if (b === 0) {
throw new Error('除数不能为零');
}
return a / b;
}
try {
const result = divide(10, 0);
} catch (err) {
console.error('捕获错误:', err.message);
} finally {
console.log('清理资源');
}
异步错误处理
Promise 错误
JavaScript
// catch 捕获
fetch('api/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error('请求失败:', err.message));
// async/await
async function fetchData() {
try {
const res = await fetch('api/data');
const data = await res.json();
return data;
} catch (err) {
console.error('获取数据失败:', err.message);
throw err;
}
}
全局错误处理
未捕获异常
JavaScript
process.on('uncaughtException', (err, origin) => {
console.error('未捕获异常:', err.message);
console.error('来源:', origin);
// 记录日志后退出
process.exit(1);
});
未处理的 Promise 拒绝
JavaScript
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的 Promise 拒绝:', reason);
});
// 正确做法:始终处理 Promise 拒绝
asyncFunction().catch(err => {
console.error('处理错误:', err);
});
日志框架
使用 winston
JavaScript
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: 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('服务启动', { port: 3000 });
logger.error('数据库连接失败', { error: err.message });
使用 pino
JavaScript
const pino = require('pino');
const logger = pino({
level: 'info',
transport: {
target: 'pino-pretty'
}
});
logger.info('用户登录');
logger.error({ err }, '请求处理失败');
错误处理中间件
Express 错误处理
JavaScript
// 错误处理中间件(必须放在最后)
app.use((err, req, res, next) => {
console.error('错误:', err.message);
res.status(err.status || 500).json({
error: {
message: err.message,
code: err.code
}
});
});
// 调用 next(err) 触发
app.get('/user/:id', (req, res, next) => {
const user = findUser(req.params.id);
if (!user) {
return next(new Error('用户不存在'));
}
res.json(user);
});
注意事项
- 生产环境使用专业日志框架(winston、pino)
- 不要吞掉错误,确保所有错误都被处理或记录
uncaughtException后应退出进程- 日志避免包含敏感信息(密码、令牌)
要点总结
- 使用日志级别区分信息重要性
try-catch处理同步错误- Promise 使用
.catch()或try/await process.on('uncaughtException')捕获全局异常- Express 错误中间件统一处理请求错误
📝 发现内容有误?点击此处直接编辑