feat: 添加全局错误处理机制
- 添加 unhandledRejection 处理器捕获未处理的 Promise rejection - 添加 uncaughtException 处理器捕获未捕获的异常 - 添加 SIGTERM/SIGINT 信号处理实现优雅关闭 - 实现 gracefulShutdown 函数,给正在处理的请求3秒完成时间 - 错误信息经过 sanitizeForLog 脱敏处理 - 生产环境下隐藏堆栈信息 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
96
server.js
96
server.js
@@ -7,6 +7,102 @@ import { initializeUserAgentUpdater } from './user-agent-updater.js';
|
||||
import './sls-logger.js'; // 初始化阿里云日志服务
|
||||
import { sanitizeForLog } from './log-sanitizer.js';
|
||||
|
||||
// ============================================================================
|
||||
// 全局错误处理 - 必须在应用启动前注册
|
||||
// ============================================================================
|
||||
|
||||
let isShuttingDown = false;
|
||||
|
||||
/**
|
||||
* 优雅关闭服务器
|
||||
*/
|
||||
function gracefulShutdown(reason, exitCode = 1) {
|
||||
if (isShuttingDown) {
|
||||
return;
|
||||
}
|
||||
isShuttingDown = true;
|
||||
|
||||
console.error(`\n${'='.repeat(80)}`);
|
||||
console.error(`🛑 Server shutting down: ${reason}`);
|
||||
console.error(`${'='.repeat(80)}\n`);
|
||||
|
||||
// 给正在处理的请求一些时间完成
|
||||
setTimeout(() => {
|
||||
process.exit(exitCode);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理未捕获的 Promise Rejection
|
||||
*/
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
const errorInfo = {
|
||||
type: 'unhandledRejection',
|
||||
reason: reason instanceof Error ? reason.message : String(reason),
|
||||
stack: reason instanceof Error ? reason.stack : undefined,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
console.error(`\n${'='.repeat(80)}`);
|
||||
console.error('⚠️ Unhandled Promise Rejection');
|
||||
console.error('='.repeat(80));
|
||||
console.error(`Time: ${errorInfo.timestamp}`);
|
||||
console.error(`Reason: ${errorInfo.reason}`);
|
||||
if (errorInfo.stack && isDevMode()) {
|
||||
console.error(`Stack: ${errorInfo.stack}`);
|
||||
}
|
||||
console.error('='.repeat(80) + '\n');
|
||||
|
||||
logError('Unhandled Promise Rejection', sanitizeForLog(errorInfo));
|
||||
|
||||
// 在生产环境中,unhandledRejection 可能表示严重问题,考虑退出
|
||||
// 但为了服务稳定性,这里只记录不退出
|
||||
// 如需更严格的处理,可取消下面的注释:
|
||||
// gracefulShutdown('Unhandled Promise Rejection', 1);
|
||||
});
|
||||
|
||||
/**
|
||||
* 处理未捕获的异常
|
||||
*/
|
||||
process.on('uncaughtException', (error) => {
|
||||
const errorInfo = {
|
||||
type: 'uncaughtException',
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
console.error(`\n${'='.repeat(80)}`);
|
||||
console.error('💥 Uncaught Exception');
|
||||
console.error('='.repeat(80));
|
||||
console.error(`Time: ${errorInfo.timestamp}`);
|
||||
console.error(`Error: ${errorInfo.message}`);
|
||||
if (errorInfo.stack) {
|
||||
console.error(`Stack: ${errorInfo.stack}`);
|
||||
}
|
||||
console.error('='.repeat(80) + '\n');
|
||||
|
||||
logError('Uncaught Exception', sanitizeForLog(errorInfo));
|
||||
|
||||
// uncaughtException 后进程状态不确定,必须退出
|
||||
gracefulShutdown('Uncaught Exception', 1);
|
||||
});
|
||||
|
||||
/**
|
||||
* 处理进程信号
|
||||
*/
|
||||
process.on('SIGTERM', () => {
|
||||
logInfo('Received SIGTERM signal');
|
||||
gracefulShutdown('SIGTERM', 0);
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
logInfo('Received SIGINT signal');
|
||||
gracefulShutdown('SIGINT', 0);
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(express.json({ limit: '50mb' }));
|
||||
|
||||
Reference in New Issue
Block a user