# 错误处理和日志系统文档 ## 概述 错误处理和日志系统是解班客平台的核心基础设施,提供统一的错误处理机制、完善的日志记录功能和系统监控能力。系统采用分层设计,支持多种错误类型处理、多级日志记录和实时监控。 ## 系统架构 ### 核心组件 1. **错误处理中间件** (`middleware/errorHandler.js`) - 全局错误捕获 - 错误分类处理 - 统一错误响应 - 错误日志记录 2. **日志记录系统** (`utils/logger.js`) - 多级日志记录 - 日志格式化 - 日志轮转管理 - 性能监控 3. **自定义错误类** - 业务错误定义 - 错误码管理 - 错误信息国际化 - 错误堆栈追踪 ## 错误处理机制 ### 错误分类 #### 1. 业务错误 (Business Errors) - **用户认证错误**: 登录失败、token过期等 - **权限错误**: 无权限访问、操作被拒绝等 - **数据验证错误**: 参数格式错误、必填项缺失等 - **业务逻辑错误**: 余额不足、状态不允许等 #### 2. 系统错误 (System Errors) - **数据库错误**: 连接失败、查询超时等 - **网络错误**: 请求超时、连接中断等 - **文件系统错误**: 文件不存在、权限不足等 - **第三方服务错误**: API调用失败、服务不可用等 #### 3. 程序错误 (Programming Errors) - **语法错误**: 代码语法问题 - **运行时错误**: 空指针、类型错误等 - **内存错误**: 内存溢出、内存泄漏等 - **配置错误**: 配置文件错误、环境变量缺失等 ### 错误处理流程 ```mermaid graph TD A[请求开始] --> B[业务逻辑处理] B --> C{是否发生错误?} C -->|否| D[正常响应] C -->|是| E[错误捕获] E --> F[错误分类] F --> G[错误日志记录] G --> H[错误响应格式化] H --> I[返回错误响应] D --> J[请求结束] I --> J ``` ### 自定义错误类 #### AppError 类 ```javascript class AppError extends Error { constructor(message, statusCode, errorCode = null, isOperational = true) { super(message); this.statusCode = statusCode; this.errorCode = errorCode; this.isOperational = isOperational; this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error'; Error.captureStackTrace(this, this.constructor); } } ``` #### 错误类型定义 ```javascript const ErrorTypes = { // 认证相关错误 AUTH_TOKEN_MISSING: { code: 'AUTH_001', message: '缺少认证令牌' }, AUTH_TOKEN_INVALID: { code: 'AUTH_002', message: '无效的认证令牌' }, AUTH_TOKEN_EXPIRED: { code: 'AUTH_003', message: '认证令牌已过期' }, // 权限相关错误 PERMISSION_DENIED: { code: 'PERM_001', message: '权限不足' }, RESOURCE_FORBIDDEN: { code: 'PERM_002', message: '资源访问被禁止' }, // 验证相关错误 VALIDATION_FAILED: { code: 'VALID_001', message: '数据验证失败' }, REQUIRED_FIELD_MISSING: { code: 'VALID_002', message: '必填字段缺失' }, INVALID_FORMAT: { code: 'VALID_003', message: '数据格式无效' }, // 业务逻辑错误 RESOURCE_NOT_FOUND: { code: 'BIZ_001', message: '资源不存在' }, RESOURCE_ALREADY_EXISTS: { code: 'BIZ_002', message: '资源已存在' }, OPERATION_NOT_ALLOWED: { code: 'BIZ_003', message: '操作不被允许' }, // 系统错误 DATABASE_ERROR: { code: 'SYS_001', message: '数据库操作失败' }, FILE_SYSTEM_ERROR: { code: 'SYS_002', message: '文件系统错误' }, NETWORK_ERROR: { code: 'SYS_003', message: '网络连接错误' }, // 第三方服务错误 THIRD_PARTY_SERVICE_ERROR: { code: 'EXT_001', message: '第三方服务错误' }, API_RATE_LIMIT_EXCEEDED: { code: 'EXT_002', message: 'API调用频率超限' } }; ``` ### 错误响应格式 #### 标准错误响应 ```json { "success": false, "error": { "code": "AUTH_002", "message": "无效的认证令牌", "details": "Token signature verification failed", "timestamp": "2024-01-15T10:30:00.000Z", "path": "/api/v1/admin/users", "method": "GET", "requestId": "req_1234567890" } } ``` #### 验证错误响应 ```json { "success": false, "error": { "code": "VALID_001", "message": "数据验证失败", "details": { "email": ["邮箱格式不正确"], "password": ["密码长度至少8位", "密码必须包含数字和字母"] }, "timestamp": "2024-01-15T10:30:00.000Z", "path": "/api/v1/auth/register", "method": "POST", "requestId": "req_1234567891" } } ``` ## 日志系统 ### 日志级别 #### 1. ERROR (错误) - **用途**: 记录系统错误和异常 - **示例**: 数据库连接失败、未捕获的异常 - **处理**: 需要立即关注和处理 #### 2. WARN (警告) - **用途**: 记录潜在问题和警告信息 - **示例**: 性能警告、配置问题 - **处理**: 需要关注,但不影响系统运行 #### 3. INFO (信息) - **用途**: 记录重要的业务操作和系统状态 - **示例**: 用户登录、重要配置变更 - **处理**: 用于审计和监控 #### 4. HTTP (HTTP请求) - **用途**: 记录HTTP请求和响应信息 - **示例**: API调用、响应时间 - **处理**: 用于性能分析和调试 #### 5. DEBUG (调试) - **用途**: 记录详细的调试信息 - **示例**: 变量值、执行流程 - **处理**: 仅在开发环境使用 ### 日志格式 #### 标准日志格式 ``` [2024-01-15 10:30:00.123] [INFO] [USER_AUTH] 用户登录成功 - userId: 12345, ip: 192.168.1.100, userAgent: Mozilla/5.0... ``` #### JSON格式日志 ```json { "timestamp": "2024-01-15T10:30:00.123Z", "level": "INFO", "category": "USER_AUTH", "message": "用户登录成功", "metadata": { "userId": 12345, "ip": "192.168.1.100", "userAgent": "Mozilla/5.0...", "requestId": "req_1234567890", "duration": 150 } } ``` ### 日志分类 #### 1. 请求日志 (Request Logs) ```javascript // 记录HTTP请求信息 logger.http('API请求', { method: 'POST', url: '/api/v1/users', ip: '192.168.1.100', userAgent: 'Mozilla/5.0...', requestId: 'req_1234567890', userId: 12345, duration: 150, statusCode: 200 }); ``` #### 2. 业务日志 (Business Logs) ```javascript // 记录业务操作 logger.business('用户注册', { action: 'USER_REGISTER', userId: 12345, email: 'user@example.com', ip: '192.168.1.100', success: true }); ``` #### 3. 安全日志 (Security Logs) ```javascript // 记录安全事件 logger.security('登录失败', { event: 'LOGIN_FAILED', email: 'user@example.com', ip: '192.168.1.100', reason: 'INVALID_PASSWORD', attempts: 3 }); ``` #### 4. 性能日志 (Performance Logs) ```javascript // 记录性能数据 logger.performance('数据库查询', { operation: 'SELECT', table: 'users', duration: 50, rowCount: 100, query: 'SELECT * FROM users WHERE status = ?' }); ``` #### 5. 系统日志 (System Logs) ```javascript // 记录系统事件 logger.system('服务启动', { event: 'SERVER_START', port: 3000, environment: 'production', version: '1.0.0' }); ``` ### 日志存储和轮转 #### 日志文件结构 ``` logs/ ├── app.log # 应用主日志 ├── error.log # 错误日志 ├── access.log # 访问日志 ├── security.log # 安全日志 ├── performance.log # 性能日志 ├── business.log # 业务日志 └── archived/ # 归档日志 ├── app-2024-01-14.log ├── error-2024-01-14.log └── ... ``` #### 日志轮转配置 ```javascript const winston = require('winston'); require('winston-daily-rotate-file'); const transport = new winston.transports.DailyRotateFile({ filename: 'logs/app-%DATE%.log', datePattern: 'YYYY-MM-DD', zippedArchive: true, maxSize: '20m', maxFiles: '30d' }); ``` ## 监控和告警 ### 错误监控 #### 1. 错误率监控 - **指标**: 每分钟错误数量、错误率 - **阈值**: 错误率超过5%触发告警 - **处理**: 自动发送告警通知 #### 2. 响应时间监控 - **指标**: 平均响应时间、95%分位数 - **阈值**: 响应时间超过2秒触发告警 - **处理**: 性能优化建议 #### 3. 系统资源监控 - **指标**: CPU使用率、内存使用率、磁盘空间 - **阈值**: 资源使用率超过80%触发告警 - **处理**: 资源扩容建议 ### 日志分析 #### 1. 实时日志分析 ```javascript // 实时错误统计 const errorStats = { total: 0, byType: {}, byEndpoint: {}, recentErrors: [] }; // 更新错误统计 function updateErrorStats(error, req) { errorStats.total++; errorStats.byType[error.code] = (errorStats.byType[error.code] || 0) + 1; errorStats.byEndpoint[req.path] = (errorStats.byEndpoint[req.path] || 0) + 1; errorStats.recentErrors.unshift({ timestamp: new Date(), code: error.code, message: error.message, path: req.path, method: req.method }); // 保持最近100个错误 if (errorStats.recentErrors.length > 100) { errorStats.recentErrors.pop(); } } ``` #### 2. 日志聚合分析 ```javascript // 按时间段聚合日志 function aggregateLogs(startTime, endTime) { return { totalRequests: 0, successRequests: 0, errorRequests: 0, averageResponseTime: 0, topEndpoints: [], topErrors: [], userActivity: {} }; } ``` ### 告警机制 #### 1. 邮件告警 ```javascript const nodemailer = require('nodemailer'); async function sendErrorAlert(error, context) { const transporter = nodemailer.createTransporter({ // 邮件服务配置 }); const mailOptions = { from: 'system@jiebanke.com', to: 'admin@jiebanke.com', subject: `[解班客] 系统错误告警 - ${error.code}`, html: `

