Node.js 错误处理
更新: 8/8/2025 字数: 0 字 时长: 0 分钟
概述
错误处理是 Node.js 应用开发中的关键环节,良好的错误处理机制能够提高应用的稳定性和用户体验。本章将详细介绍 Node.js 中的错误处理最佳实践。
错误类型
1. 同步错误
javascript
// sync-error-handler.js
class SyncErrorHandler {
constructor() {
this.errorTypes = {
VALIDATION_ERROR: 'ValidationError',
TYPE_ERROR: 'TypeError',
REFERENCE_ERROR: 'ReferenceError',
SYNTAX_ERROR: 'SyntaxError'
};
}
// Try-Catch 错误处理
handleSyncOperation(operation, data) {
try {
const result = operation(data);
return { success: true, data: result };
} catch (error) {
return this.handleError(error);
}
}
// 数据验证错误
validateUserData(userData) {
try {
if (!userData) {
throw new Error('User data is required');
}
if (!userData.email || !this.isValidEmail(userData.email)) {
throw new ValidationError('Invalid email address');
}
if (!userData.password || userData.password.length < 8) {
throw new ValidationError('Password must be at least 8 characters');
}
return { valid: true, data: userData };
} catch (error) {
return { valid: false, error: this.handleError(error) };
}
}
// JSON 解析错误
parseJSON(jsonString) {
try {
const data = JSON.parse(jsonString);
return { success: true, data };
} catch (error) {
if (error instanceof SyntaxError) {
return {
success: false,
error: {
type: 'JSON_PARSE_ERROR',
message: 'Invalid JSON format',
details: error.message
}
};
}
return this.handleError(error);
}
}
// 类型转换错误
convertToNumber(value) {
try {
if (value === null || value === undefined) {
throw new TypeError('Value cannot be null or undefined');
}
const number = Number(value);
if (isNaN(number)) {
throw new TypeError(`Cannot convert '${value}' to number`);
}
return { success: true, data: number };
} catch (error) {
return this.handleError(error);
}
}
handleError(error) {
const errorInfo = {
success: false,
error: {
name: error.name,
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
}
};
// 记录错误日志
console.error('Sync Error:', errorInfo);
return errorInfo;
}
isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
}
// 自定义错误类
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
this.code = 'VALIDATION_FAILED';
}
}
module.exports = { SyncErrorHandler, ValidationError };2. 异步错误
javascript
// async-error-handler.js
const fs = require('fs').promises;
const http = require('http');
const https = require('https');
class AsyncErrorHandler {
constructor() {
this.retryAttempts = 3;
this.retryDelay = 1000;
}
// Promise 错误处理
async handleAsyncOperation(operation, ...args) {
try {
const result = await operation(...args);
return { success: true, data: result };
} catch (error) {
return this.handleAsyncError(error);
}
}
// 文件操作错误处理
async readFileWithErrorHandling(filePath) {
try {
const data = await fs.readFile(filePath, 'utf8');
return { success: true, data };
} catch (error) {
switch (error.code) {
case 'ENOENT':
return {
success: false,
error: {
type: 'FILE_NOT_FOUND',
message: `File not found: ${filePath}`,
code: error.code
}
};
case 'EACCES':
return {
success: false,
error: {
type: 'PERMISSION_DENIED',
message: `Permission denied: ${filePath}`,
code: error.code
}
};
case 'EISDIR':
return {
success: false,
error: {
type: 'IS_DIRECTORY',
message: `Path is a directory: ${filePath}`,
code: error.code
}
};
default:
return this.handleAsyncError(error);
}
}
}
// HTTP 请求错误处理
async makeHttpRequest(url, options = {}) {
return new Promise((resolve, reject) => {
const protocol = url.startsWith('https:') ? https : http;
const req = protocol.request(url, options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve({
success: true,
data: {
statusCode: res.statusCode,
headers: res.headers,
body: data
}
});
} else {
resolve({
success: false,
error: {
type: 'HTTP_ERROR',
statusCode: res.statusCode,
message: `HTTP ${res.statusCode}: ${res.statusMessage}`,
body: data
}
});
}
});
});
req.on('error', (error) => {
resolve({
success: false,
error: {
type: 'REQUEST_ERROR',
message: error.message,
code: error.code
}
});
});
req.on('timeout', () => {
req.destroy();
resolve({
success: false,
error: {
type: 'TIMEOUT_ERROR',
message: 'Request timeout'
}
});
});
if (options.timeout) {
req.setTimeout(options.timeout);
}
req.end();
});
}
// 重试机制
async withRetry(operation, maxAttempts = this.retryAttempts) {
let lastError;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
const result = await operation();
return { success: true, data: result, attempt };
} catch (error) {
lastError = error;
console.warn(`Attempt ${attempt} failed:`, error.message);
if (attempt < maxAttempts) {
const delay = this.retryDelay * Math.pow(2, attempt - 1); // 指数退避
await this.sleep(delay);
}
}
}
return {
success: false,
error: {
type: 'MAX_RETRIES_EXCEEDED',
message: `Operation failed after ${maxAttempts} attempts`,
lastError: this.handleAsyncError(lastError).error,
attempts: maxAttempts
}
};
}
// 超时处理
async withTimeout(operation, timeoutMs) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Operation timed out after ${timeoutMs}ms`));
}, timeoutMs);
});
try {
const result = await Promise.race([operation(), timeoutPromise]);
return { success: true, data: result };
} catch (error) {
return this.handleAsyncError(error);
}
}
// 并发错误处理
async handleConcurrentOperations(operations) {
const results = await Promise.allSettled(operations);
const successful = [];
const failed = [];
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
successful.push({ index, data: result.value });
} else {
failed.push({
index,
error: this.handleAsyncError(result.reason).error
});
}
});
return {
successful,
failed,
totalCount: operations.length,
successCount: successful.length,
failureCount: failed.length
};
}
handleAsyncError(error) {
const errorInfo = {
success: false,
error: {
name: error.name,
message: error.message,
code: error.code,
stack: error.stack,
timestamp: new Date().toISOString()
}
};
// 记录错误日志
console.error('Async Error:', errorInfo);
return errorInfo;
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
module.exports = AsyncErrorHandler;3. 流错误处理
javascript
// stream-error-handler.js
const fs = require('fs');
const { Transform, Writable, Readable } = require('stream');
const { pipeline } = require('stream/promises');
class StreamErrorHandler {
constructor() {
this.errorHandlers = new Map();
}
// 文件流错误处理
createSafeFileStream(filePath, options = {}) {
const stream = fs.createReadStream(filePath, options);
stream.on('error', (error) => {
this.handleStreamError('file-read', error, { filePath });
});
return stream;
}
// Transform 流错误处理
createSafeTransformStream(transformFn) {
return new Transform({
objectMode: true,
transform(chunk, encoding, callback) {
try {
const result = transformFn(chunk);
callback(null, result);
} catch (error) {
this.emit('error', error);
}
}
});
}
// 写入流错误处理
createSafeWriteStream(filePath, options = {}) {
const stream = fs.createWriteStream(filePath, options);
stream.on('error', (error) => {
this.handleStreamError('file-write', error, { filePath });
});
return stream;
}
// Pipeline 错误处理
async safePipeline(...streams) {
try {
await pipeline(...streams);
return { success: true };
} catch (error) {
return {
success: false,
error: this.handleStreamError('pipeline', error, {
streamCount: streams.length
})
};
}
}
// 流数据处理错误恢复
createResilientProcessor(processFn, options = {}) {
const { skipOnError = true, maxErrors = 10 } = options;
let errorCount = 0;
return new Transform({
objectMode: true,
transform(chunk, encoding, callback) {
try {
const result = processFn(chunk);
callback(null, result);
} catch (error) {
errorCount++;
console.error(`Processing error ${errorCount}:`, error.message);
if (errorCount >= maxErrors) {
return callback(new Error(`Too many errors: ${errorCount}`));
}
if (skipOnError) {
// 跳过错误数据,继续处理
callback();
} else {
callback(error);
}
}
}
});
}
// 流监控和错误统计
createMonitoredStream(stream, streamName) {
const stats = {
bytesProcessed: 0,
chunksProcessed: 0,
errors: 0,
startTime: Date.now()
};
stream.on('data', (chunk) => {
stats.chunksProcessed++;
stats.bytesProcessed += chunk.length || 0;
});
stream.on('error', (error) => {
stats.errors++;
this.handleStreamError(streamName, error, stats);
});
stream.on('end', () => {
const duration = Date.now() - stats.startTime;
console.log(`Stream ${streamName} completed:`, {
...stats,
duration: `${duration}ms`,
throughput: `${(stats.bytesProcessed / duration * 1000).toFixed(2)} bytes/sec`
});
});
return stream;
}
// 批处理流错误处理
createBatchProcessor(batchSize, processBatch) {
let batch = [];
return new Transform({
objectMode: true,
transform(chunk, encoding, callback) {
batch.push(chunk);
if (batch.length >= batchSize) {
this.processBatch(batch, callback);
batch = [];
} else {
callback();
}
},
flush(callback) {
if (batch.length > 0) {
this.processBatch(batch, callback);
} else {
callback();
}
},
processBatch(items, callback) {
try {
const results = processBatch(items);
results.forEach(result => this.push(result));
callback();
} catch (error) {
callback(error);
}
}
});
}
handleStreamError(streamType, error, context = {}) {
const errorInfo = {
type: 'STREAM_ERROR',
streamType,
error: {
name: error.name,
message: error.message,
code: error.code,
stack: error.stack
},
context,
timestamp: new Date().toISOString()
};
console.error('Stream Error:', errorInfo);
// 触发错误处理器
const handler = this.errorHandlers.get(streamType);
if (handler) {
handler(errorInfo);
}
return errorInfo;
}
// 注册错误处理器
registerErrorHandler(streamType, handler) {
this.errorHandlers.set(streamType, handler);
}
// 创建错误恢复流
createRecoveryStream(options = {}) {
const { maxRetries = 3, retryDelay = 1000 } = options;
return new Transform({
objectMode: true,
async transform(chunk, encoding, callback) {
let retries = 0;
while (retries <= maxRetries) {
try {
// 处理数据
const result = await this.processChunk(chunk);
return callback(null, result);
} catch (error) {
retries++;
if (retries > maxRetries) {
return callback(error);
}
console.warn(`Retry ${retries} for chunk processing:`, error.message);
await new Promise(resolve => setTimeout(resolve, retryDelay));
}
}
},
async processChunk(chunk) {
// 实际的数据处理逻辑
return chunk;
}
});
}
}
module.exports = StreamErrorHandler;全局错误处理
1. 未捕获异常处理
javascript
// global-error-handler.js
const fs = require('fs');
const path = require('path');
const cluster = require('cluster');
class GlobalErrorHandler {
constructor(options = {}) {
this.logDir = options.logDir || './logs';
this.exitOnUncaught = options.exitOnUncaught !== false;
this.restartOnExit = options.restartOnExit !== false;
this.maxRestarts = options.maxRestarts || 5;
this.restartCount = 0;
this.setupErrorHandlers();
this.ensureLogDirectory();
}
setupErrorHandlers() {
// 未捕获的异常
process.on('uncaughtException', (error) => {
this.handleUncaughtException(error);
});
// 未处理的 Promise 拒绝
process.on('unhandledRejection', (reason, promise) => {
this.handleUnhandledRejection(reason, promise);
});
// 警告处理
process.on('warning', (warning) => {
this.handleWarning(warning);
});
// 进程退出处理
process.on('exit', (code) => {
this.handleExit(code);
});
// 信号处理
['SIGTERM', 'SIGINT', 'SIGUSR2'].forEach(signal => {
process.on(signal, () => {
this.handleSignal(signal);
});
});
}
handleUncaughtException(error) {
const errorInfo = {
type: 'UNCAUGHT_EXCEPTION',
error: {
name: error.name,
message: error.message,
stack: error.stack
},
process: {
pid: process.pid,
uptime: process.uptime(),
memoryUsage: process.memoryUsage(),
cpuUsage: process.cpuUsage()
},
timestamp: new Date().toISOString()
};
console.error('UNCAUGHT EXCEPTION:', errorInfo);
this.logError(errorInfo);
// 发送错误报告
this.sendErrorReport(errorInfo);
if (this.exitOnUncaught) {
console.error('Exiting process due to uncaught exception...');
process.exit(1);
}
}
handleUnhandledRejection(reason, promise) {
const errorInfo = {
type: 'UNHANDLED_REJECTION',
reason: reason instanceof Error ? {
name: reason.name,
message: reason.message,
stack: reason.stack
} : reason,
promise: promise.toString(),
process: {
pid: process.pid,
uptime: process.uptime()
},
timestamp: new Date().toISOString()
};
console.error('UNHANDLED REJECTION:', errorInfo);
this.logError(errorInfo);
// 发送错误报告
this.sendErrorReport(errorInfo);
}
handleWarning(warning) {
const warningInfo = {
type: '警告',
name: warning.name,
message: warning.message,
stack: warning.stack,
timestamp: new Date().toISOString()
};
console.warn('PROCESS 警告:', warningInfo);
this.logError(warningInfo, 'warning');
}
handleExit(code) {
const exitInfo = {
type: 'PROCESS_EXIT',
exitCode: code,
uptime: process.uptime(),
memoryUsage: process.memoryUsage(),
timestamp: new Date().toISOString()
};
console.log('PROCESS EXIT:', exitInfo);
this.logError(exitInfo, 'exit');
// 如果是集群模式且需要重启
if (cluster.isWorker && this.restartOnExit && code !== 0) {
this.requestRestart();
}
}
handleSignal(signal) {
const signalInfo = {
type: 'SIGNAL_RECEIVED',
signal,
timestamp: new Date().toISOString()
};
console.log('SIGNAL RECEIVED:', signalInfo);
this.logError(signalInfo, 'signal');
// 优雅关闭
this.gracefulShutdown();
}
logError(errorInfo, level = 'error') {
const logFile = path.join(this.logDir, `${level}-${this.getDateString()}.log`);
const logEntry = JSON.stringify(errorInfo) + '\n';
try {
fs.appendFileSync(logFile, logEntry);
} catch (err) {
console.error('Failed to write error log:', err);
}
}
sendErrorReport(errorInfo) {
// 这里可以实现发送错误报告到监控系统
// 例如:Sentry, Bugsnag, 自定义错误收集服务等
if (process.env.ERROR_REPORTING_URL) {
this.sendToErrorService(errorInfo);
}
// 发送邮件通知
if (process.env.ERROR_EMAIL) {
this.sendEmailNotification(errorInfo);
}
}
async sendToErrorService(errorInfo) {
try {
const response = await fetch(process.env.ERROR_REPORTING_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.ERROR_REPORTING_TOKEN}`
},
body: JSON.stringify(errorInfo)
});
if (!response.ok) {
console.error('Failed to send error report:', response.statusText);
}
} catch (error) {
console.error('Error sending error report:', error);
}
}
sendEmailNotification(errorInfo) {
// 实现邮件发送逻辑
console.log('Email notification would be sent for:', errorInfo.type);
}
requestRestart() {
if (this.restartCount >= this.maxRestarts) {
console.error(`Max restarts (${this.maxRestarts}) exceeded, not restarting`);
return;
}
this.restartCount++;
console.log(`Requesting restart (${this.restartCount}/${this.maxRestarts})`);
// 向主进程发送重启请求
if (process.send) {
process.send({ cmd: 'restart', restartCount: this.restartCount });
}
}
gracefulShutdown() {
console.log('Starting graceful shutdown...');
// 停止接受新连接
if (this.server) {
this.server.close(() => {
console.log('Server closed');
process.exit(0);
});
// 强制退出超时
setTimeout(() => {
console.error('Forced shutdown due to timeout');
process.exit(1);
}, 10000);
} else {
process.exit(0);
}
}
ensureLogDirectory() {
try {
if (!fs.existsSync(this.logDir)) {
fs.mkdirSync(this.logDir, { recursive: true });
}
} catch (error) {
console.error('Failed to create log directory:', error);
}
}
getDateString() {
return new Date().toISOString().split('T')[0];
}
// 设置服务器引用(用于优雅关闭)
setServer(server) {
this.server = server;
}
// 获取错误统计
getErrorStats() {
const stats = {
restartCount: this.restartCount,
maxRestarts: this.maxRestarts,
uptime: process.uptime(),
memoryUsage: process.memoryUsage(),
cpuUsage: process.cpuUsage()
};
return stats;
}
}
module.exports = GlobalErrorHandler;2. Express 错误处理中间件
javascript
// express-error-middleware.js
const winston = require('winston');
class ExpressErrorHandler {
constructor(options = {}) {
this.logger = options.logger || this.createDefaultLogger();
this.includeStack = options.includeStack !== false;
this.logErrors = options.logErrors !== false;
this.errorHandlers = new Map();
this.setupErrorHandlers();
}
createDefaultLogger() {
return winston.createLogger({
level: 'error',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'logs/express-errors.log' }),
new winston.transports.Console()
]
});
}
setupErrorHandlers() {
// 注册常见错误类型的处理器
this.registerErrorHandler('ValidationError', this.handleValidationError.bind(this));
this.registerErrorHandler('UnauthorizedError', this.handleUnauthorizedError.bind(this));
this.registerErrorHandler('ForbiddenError', this.handleForbiddenError.bind(this));
this.registerErrorHandler('NotFoundError', this.handleNotFoundError.bind(this));
this.registerErrorHandler('DatabaseError', this.handleDatabaseError.bind(this));
this.registerErrorHandler('RateLimitError', this.handleRateLimitError.bind(this));
}
// 主要错误处理中间件
errorHandler() {
return (error, req, res, next) => {
// 记录错误
if (this.logErrors) {
this.logError(error, req);
}
// 检查是否已经发送响应
if (res.headersSent) {
return next(error);
}
// 获取错误处理器
const handler = this.errorHandlers.get(error.name) || this.handleGenericError.bind(this);
// 处理错误
const errorResponse = handler(error, req);
// 发送响应
res.status(errorResponse.statusCode).json(errorResponse);
};
}
// 404 处理中间件
notFoundHandler() {
return (req, res, next) => {
const error = new Error(`Route not found: ${req.method} ${req.path}`);
error.name = 'NotFoundError';
error.statusCode = 404;
next(error);
};
}
// 异步错误捕获中间件
asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
// 验证错误处理
handleValidationError(error, req) {
return {
statusCode: 400,
error: {
type: 'VALIDATION_ERROR',
message: 'Validation failed',
details: error.details || error.message,
field: error.field
},
timestamp: new Date().toISOString(),
path: req.path,
method: req.method
};
}
// 未授权错误处理
handleUnauthorizedError(error, req) {
return {
statusCode: 401,
error: {
type: 'UNAUTHORIZED',
message: 'Authentication required',
details: error.message
},
timestamp: new Date().toISOString(),
path: req.path,
method: req.method
};
}
// 禁止访问错误处理
handleForbiddenError(error, req) {
return {
statusCode: 403,
error: {
type: 'FORBIDDEN',
message: 'Access denied',
details: error.message
},
timestamp: new Date().toISOString(),
path: req.path,
method: req.method
};
}
// 未找到错误处理
handleNotFoundError(error, req) {
return {
statusCode: 404,
error: {
type: 'NOT_FOUND',
message: 'Resource not found',
details: error.message
},
timestamp: new Date().toISOString(),
path: req.path,
method: req.method
};
}
// 数据库错误处理
handleDatabaseError(error, req) {
let statusCode = 500;
let message = 'Database error';
// MongoDB 错误
if (error.code === 11000) {
statusCode = 409;
message = 'Duplicate key error';
} else if (error.name === 'CastError') {
statusCode = 400;
message = 'Invalid ID format';
}
return {
statusCode,
error: {
type: 'DATABASE_ERROR',
message,
code: error.code,
details: process.env.NODE_ENV === 'development' ? error.message : undefined
},
timestamp: new Date().toISOString(),
path: req.path,
method: req.method
};
}
// 速率限制错误处理
handleRateLimitError(error, req) {
return {
statusCode: 429,
error: {
type: 'RATE_LIMIT_EXCEEDED',
message: 'Too many requests',
retryAfter: error.retryAfter,
limit: error.limit
},
timestamp: new Date().toISOString(),
path: req.path,
method: req.method
};
}
// 通用错误处理
handleGenericError(error, req) {
const statusCode = error.statusCode || error.status || 500;
const isProduction = process.env.NODE_ENV === 'production';
return {
statusCode,
error: {
type: 'INTERNAL_ERROR',
message: isProduction ? 'Internal server error' : error.message,
stack: !isProduction && this.includeStack ? error.stack : undefined
},
timestamp: new Date().toISOString(),
path: req.path,
method: req.method
};
}
// 记录错误
logError(error, req) {
const errorInfo = {
error: {
name: error.name,
message: error.message,
stack: error.stack,
statusCode: error.statusCode || error.status
},
request: {
method: req.method,
url: req.url,
path: req.path,
query: req.query,
params: req.params,
headers: req.headers,
ip: req.ip,
userAgent: req.get('User-Agent')
},
user: req.user ? {
id: req.user.id,
email: req.user.email
} : null,
timestamp: new Date().toISOString()
};
this.logger.error('Express Error', errorInfo);
}
// 注册错误处理器
registerErrorHandler(errorName, handler) {
this.errorHandlers.set(errorName, handler);
}
// 创建自定义错误类
static createErrorClass(name, defaultMessage, defaultStatusCode = 500) {
class CustomError extends Error {
constructor(message = defaultMessage, statusCode = defaultStatusCode) {
super(message);
this.name = name;
this.statusCode = statusCode;
Error.captureStackTrace(this, CustomError);
}
}
return CustomError;
}
}
// 预定义错误类
const ValidationError = ExpressErrorHandler.createErrorClass('ValidationError', 'Validation failed', 400);
const UnauthorizedError = ExpressErrorHandler.createErrorClass('UnauthorizedError', 'Unauthorized', 401);
const ForbiddenError = ExpressErrorHandler.createErrorClass('ForbiddenError', 'Forbidden', 403);
const NotFoundError = ExpressErrorHandler.createErrorClass('NotFoundError', 'Not found', 404);
const DatabaseError = ExpressErrorHandler.createErrorClass('DatabaseError', 'Database error', 500);
const RateLimitError = ExpressErrorHandler.createErrorClass('RateLimitError', 'Rate limit exceeded', 429);
module.exports = {
ExpressErrorHandler,
ValidationError,
UnauthorizedError,
ForbiddenError,
NotFoundError,
DatabaseError,
RateLimitError
};错误监控和报告
1. 错误收集和分析
javascript
// error-collector.js
const EventEmitter = require('events');
const fs = require('fs').promises;
const path = require('path');
class ErrorCollector extends EventEmitter {
constructor(options = {}) {
super();
this.maxErrors = options.maxErrors || 1000;
this.flushInterval = options.flushInterval || 60000; // 1分钟
this.storageDir = options.storageDir || './error-storage';
this.errors = [];
this.errorStats = {
total: 0,
byType: new Map(),
byHour: new Map(),
byEndpoint: new Map()
};
this.startFlushTimer();
this.ensureStorageDir();
}
// 收集错误
collect(error, context = {}) {
const errorEntry = {
id: this.generateErrorId(),
timestamp: new Date().toISOString(),
error: {
name: error.name,
message: error.message,
stack: error.stack,
code: error.code
},
context,
fingerprint: this.generateFingerprint(error)
};
this.errors.push(errorEntry);
this.updateStats(errorEntry);
// 触发事件
this.emit('error-collected', errorEntry);
// 检查是否需要立即刷新
if (this.errors.length >= this.maxErrors) {
this.flush();
}
return errorEntry.id;
}
// 更新统计信息
updateStats(errorEntry) {
this.errorStats.total++;
// 按类型统计
const errorType = errorEntry.error.name;
this.errorStats.byType.set(
errorType,
(this.errorStats.byType.get(errorType) || 0) + 1
);
// 按小时统计
const hour = new Date(errorEntry.timestamp).getHours();
this.errorStats.byHour.set(
hour,
(this.errorStats.byHour.get(hour) || 0) + 1
);
// 按端点统计
if (errorEntry.context.endpoint) {
const endpoint = errorEntry.context.endpoint;
this.errorStats.byEndpoint.set(
endpoint,
(this.errorStats.byEndpoint.get(endpoint) || 0) + 1
);
}
}
// 生成错误指纹(用于去重)
generateFingerprint(error) {
const crypto = require('crypto');
const content = `${error.name}:${error.message}:${this.getStackTrace(error)}`;
return crypto.createHash('md5').update(content).digest('hex');
}
getStackTrace(error) {
if (!error.stack) return '';
// 只取前3行堆栈信息用于指纹生成
return error.stack.split('\n').slice(0, 3).join('\n');
}
// 刷新错误到存储
async flush() {
if (this.errors.length === 0) return;
const errorsToFlush = [...this.errors];
this.errors = [];
try {
await this.saveErrors(errorsToFlush);
this.emit('errors-flushed', errorsToFlush.length);
} catch (error) {
console.error('Failed to flush errors:', error);
// 将错误重新加入队列
this.errors.unshift(...errorsToFlush);
}
}
// 保存错误到文件
async saveErrors(errors) {
const date = new Date().toISOString().split('T')[0];
const filename = `errors-${date}.jsonl`;
const filepath = path.join(this.storageDir, filename);
const lines = errors.map(error => JSON.stringify(error)).join('\n') + '\n';
await fs.appendFile(filepath, lines);
}
// 分析错误趋势
analyzeErrors(timeRange = '24h') {
const now = new Date();
let startTime;
switch (timeRange) {
case '1h':
startTime = new Date(now.getTime() - 60 * 60 * 1000);
break;
case '24h':
startTime = new Date(now.getTime() - 24 * 60 * 60 * 1000);
break;
case '7d':
startTime = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
break;
default:
startTime = new Date(now.getTime() - 24 * 60 * 60 * 1000);
}
const recentErrors = this.errors.filter(
error => new Date(error.timestamp) >= startTime
);
return {
totalErrors: recentErrors.length,
errorRate: recentErrors.length / (timeRange === '1h' ? 1 : timeRange === '24h' ? 24 : 168),
topErrors: this.getTopErrors(recentErrors),
errorsByHour: this.groupErrorsByHour(recentErrors),
affectedEndpoints: this.getAffectedEndpoints(recentErrors)
};
}
getTopErrors(errors) {
const errorCounts = new Map();
errors.forEach(error => {
const key = `${error.error.name}: ${error.error.message}`;
errorCounts.set(key, (errorCounts.get(key) || 0) + 1);
});
return Array.from(errorCounts.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.map(([error, count]) => ({ error, count }));
}
groupErrorsByHour(errors) {
const hourlyErrors = new Map();
errors.forEach(error => {
const hour = new Date(error.timestamp).getHours();
hourlyErrors.set(hour, (hourlyErrors.get(hour) || 0) + 1);
});
return Object.fromEntries(hourlyErrors);
}
getAffectedEndpoints(errors) {
const endpointErrors = new Map();
errors.forEach(error => {
if (error.context.endpoint) {
const endpoint = error.context.endpoint;
endpointErrors.set(endpoint, (endpointErrors.get(endpoint) || 0) + 1);
}
});
return Array.from(endpointErrors.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.map(([endpoint, count]) => ({ endpoint, count }));
}
// 获取错误统计
getStats() {
return {
...this.errorStats,
byType: Object.fromEntries(this.errorStats.byType),
byHour: Object.fromEntries(this.errorStats.byHour),
byEndpoint: Object.fromEntries(this.errorStats.byEndpoint),
queueSize: this.errors.length
};
}
// 搜索错误
searchErrors(query) {
const results = this.errors.filter(error => {
const searchText = `${error.error.name} ${error.error.message} ${error.error.stack}`
.toLowerCase();
return searchText.includes(query.toLowerCase());
});
return results.slice(0, 100); // 限制结果数量
}
// 获取错误详情
getErrorById(id) {
return this.errors.find(error => error.id === id);
}
startFlushTimer() {
setInterval(() => {
this.flush();
}, this.flushInterval);
}
async ensureStorageDir() {
try {
await fs.mkdir(this.storageDir, { recursive: true });
} catch (error) {
console.error('Failed to create storage directory:', error);
}
}
generateErrorId() {
return `err_${Date.now()}_${Math.random().toString(36).substring(2)}`;
}
// 清理旧错误
async cleanup(daysToKeep = 30) {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
try {
const files = await fs.readdir(this.storageDir);
for (const file of files) {
if (file.startsWith('errors-') && file.endsWith('.jsonl')) {
const filePath = path.join(this.storageDir, file);
const stats = await fs.stat(filePath);
if (stats.mtime < cutoffDate) {
await fs.unlink(filePath);
console.log(`Cleaned up old error file: ${file}`);
}
}
}
} catch (error) {
console.error('Error during cleanup:', error);
}
}
}
module.exports = ErrorCollector;2. 错误报告和通知
javascript
// error-reporter.js
const nodemailer = require('nodemailer');
const axios = require('axios');
class ErrorReporter {
constructor(options = {}) {
this.emailConfig = options.email;
this.slackConfig = options.slack;
this.webhookConfig = options.webhook;
this.sentryConfig = options.sentry;
this.alertThresholds = {
errorRate: options.errorRateThreshold || 10, // 每分钟错误数
criticalErrors: options.criticalErrorThreshold || 5, // 严重错误数
responseTime: options.responseTimeThreshold || 5000 // 响应时间阈值
};
this.setupEmailTransporter();
this.setupSentry();
}
setupEmailTransporter() {
if (this.emailConfig) {
this.emailTransporter = nodemailer.createTransporter({
host: this.emailConfig.host,
port: this.emailConfig.port,
secure: this.emailConfig.secure,
auth: {
user: this.emailConfig.user,
pass: this.emailConfig.password
}
});
}
}
setupSentry() {
if (this.sentryConfig) {
const Sentry = require('@sentry/node');
Sentry.init({
dsn: this.sentryConfig.dsn,
environment: this.sentryConfig.environment || 'production',
tracesSampleRate: this.sentryConfig.tracesSampleRate || 0.1
});
this.sentry = Sentry;
}
}
// 报告错误
async reportError(error, context = {}) {
const errorReport = this.createErrorReport(error, context);
// 并行发送到各个渠道
const promises = [];
if (this.shouldSendEmail(errorReport)) {
promises.push(this.sendEmailReport(errorReport));
}
if (this.shouldSendSlack(errorReport)) {
promises.push(this.sendSlackReport(errorReport));
}
if (this.webhookConfig) {
promises.push(this.sendWebhookReport(errorReport));
}
if (this.sentry) {
promises.push(this.sendSentryReport(error, context));
}
try {
await Promise.allSettled(promises);
} catch (error) {
console.error('Error sending reports:', error);
}
}
createErrorReport(error, context) {
return {
id: this.generateReportId(),
timestamp: new Date().toISOString(),
error: {
name: error.name,
message: error.message,
stack: error.stack,
code: error.code
},
context: {
...context,
environment: process.env.NODE_ENV,
nodeVersion: process.version,
platform: process.platform,
memory: process.memoryUsage(),
uptime: process.uptime()
},
severity: this.determineSeverity(error, context)
};
}
determineSeverity(error, context) {
// 严重错误
if (error.name === 'DatabaseError' ||
error.message.includes('ECONNREFUSED') ||
context.statusCode >= 500) {
return 'critical';
}
// 警告级别
if (context.statusCode >= 400 ||
error.name === 'ValidationError') {
return 'warning';
}
return 'info';
}
shouldSendEmail(errorReport) {
return this.emailConfig &&
(errorReport.severity === 'critical' ||
this.isHighFrequencyError(errorReport));
}
shouldSendSlack(errorReport) {
return this.slackConfig &&
errorReport.severity !== 'info';
}
isHighFrequencyError(errorReport) {
// 这里可以实现高频错误检测逻辑
return false;
}
// 发送邮件报告
async sendEmailReport(errorReport) {
if (!this.emailTransporter) return;
const subject = `[${errorReport.severity.toUpperCase()}] ${errorReport.error.name}: ${errorReport.error.message}`;
const html = this.generateEmailHTML(errorReport);
const mailOptions = {
from: this.emailConfig.from,
to: this.emailConfig.to,
subject,
html
};
try {
await this.emailTransporter.sendMail(mailOptions);
console.log('Error report sent via email');
} catch (error) {
console.error('Failed to send email report:', error);
}
}
generateEmailHTML(errorReport) {
return `
<html>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
<div style="max-width: 800px; margin: 0 auto; padding: 20px;">
<h1 style="color: ${this.getSeverityColor(errorReport.severity)};">
Error Report - ${errorReport.severity.toUpperCase()}
</h1>
<div style="background: #f5f5f5; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h2>Error Details</h2>
<p><strong>Type:</strong> ${errorReport.error.name}</p>
<p><strong>Message:</strong> ${errorReport.error.message}</p>
<p><strong>Time:</strong> ${errorReport.timestamp}</p>
${errorReport.error.code ? `<p><strong>Code:</strong> ${errorReport.error.code}</p>` : ''}
</div>
<div style="background: #f9f9f9; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h2>Context</h2>
<p><strong>Environment:</strong> ${errorReport.context.environment}</p>
<p><strong>Node Version:</strong> ${errorReport.context.nodeVersion}</p>
<p><strong>Platform:</strong> ${errorReport.context.platform}</p>
<p><strong>Uptime:</strong> ${Math.round(errorReport.context.uptime)} seconds</p>
${errorReport.context.endpoint ? `<p><strong>Endpoint:</strong> ${errorReport.context.endpoint}</p>` : ''}
${errorReport.context.userId ? `<p><strong>User ID:</strong> ${errorReport.context.userId}</p>` : ''}
</div>
${errorReport.error.stack ? `
<div style="background: #fff; border: 1px solid #ddd; padding: 15px; border-radius: 5px; margin: 20px 0;">
<h2>Stack Trace</h2>
<pre style="white-space: pre-wrap; font-size: 12px; overflow-x: auto;">${errorReport.error.stack}</pre>
</div>
` : ''}
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 12px; color: #666;">
<p>Report ID: ${errorReport.id}</p>
<p>Generated by Node.js Error Reporter</p>
</div>
</div>
</body>
</html>
`;
}
getSeverityColor(severity) {
switch (severity) {
case 'critical': return '#d32f2f';
case 'warning': return '#f57c00';
case 'info': return '#1976d2';
default: return '#666';
}
}
// 发送 Slack 报告
async sendSlackReport(errorReport) {
if (!this.slackConfig) return;
const payload = {
text: `Error Report - ${errorReport.severity.toUpperCase()}`,
attachments: [
{
color: this.getSlackColor(errorReport.severity),
fields: [
{
title: 'Error Type',
value: errorReport.error.name,
short: true
},
{
title: 'Message',
value: errorReport.error.message,
short: true
},
{
title: 'Environment',
value: errorReport.context.environment,
short: true
},
{
title: 'Time',
value: errorReport.timestamp,
short: true
}
],
footer: `Report ID: ${errorReport.id}`,
ts: Math.floor(Date.now() / 1000)
}
]
};
try {
await axios.post(this.slackConfig.webhookUrl, payload);
console.log('Error report sent to Slack');
} catch (error) {
console.error('Failed to send Slack report:', error);
}
}
getSlackColor(severity) {
switch (severity) {
case 'critical': return 'danger';
case 'warning': return 'warning';
case 'info': return 'good';
default: return '#666666';
}
}
// 发送 Webhook 报告
async sendWebhookReport(errorReport) {
try {
await axios.post(this.webhookConfig.url, errorReport, {
headers: {
'Content-Type': 'application/json',
...this.webhookConfig.headers
},
timeout: 5000
});
console.log('Error report sent via webhook');
} catch (error) {
console.error('Failed to send webhook report:', error);
}
}
// 发送 Sentry 报告
async sendSentryReport(error, context) {
if (!this.sentry) return;
this.sentry.withScope((scope) => {
// 设置上下文
if (context.userId) {
scope.setUser({ id: context.userId });
}
if (context.endpoint) {
scope.setTag('endpoint', context.endpoint);
}
if (context.requestId) {
scope.setTag('requestId', context.requestId);
}
// 设置额外数据
scope.setContext('context', context);
// 发送错误
this.sentry.captureException(error);
});
console.log('Error report sent to Sentry');
}
// 批量报告错误
async reportBatch(errors) {
const reports = errors.map(({ error, context }) =>
this.reportError(error, context)
);
await Promise.allSettled(reports);
}
// 生成报告摘要
generateSummaryReport(errors, timeRange = '24h') {
const now = new Date();
const startTime = new Date(now.getTime() - this.getTimeRangeMs(timeRange));
const recentErrors = errors.filter(
error => new Date(error.timestamp) >= startTime
);
const summary = {
timeRange,
totalErrors: recentErrors.length,
errorsByType: this.groupBy(recentErrors, 'error.name'),
errorsBySeverity: this.groupBy(recentErrors, 'severity'),
topEndpoints: this.getTopEndpoints(recentErrors),
errorTrend: this.calculateErrorTrend(recentErrors)
};
return summary;
}
getTimeRangeMs(timeRange) {
switch (timeRange) {
case '1h': return 60 * 60 * 1000;
case '24h': return 24 * 60 * 60 * 1000;
case '7d': return 7 * 24 * 60 * 60 * 1000;
default: return 24 * 60 * 60 * 1000;
}
}
groupBy(array, path) {
const result = {};
array.forEach(item => {
const key = this.getNestedValue(item, path);
result[key] = (result[key] || 0) + 1;
});
return result;
}
getNestedValue(obj, path) {
return path.split('.').reduce((current, key) => current?.[key], obj);
}
getTopEndpoints(errors) {
const endpointCounts = {};
errors.forEach(error => {
if (error.context?.endpoint) {
const endpoint = error.context.endpoint;
endpointCounts[endpoint] = (endpointCounts[endpoint] || 0) + 1;
}
});
return Object.entries(endpointCounts)
.sort((a, b) => b[1] - a[1])
.slice(0, 5)
.map(([endpoint, count]) => ({ endpoint, count }));
}
calculateErrorTrend(errors) {
const hourlyErrors = {};
errors.forEach(error => {
const hour = new Date(error.timestamp).getHours();
hourlyErrors[hour] = (hourlyErrors[hour] || 0) + 1;
});
return hourlyErrors;
}
generateReportId() {
return `report_${Date.now()}_${Math.random().toString(36).substring(2)}`;
}
}
module.exports = ErrorReporter;最佳实践
1. 错误处理策略
javascript
// error-strategy.js
class ErrorHandlingStrategy {
constructor() {
this.strategies = {
// 快速失败策略
FAIL_FAST: 'fail_fast',
// 优雅降级策略
GRACEFUL_DEGRADATION: 'graceful_degradation',
// 重试策略
RETRY: 'retry',
// 断路器策略
CIRCUIT_BREAKER: 'circuit_breaker'
};
}
// 根据错误类型选择处理策略
selectStrategy(error, context) {
// 网络错误 - 使用重试策略
if (this.isNetworkError(error)) {
return this.strategies.RETRY;
}
// 验证错误 - 快速失败
if (this.isValidationError(error)) {
return this.strategies.FAIL_FAST;
}
// 外部服务错误 - 优雅降级
if (this.isExternalServiceError(error)) {
return this.strategies.GRACEFUL_DEGRADATION;
}
// 高频错误 - 断路器
if (this.isHighFrequencyError(error, context)) {
return this.strategies.CIRCUIT_BREAKER;
}
return this.strategies.FAIL_FAST;
}
isNetworkError(error) {
return error.code === 'ECONNREFUSED' ||
error.code === 'ETIMEDOUT' ||
error.code === 'ENOTFOUND';
}
isValidationError(error) {
return error.name === 'ValidationError' ||
error.name === 'TypeError';
}
isExternalServiceError(error) {
return error.message?.includes('external service') ||
error.statusCode >= 500;
}
isHighFrequencyError(error, context) {
// 实现高频错误检测逻辑
return false;
}
}
module.exports = ErrorHandlingStrategy;2. 错误恢复机制
javascript
// error-recovery.js
class ErrorRecovery {
constructor(options = {}) {
this.maxRetries = options.maxRetries || 3;
this.baseDelay = options.baseDelay || 1000;
this.maxDelay = options.maxDelay || 30000;
this.backoffFactor = options.backoffFactor || 2;
}
// 指数退避重试
async retryWithBackoff(operation, context = {}) {
let lastError;
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
const result = await operation();
return { success: true, data: result, attempts: attempt };
} catch (error) {
lastError = error;
if (attempt === this.maxRetries) {
break;
}
if (!this.shouldRetry(error)) {
break;
}
const delay = Math.min(
this.baseDelay * Math.pow(this.backoffFactor, attempt - 1),
this.maxDelay
);
console.warn(`Attempt ${attempt} failed, retrying in ${delay}ms:`, error.message);
await this.sleep(delay);
}
}
return {
success: false,
error: lastError,
attempts: this.maxRetries
};
}
shouldRetry(error) {
// 不重试的错误类型
const nonRetryableErrors = [
'ValidationError',
'AuthenticationError',
'AuthorizationError'
];
if (nonRetryableErrors.includes(error.name)) {
return false;
}
// HTTP 状态码判断
if (error.statusCode) {
// 4xx 错误通常不需要重试(除了 429)
if (error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 429) {
return false;
}
}
return true;
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
module.exports = ErrorRecovery;3. 错误日志记录
javascript
// error-logger.js
const winston = require('winston');
const path = require('path');
class ErrorLogger {
constructor(options = {}) {
this.logDir = options.logDir || './logs';
this.maxFiles = options.maxFiles || 14;
this.maxSize = options.maxSize || '20m';
this.logger = this.createLogger();
}
createLogger() {
return winston.createLogger({
level: 'error',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json(),
winston.format.printf(({ timestamp, level, message, stack, ...meta }) => {
return JSON.stringify({
timestamp,
level,
message,
stack,
...meta
});
})
),
transports: [
// 错误日志文件
new winston.transports.File({
filename: path.join(this.logDir, 'error.log'),
level: 'error',
maxsize: this.maxSize,
maxFiles: this.maxFiles,
tailable: true
}),
// 所有日志文件
new winston.transports.File({
filename: path.join(this.logDir, 'combined.log'),
maxsize: this.maxSize,
maxFiles: this.maxFiles,
tailable: true
}),
// 控制台输出
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
})
],
// 处理未捕获的异常
exceptionHandlers: [
new winston.transports.File({
filename: path.join(this.logDir, 'exceptions.log')
})
],
// 处理未处理的 Promise 拒绝
rejectionHandlers: [
new winston.transports.File({
filename: path.join(this.logDir, 'rejections.log')
})
]
});
}
// 记录错误
logError(error, context = {}) {
const errorInfo = {
error: {
name: error.name,
message: error.message,
stack: error.stack,
code: error.code
},
context: {
...context,
timestamp: new Date().toISOString(),
nodeVersion: process.version,
platform: process.platform,
memory: process.memoryUsage(),
uptime: process.uptime()
}
};
this.logger.error('Application Error', errorInfo);
}
// 记录警告
logWarning(message, context = {}) {
this.logger.warn(message, context);
}
// 记录信息
logInfo(message, context = {}) {
this.logger.info(message, context);
}
// 创建子日志器
createChildLogger(defaultMeta) {
return this.logger.child(defaultMeta);
}
}
module.exports = ErrorLogger;使用示例
1. Express 应用错误处理
javascript
// app.js
const express = require('express');
const { ExpressErrorHandler, ValidationError } = require('./express-error-middleware');
const GlobalErrorHandler = require('./global-error-handler');
const ErrorCollector = require('./error-collector');
const ErrorReporter = require('./error-reporter');
const app = express();
// 初始化错误处理组件
const globalErrorHandler = new GlobalErrorHandler({
logDir: './logs',
exitOnUncaught: false
});
const errorCollector = new ErrorCollector({
maxErrors: 1000,
flushInterval: 30000
});
const errorReporter = new ErrorReporter({
email: {
host: 'smtp.gmail.com',
port: 587,
secure: false,
user: process.env.EMAIL_USER,
password: process.env.EMAIL_PASSWORD,
from: 'errors@myapp.com',
to: 'admin@myapp.com'
},
slack: {
webhookUrl: process.env.SLACK_WEBHOOK_URL
}
});
const expressErrorHandler = new ExpressErrorHandler({
includeStack: process.env.NODE_ENV !== 'production',
logErrors: true
});
// 中间件
app.use(express.json());
// 路由
app.get('/api/users/:id', expressErrorHandler.asyncHandler(async (req, res) => {
const { id } = req.params;
// 验证 ID
if (!id || isNaN(id)) {
throw new ValidationError('Invalid user ID');
}
// 模拟数据库查询
const user = await getUserById(id);
if (!user) {
throw new NotFoundError('User not found');
}
res.json(user);
}));
// 错误处理中间件
app.use(expressErrorHandler.notFoundHandler());
app.use(expressErrorHandler.errorHandler());
// 错误收集和报告
app.use((error, req, res, next) => {
// 收集错误
const errorId = errorCollector.collect(error, {
endpoint: `${req.method} ${req.path}`,
userId: req.user?.id,
requestId: req.id,
userAgent: req.get('User-Agent'),
ip: req.ip
});
// 报告严重错误
if (error.statusCode >= 500) {
errorReporter.reportError(error, {
endpoint: `${req.method} ${req.path}`,
userId: req.user?.id,
requestId: req.id,
errorId
});
}
next();
});
// 设置服务器引用
const server = app.listen(3000, () => {
console.log('Server running on port 3000');
});
globalErrorHandler.setServer(server);
// 模拟数据库查询函数
async function getUserById(id) {
// 模拟数据库错误
if (id === '999') {
const error = new Error('Database connection failed');
error.name = 'DatabaseError';
throw error;
}
// 模拟用户不存在
if (id === '404') {
return null;
}
return {
id: parseInt(id),
name: `User ${id}`,
email: `user${id}@example.com`
};
}2. 微服务错误处理
javascript
// microservice-error-handler.js
const AsyncErrorHandler = require('./async-error-handler');
const ErrorRecovery = require('./error-recovery');
class MicroserviceErrorHandler {
constructor(serviceName, options = {}) {
this.serviceName = serviceName;
this.asyncErrorHandler = new AsyncErrorHandler();
this.errorRecovery = new ErrorRecovery(options.recovery);
this.circuitBreaker = new Map(); // 简单的断路器实现
}
// 服务间调用错误处理
async callService(serviceName, operation, ...args) {
// 检查断路器状态
if (this.isCircuitOpen(serviceName)) {
throw new Error(`Circuit breaker is open for service: ${serviceName}`);
}
try {
const result = await this.errorRecovery.retryWithBackoff(
() => operation(...args)
);
if (result.success) {
this.recordSuccess(serviceName);
return result.data;
} else {
this.recordFailure(serviceName);
throw result.error;
}
} catch (error) {
this.recordFailure(serviceName);
throw error;
}
}
// 断路器逻辑
isCircuitOpen(serviceName) {
const circuit = this.circuitBreaker.get(serviceName);
if (!circuit) return false;
const now = Date.now();
// 如果在冷却期内,检查是否可以尝试
if (circuit.state === 'open' && now - circuit.lastFailure > circuit.timeout) {
circuit.state = 'half-open';
return false;
}
return circuit.state === 'open';
}
recordSuccess(serviceName) {
const circuit = this.circuitBreaker.get(serviceName);
if (circuit) {
circuit.failures = 0;
circuit.state = 'closed';
}
}
recordFailure(serviceName) {
let circuit = this.circuitBreaker.get(serviceName);
if (!circuit) {
circuit = {
failures: 0,
state: 'closed',
threshold: 5,
timeout: 60000, // 1分钟
lastFailure: null
};
this.circuitBreaker.set(serviceName, circuit);
}
circuit.failures++;
circuit.lastFailure = Date.now();
if (circuit.failures >= circuit.threshold) {
circuit.state = 'open';
}
}
}
module.exports = MicroserviceErrorHandler;总结
本章详细介绍了 Node.js 中的错误处理最佳实践,包括:
- 错误类型处理:同步错误、异步错误、流错误的处理方法
- 全局错误处理:未捕获异常、未处理的 Promise 拒绝处理
- Express 错误处理:中间件、自定义错误类、错误响应格式化
- 错误监控:错误收集、分析、报告和通知机制
- 最佳实践:错误处理策略、恢复机制、日志记录
通过合理的错误处理机制,可以显著提高 Node.js 应用的稳定性和可维护性。
下一步
- 学习性能优化技术
- 了解安全最佳实践
- 掌握部署和运维知识