系统错误告警

错误代码: ${error.code}

错误信息: ${error.message}

发生时间: ${new Date().toLocaleString()}

请求路径: ${context.path}

用户ID: ${context.userId || '未知'}

IP地址: ${context.ip}

错误堆栈:\n${error.stack}
` }; await transporter.sendMail(mailOptions); } ``` #### 2. 钉钉/企业微信告警 ```javascript async function sendDingTalkAlert(error, context) { const webhook = process.env.DINGTALK_WEBHOOK; const message = { msgtype: 'markdown', markdown: { title: '系统错误告警', text: ` ### 系统错误告警 - **错误代码**: ${error.code} - **错误信息**: ${error.message} - **发生时间**: ${new Date().toLocaleString()} - **请求路径**: ${context.path} - **用户ID**: ${context.userId || '未知'} - **IP地址**: ${context.ip} ` } }; await fetch(webhook, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(message) }); } ``` ## 性能优化 ### 日志性能优化 #### 1. 异步日志写入 ```javascript const winston = require('winston'); const logger = winston.createLogger({ transports: [ new winston.transports.File({ filename: 'logs/app.log', // 启用异步写入 options: { flags: 'a' } }) ] }); ``` #### 2. 日志缓冲 ```javascript class LogBuffer { constructor(flushInterval = 1000, maxBufferSize = 100) { this.buffer = []; this.flushInterval = flushInterval; this.maxBufferSize = maxBufferSize; // 定时刷新缓冲区 setInterval(() => this.flush(), flushInterval); } add(logEntry) { this.buffer.push(logEntry); // 缓冲区满时立即刷新 if (this.buffer.length >= this.maxBufferSize) { this.flush(); } } flush() { if (this.buffer.length === 0) return; const logs = this.buffer.splice(0); // 批量写入日志 this.writeLogs(logs); } writeLogs(logs) { // 实现批量日志写入 } } ``` #### 3. 日志采样 ```javascript class LogSampler { constructor(sampleRate = 0.1) { this.sampleRate = sampleRate; } shouldLog(level) { // 错误日志始终记录 if (level === 'error') return true; // 其他日志按采样率记录 return Math.random() < this.sampleRate; } } ``` ### 错误处理性能优化 #### 1. 错误缓存 ```javascript const errorCache = new Map(); function cacheError(error, context) { const key = `${error.code}_${context.path}`; const cached = errorCache.get(key); if (cached && Date.now() - cached.timestamp < 60000) { // 1分钟内相同错误不重复处理 return false; } errorCache.set(key, { timestamp: Date.now(), count: (cached?.count || 0) + 1 }); return true; } ``` #### 2. 错误聚合 ```javascript class ErrorAggregator { constructor(windowSize = 60000) { this.windowSize = windowSize; this.errors = new Map(); // 定期清理过期错误 setInterval(() => this.cleanup(), windowSize); } add(error, context) { const key = `${error.code}_${context.path}`; const now = Date.now(); if (!this.errors.has(key)) { this.errors.set(key, { first: now, last: now, count: 1, error, context }); } else { const entry = this.errors.get(key); entry.last = now; entry.count++; } } cleanup() { const now = Date.now(); for (const [key, entry] of this.errors.entries()) { if (now - entry.last > this.windowSize) { this.errors.delete(key); } } } } ``` ## 使用示例 ### 基础错误处理 #### 1. 控制器中的错误处理 ```javascript const { AppError, ErrorTypes, catchAsync } = require('../middleware/errorHandler'); const logger = require('../utils/logger'); // 获取用户信息 const getUser = catchAsync(async (req, res, next) => { const { userId } = req.params; // 参数验证 if (!userId || !mongoose.Types.ObjectId.isValid(userId)) { return next(new AppError( ErrorTypes.INVALID_FORMAT.message, 400, ErrorTypes.INVALID_FORMAT.code )); } // 查询用户 const user = await User.findById(userId); if (!user) { return next(new AppError( ErrorTypes.RESOURCE_NOT_FOUND.message, 404, ErrorTypes.RESOURCE_NOT_FOUND.code )); } // 权限检查 if (req.user.id !== userId && req.user.role !== 'admin') { return next(new AppError( ErrorTypes.PERMISSION_DENIED.message, 403, ErrorTypes.PERMISSION_DENIED.code )); } // 记录业务日志 logger.business('查看用户信息', { action: 'VIEW_USER', targetUserId: userId, operatorId: req.user.id, ip: req.ip }); res.json({ success: true, data: { user } }); }); ``` #### 2. 数据库操作错误处理 ```javascript const { handleDatabaseError } = require('../middleware/errorHandler'); async function createUser(userData) { try { const user = new User(userData); await user.save(); logger.business('用户创建成功', { action: 'CREATE_USER', userId: user._id, email: user.email }); return user; } catch (error) { // 处理数据库错误 throw handleDatabaseError(error); } } ``` ### 高级日志记录 #### 1. 请求日志中间件使用 ```javascript const express = require('express'); const { requestLogger } = require('../utils/logger'); const app = express(); // 使用请求日志中间件 app.use(requestLogger); // 路由定义 app.get('/api/users', (req, res) => { // 业务逻辑 }); ``` #### 2. 性能监控 ```javascript const logger = require('../utils/logger'); async function performDatabaseQuery(query) { const startTime = Date.now(); try { const result = await db.query(query); const duration = Date.now() - startTime; // 记录性能日志 logger.performance('数据库查询', { query: query.sql, duration, rowCount: result.length, success: true }); // 慢查询告警 if (duration > 1000) { logger.warn('慢查询检测', { query: query.sql, duration, threshold: 1000 }); } return result; } catch (error) { const duration = Date.now() - startTime; logger.performance('数据库查询失败', { query: query.sql, duration, error: error.message, success: false }); throw error; } } ``` #### 3. 安全事件记录 ```javascript const logger = require('../utils/logger'); // 登录失败记录 function recordLoginFailure(email, ip, reason) { logger.security('登录失败', { event: 'LOGIN_FAILED', email, ip, reason, timestamp: new Date(), severity: 'medium' }); } // 可疑活动记录 function recordSuspiciousActivity(userId, activity, details) { logger.security('可疑活动', { event: 'SUSPICIOUS_ACTIVITY', userId, activity, details, timestamp: new Date(), severity: 'high' }); } ``` ## 故障排除 ### 常见问题 #### 1. 日志文件过大 **问题**: 日志文件增长过快,占用大量磁盘空间 **解决方案**: - 启用日志轮转 - 调整日志级别 - 实施日志采样 - 定期清理旧日志 #### 2. 错误信息泄露 **问题**: 错误响应包含敏感信息 **解决方案**: - 使用统一错误响应格式 - 过滤敏感信息 - 区分开发和生产环境 - 记录详细日志但返回简化错误 #### 3. 性能影响 **问题**: 日志记录影响系统性能 **解决方案**: - 使用异步日志写入 - 实施日志缓冲 - 优化日志格式 - 使用日志采样 ### 调试技巧 #### 1. 启用调试日志 ```javascript // 设置环境变量 NODE_ENV=development LOG_LEVEL=debug // 或在代码中动态设置 logger.level = 'debug'; ``` #### 2. 错误追踪 ```javascript // 添加请求ID用于追踪 const { v4: uuidv4 } = require('uuid'); app.use((req, res, next) => { req.requestId = uuidv4(); res.setHeader('X-Request-ID', req.requestId); next(); }); // 在日志中包含请求ID logger.info('处理请求', { requestId: req.requestId, method: req.method, url: req.url }); ``` #### 3. 错误重现 ```javascript // 保存错误上下文用于重现 function saveErrorContext(error, req) { const context = { timestamp: new Date(), error: { message: error.message, stack: error.stack, code: error.code }, request: { method: req.method, url: req.url, headers: req.headers, body: req.body, params: req.params, query: req.query }, user: req.user, session: req.session }; // 保存到文件或数据库 fs.writeFileSync( `error-contexts/${Date.now()}.json`, JSON.stringify(context, null, 2) ); } ``` ## 最佳实践 ### 错误处理最佳实践 1. **统一错误格式**: 使用统一的错误响应格式 2. **错误分类**: 明确区分业务错误和系统错误 3. **错误码管理**: 使用有意义的错误码 4. **安全考虑**: 不在错误响应中暴露敏感信息 5. **用户友好**: 提供用户友好的错误信息 ### 日志记录最佳实践 1. **结构化日志**: 使用JSON格式记录结构化数据 2. **上下文信息**: 记录足够的上下文信息用于调试 3. **性能考虑**: 避免日志记录影响系统性能 4. **安全性**: 不在日志中记录敏感信息 5. **可搜索性**: 使用一致的字段名和格式 ### 监控告警最佳实践 1. **合理阈值**: 设置合理的告警阈值 2. **告警分级**: 区分不同级别的告警 3. **避免告警疲劳**: 防止过多无用告警 4. **快速响应**: 建立快速响应机制 5. **持续优化**: 根据实际情况调整监控策略 ## 总结 错误处理和日志系统是解班客平台稳定运行的重要保障。通过统一的错误处理机制、完善的日志记录功能和实时监控告警,系统能够快速发现和解决问题,提供稳定可靠的服务。 系统采用分层设计,支持多种错误类型和日志级别,提供了灵活的配置选项和丰富的功能特性。通过性能优化和最佳实践,确保系统在高负载情况下仍能正常运行。 未来将继续完善系统功能,增加更多监控指标和告警机制,为平台的稳定运行提供更强有力的支持。