# 工具模块详解 ## 📋 文档概述 本文档详细说明进贤麻将的工具模块系统,包括: - **Logger.js** - 日志管理器 - **ErrorHandler.js** - 错误处理器 - **shared/utils** - 共享工具类 - 工具模块的使用方法和最佳实践 **文档目标**:帮助开发者正确使用日志系统和错误处理机制,提升代码质量和可维护性。 --- ## 🎯 工具模块架构 ### 系统组成 ``` 工具模块系统 ├── utils/ # 子游戏专用工具 │ ├── Logger.js # 日志管理器 │ └── ErrorHandler.js # 错误处理器 │ └── shared/utils/ # 前后端共享工具 ├── ArrayUtils.js # 数组操作工具 ├── CardSourceInfoHelper.js # 牌源信息辅助 ├── GameContextHelper.js # 游戏上下文辅助 ├── GameStateHelper.js # 游戏状态辅助 ├── MahjongCardUniqueId.js # 麻将牌唯一ID └── RoomConfigUtils.js # 房间配置工具 ``` ### 设计原则 1. **分离关注点**:日志和错误处理分开管理 2. **ES5兼容**:兼容浏览器和Node.js环境 3. **统一接口**:提供统一的API调用方式 4. **性能优化**:最小化性能开销 5. **易于使用**:简单直观的API设计 --- ## 📝 Logger.js - 日志管理器 ### 核心功能 **文件位置**:`server/games2/jinxianmahjong/utils/Logger.js` **版本**:v1.0.0 **总行数**:606行 Logger提供了完整的日志管理功能: - ✅ 6级日志系统(TRACE/DEBUG/INFO/WARN/ERROR/FATAL) - ✅ 日志格式化和美化输出 - ✅ 日志历史记录和查询 - ✅ 日志统计分析 - ✅ 性能计时功能 - ✅ 分组日志输出 - ✅ 控制台颜色支持 ### 日志级别定义 ```javascript LOG_LEVELS: { TRACE: 0, // 最详细的信息,用于跟踪程序执行 DEBUG: 1, // 调试信息,开发时使用 INFO: 2, // 一般信息,记录程序运行状态 WARN: 3, // 警告信息,可能的问题 ERROR: 4, // 错误信息,程序运行错误 FATAL: 5 // 致命错误,程序无法继续运行 } ``` **级别控制**:只有 `>= currentLevel` 的日志才会输出 **颜色编码**: | 级别 | 颜色 | 说明 | |------|------|------| | TRACE | 白色 | 详细跟踪信息 | | DEBUG | 青色 | 开发调试信息 | | INFO | 绿色 | 正常运行信息 | | WARN | 黄色 | 警告提示信息 | | ERROR | 红色 | 错误异常信息 | | FATAL | 紫色 | 致命错误信息 | ### 配置参数 ```javascript config: { // 当前日志级别 currentLevel: 1, // 默认DEBUG级别 // 输出设置 enableConsole: true, // 启用控制台输出 enableColors: true, // 启用颜色(如果支持) enableTimestamp: true, // 启用时间戳 enableModuleName: true, // 启用模块名 enableStackTrace: false, // 启用堆栈跟踪(ERROR级别以上) // 格式设置 timestampFormat: 'ISO', // 时间戳格式: 'ISO', 'LOCAL', 'TIMESTAMP' moduleName: 'JinXianMahjong', // 模块名 // 性能设置 maxLogHistory: 1000, // 最大日志历史记录数 flushInterval: 0 // 刷新间隔(毫秒,0表示立即输出) } ``` ### 核心API #### 1. 设置日志级别 ```javascript /** * 设置日志级别 * @param {number|string} level - 日志级别(数字或字符串) */ Logger.setLevel(level) // 使用示例 Logger.setLevel('DEBUG'); // 使用字符串 Logger.setLevel(1); // 使用数字 Logger.setLevel('INFO'); // 只输出INFO及以上级别 ``` #### 2. 记录日志 **基础日志方法**: ```javascript /** * 记录日志 * @param {string} tag - 标签(模块/功能名称) * @param {string} message - 日志消息 * @param {Object} data - 附加数据(可选) */ Logger.trace(tag, message, data) Logger.debug(tag, message, data) Logger.info(tag, message, data) Logger.warn(tag, message, data) Logger.error(tag, message, data) Logger.fatal(tag, message, data) ``` **使用示例**: ```javascript // 基础日志 Logger.info('GameController', '游戏开始'); // 带附加数据的日志 Logger.debug('MahjongService', '发牌完成', { playerCount: 4, tilesPerPlayer: 13 }); // 警告日志 Logger.warn('OperationManager', '玩家操作超时', { playerId: 'player001', operation: 'discard', timeout: 15000 }); // 错误日志 Logger.error('RpcHandler', 'RPC调用失败', { method: 'player_discard', errorCode: 2001, error: error.message }); // 致命错误 Logger.fatal('System', '系统初始化失败', { reason: '依赖模块加载失败' }); ``` #### 3. 分组日志 ```javascript /** * 分组日志(用于组织相关日志) */ Logger.group(groupName) // 开始分组 Logger.groupEnd() // 结束分组 Logger.groupCollapsed(groupName) // 开始折叠分组(默认折叠) // 使用示例 Logger.group('游戏初始化'); Logger.info('Init', '加载配置'); Logger.info('Init', '创建游戏状态'); Logger.info('Init', '初始化牌墙'); Logger.groupEnd(); ``` #### 4. 性能计时 ```javascript /** * 性能计时 */ Logger.time(label) // 开始计时 Logger.timeEnd(label) // 结束计时并输出 // 使用示例 Logger.time('胡牌检测'); var result = WinDetectionFactory.detectWin(handTiles, jingInfo); Logger.timeEnd('胡牌检测'); // 输出: 胡牌检测: 3.142ms ``` #### 5. 表格输出 ```javascript /** * 表格输出(用于输出结构化数据) * @param {Array|Object} data - 表格数据 */ Logger.table(data) // 使用示例 var players = [ { id: 'p1', name: '玩家1', score: 100 }, { id: 'p2', name: '玩家2', score: 50 }, { id: 'p3', name: '玩家3', score: -30 }, { id: 'p4', name: '玩家4', score: -20 } ]; Logger.table(players); ``` #### 6. 日志历史 ```javascript /** * 获取日志历史 * @param {number} limit - 返回数量限制(可选) * @param {number} level - 过滤日志级别(可选) * @returns {Array} 日志历史数组 */ Logger.getHistory(limit, level) // 使用示例 var allLogs = Logger.getHistory(); // 获取所有日志 var last100 = Logger.getHistory(100); // 获取最近100条 var errors = Logger.getHistory(null, Logger.LOG_LEVELS.ERROR); // 只获取错误日志 ``` #### 7. 清空历史 ```javascript /** * 清空日志历史 */ Logger.clearHistory() ``` #### 8. 日志统计 ```javascript /** * 获取日志统计信息 * @returns {Object} 统计对象 */ Logger.getStatistics() // 返回示例 { total: 1523, // 总日志数 byLevel: { // 按级别统计 0: 234, // TRACE 1: 567, // DEBUG 2: 456, // INFO 3: 123, // WARN 4: 89, // ERROR 5: 54 // FATAL }, startTime: 1697376000000, // 统计开始时间 duration: 3600000, // 运行时长(毫秒) rate: 0.42 // 日志频率(条/秒) } ``` #### 9. 重置统计 ```javascript /** * 重置日志统计 */ Logger.resetStatistics() ``` #### 10. 配置管理 ```javascript /** * 配置Logger * @param {Object} options - 配置选项 */ Logger.configure(options) // 使用示例 Logger.configure({ currentLevel: Logger.LOG_LEVELS.INFO, // 设置为INFO级别 enableTimestamp: true, // 启用时间戳 enableColors: true, // 启用颜色 timestampFormat: 'ISO', // 使用ISO格式 maxLogHistory: 500 // 最多保存500条历史 }); ``` ### 日志格式 **标准格式**: ``` [时间戳] [级别] [模块名:标签] 消息内容 ``` **输出示例**: ``` [2025-10-15T10:30:45.123Z] [INFO] [JinXianMahjong:GameController] 游戏开始 [2025-10-15T10:30:45.456Z] [DEBUG] [JinXianMahjong:MahjongService] 发牌完成 { playerCount: 4, tilesPerPlayer: 13 } [2025-10-15T10:30:46.789Z] [WARN] [JinXianMahjong:OperationManager] 玩家操作超时 { playerId: 'player001', timeout: 15000 } [2025-10-15T10:30:47.012Z] [ERROR] [JinXianMahjong:RpcHandler] RPC调用失败 { method: 'player_discard', errorCode: 2001 } ``` ### 实际使用场景 #### 场景1:游戏流程日志 ```javascript // GameController.js GameController.prototype.startGame = function() { Logger.info('GameController', '======= 开始新游戏 ======='); Logger.group('游戏初始化'); Logger.debug('GameController', '玩家数量:', this.players.length); Logger.debug('GameController', '房间配置:', this.roomtype); try { // 初始化游戏状态 Logger.time('初始化游戏状态'); this.gameState = GameStateManager.createGameState( this.roomcode, this.roomtype, this.getPlayerIds() ); Logger.timeEnd('初始化游戏状态'); Logger.info('GameController', '游戏状态创建成功'); // 初始化牌墙 Logger.time('初始化牌墙'); this.mahjongWall = MahjongWall.initialize(this.gameState); Logger.timeEnd('初始化牌墙'); Logger.info('GameController', '牌墙初始化完成'); // 发牌 Logger.time('发牌'); this.dealTiles(); Logger.timeEnd('发牌'); Logger.info('GameController', '发牌完成'); } catch (error) { Logger.error('GameController', '游戏初始化失败', { error: error.message, stack: error.stack }); throw error; } Logger.groupEnd(); Logger.info('GameController', '======= 游戏初始化完成 ======='); }; ``` #### 场景2:调试日志 ```javascript // MahjongGameService.js MahjongGameService.prototype.checkWinCondition = function(playerHand, gameState) { Logger.debug('MahjongGameService', '开始检查胡牌条件', { playerId: playerHand.playerId, handSize: playerHand.tiles.length }); var rules = gameState.rules; Logger.trace('MahjongGameService', '规则配置', { useJing: rules.gameRules.useJing, minFan: rules.winConditions.minFan }); // 胡牌检测 Logger.time('胡牌检测'); var winResult = WinDetectionFactory.detectWin(playerHand, gameState.jingInfo); Logger.timeEnd('胡牌检测'); Logger.debug('MahjongGameService', '胡牌检测结果', { canWin: winResult.canWin, patterns: winResult.patterns }); if (!winResult.canWin) { Logger.debug('MahjongGameService', '不满足胡牌条件'); return { canWin: false }; } // 检查分数 var score = ScoreCalculation.calculateWinScore( winResult.patterns, gameState.jingInfo, gameState ); Logger.debug('MahjongGameService', '计算得分', { totalScore: score.totalScore, minFan: rules.winConditions.minFan }); if (score.totalScore < rules.winConditions.minFan) { Logger.warn('MahjongGameService', '未达到起胡分数要求', { currentScore: score.totalScore, required: rules.winConditions.minFan }); return { canWin: false, reason: '未达到起胡分数要求(需' + rules.winConditions.minFan + '分)' }; } Logger.info('MahjongGameService', '胡牌条件满足', { playerId: playerHand.playerId, score: score.totalScore }); return { canWin: true, patterns: winResult.patterns, score: score }; }; ``` #### 场景3:错误日志 ```javascript // RpcHandler.js RpcHandler.prototype.handleRequest = function(pack, room, callback) { var method = pack.cmd; Logger.info('RpcHandler', 'RPC请求', { method: method, playerId: pack.playerid }); try { // 验证玩家 if (!this._validatePlayer(pack.playerid, room)) { Logger.warn('RpcHandler', '玩家验证失败', { playerId: pack.playerid, roomcode: room.roomcode }); return callback({ error: '玩家不存在' }); } // 执行RPC方法 var result = this._executeMethod(method, pack, room); Logger.debug('RpcHandler', 'RPC执行成功', { method: method, result: result }); callback(result); } catch (error) { Logger.error('RpcHandler', 'RPC执行失败', { method: method, playerId: pack.playerid, error: error.message, stack: error.stack }); callback({ error: '处理请求失败' }); } }; ``` --- ## ⚠️ ErrorHandler.js - 错误处理器 ### 核心功能 **文件位置**:`server/games2/jinxianmahjong/utils/ErrorHandler.js` **版本**:v1.0.0 **总行数**:659行 ErrorHandler提供了完整的错误管理功能: - ✅ 标准化错误代码系统 - ✅ 错误分类和级别管理 - ✅ 错误创建和格式化 - ✅ 错误统计和追踪 - ✅ 安全执行包装 - ✅ 错误恢复策略 ### 错误代码范围 ```javascript ERROR_CODE_RANGES: { CLIENT_START: 1000, // 客户端/接口错误 CLIENT_END: 1999, VALIDATION_START: 2000, // 数据验证错误 VALIDATION_END: 2999, GAME_LOGIC_START: 3000, // 游戏逻辑错误 GAME_LOGIC_END: 3999, BUSINESS_START: 4000, // 业务逻辑错误 BUSINESS_END: 4999, NETWORK_START: 5000, // 网络通信错误 NETWORK_END: 5999, SYSTEM_START: 9000 // 系统错误 } ``` ### 错误代码定义 #### 1. 框架接口错误(1000-1999) ```javascript INTERFACE_INVALID_PARAMS: 1001, // 接口参数无效 INTERFACE_ROOM_NOT_FOUND: 1002, // 房间不存在 INTERFACE_PLAYER_NOT_FOUND: 1003, // 玩家不存在 INTERFACE_CALLBACK_MISSING: 1004, // 回调函数缺失 INTERFACE_METHOD_NOT_IMPLEMENTED: 1005, // 接口方法未实现 INTERFACE_TIMEOUT: 1006, // 接口调用超时 INTERFACE_UNAUTHORIZED: 1007, // 接口未授权 INTERFACE_RATE_LIMITED: 1008, // 接口调用频率限制 ``` #### 2. 游戏逻辑错误(2000-2999) ```javascript GAME_INVALID_ACTION: 2001, // 无效的游戏操作 GAME_WRONG_PHASE: 2002, // 游戏阶段错误 GAME_PLAYER_LIMIT: 2003, // 玩家数量限制 GAME_ROOM_FULL: 2004, // 房间已满 GAME_ALREADY_STARTED: 2005, // 游戏已开始 GAME_NOT_STARTED: 2006, // 游戏未开始 GAME_INVALID_CARD: 2007, // 无效牌张 GAME_INVALID_OPERATION: 2008, // 无效操作 GAME_TIMEOUT: 2009, // 游戏操作超时 GAME_INSUFFICIENT_PLAYERS: 2010, // 玩家不足 ``` #### 3. 数据验证错误(3000-3999) ```javascript VALIDATION_ROOMTYPE: 3001, // RoomType格式错误 VALIDATION_CARD_DATA: 3002, // 牌张数据无效 VALIDATION_PLAYER_DATA: 3003, // 玩家数据无效 VALIDATION_ROOM_DATA: 3004, // 房间数据无效 VALIDATION_GAME_STATE: 3005, // 游戏状态无效 VALIDATION_CONFIG: 3006, // 配置数据无效 VALIDATION_MISSING_REQUIRED: 3007, // 缺少必需字段 VALIDATION_TYPE_MISMATCH: 3008, // 数据类型不匹配 ``` #### 4. 业务逻辑错误(4000-4999) ```javascript BUSINESS_ROOM_CLOSED: 4001, // 房间已关闭 BUSINESS_PLAYER_OFFLINE: 4002, // 玩家离线 BUSINESS_INSUFFICIENT_PERMISSION: 4003, // 权限不足 BUSINESS_DUPLICATE_ACTION: 4004, // 重复操作 BUSINESS_INVALID_STATE: 4005, // 无效状态 BUSINESS_RESOURCE_UNAVAILABLE: 4006, // 资源不可用 ``` #### 5. 网络通信错误(5000-5999) ```javascript NETWORK_CONNECTION_LOST: 5001, // 连接丢失 NETWORK_TIMEOUT: 5002, // 网络超时 NETWORK_INVALID_RESPONSE: 5003, // 无效响应 NETWORK_SERVER_ERROR: 5004, // 服务器错误 ``` #### 6. 系统错误(9000-9999) ```javascript SYSTEM_UNKNOWN: 9001, // 未知系统错误 SYSTEM_TIMEOUT: 9002, // 系统超时 SYSTEM_MEMORY_ERROR: 9003, // 内存错误 SYSTEM_CONFIGURATION_ERROR: 9004, // 配置错误 SYSTEM_INITIALIZATION_FAILED: 9005, // 初始化失败 SYSTEM_DEPENDENCY_ERROR: 9006 // 依赖错误 ``` ### 错误严重级别 ```javascript ERROR_LEVELS: { FATAL: 'FATAL', // 致命错误,系统无法继续运行 ERROR: 'ERROR', // 错误,功能无法正常执行 WARN: 'WARN', // 警告,可能影响功能 INFO: 'INFO' // 信息,仅用于记录 } ``` ### 核心API #### 1. 创建错误对象 ```javascript /** * 创建错误对象 * @param {number} code - 错误代码 * @param {string} message - 自定义错误消息(可选) * @param {Object} context - 错误上下文信息(可选) * @param {string} level - 错误级别(可选) * @returns {Object} 标准化错误对象 */ ErrorHandler.createError(code, message, context, level) // 使用示例 var error = ErrorHandler.createError( ErrorHandler.ERROR_CODES.GAME_INVALID_ACTION, '当前阶段不允许出牌', { playerId: 'player001', currentPhase: 'WAITING', expectedPhase: 'PLAYING' } ); // 返回的错误对象 { code: 2001, message: '当前阶段不允许出牌', level: 'ERROR', timestamp: 1697376000000, context: { playerId: 'player001', currentPhase: 'WAITING', expectedPhase: 'PLAYING' }, stack: '...堆栈信息...', module: 'JinXianMahjong', version: '1.0.0', category: 'GAME_LOGIC' } ``` #### 2. 安全执行函数 ```javascript /** * 包装函数执行,自动捕获错误 * @param {Function} func - 要执行的函数 * @param {Object} context - 执行上下文 * @param {Array} args - 函数参数 * @returns {Object} 执行结果 {success: boolean, result: any, error: Object} */ ErrorHandler.safeExecute(func, context, args) // 使用示例 var result = ErrorHandler.safeExecute( function() { return this.performRiskyOperation(); }, gameController, [] ); if (result.success) { console.log('操作成功:', result.result); } else { console.error('操作失败:', result.error); } ``` #### 3. 格式化错误信息 ```javascript /** * 格式化错误信息用于显示 * @param {Object} error - 错误对象 * @returns {string} 格式化后的错误信息 */ ErrorHandler.formatErrorMessage(error) // 使用示例 var error = ErrorHandler.createError(2001, '无效操作'); var message = ErrorHandler.formatErrorMessage(error); // 输出: "[ERROR:2001] 无效操作" ``` #### 4. 错误恢复 ```javascript /** * 尝试从错误中恢复 * @param {Object} error - 错误对象 * @param {Function} recoveryFunc - 恢复函数 * @returns {boolean} 是否成功恢复 */ ErrorHandler.tryRecover(error, recoveryFunc) // 使用示例 var recovered = ErrorHandler.tryRecover(error, function() { // 尝试恢复逻辑 return game.resetToLastValidState(); }); if (recovered) { Logger.info('ErrorHandler', '已成功从错误中恢复'); } else { Logger.error('ErrorHandler', '无法从错误中恢复'); } ``` #### 5. 错误统计 ```javascript /** * 获取错误统计信息 * @returns {Object} 统计对象 */ ErrorHandler.getStatistics() // 返回示例 { total: 45, byCode: { 2001: 12, 3001: 8, 4001: 5 }, byLevel: { FATAL: 1, ERROR: 25, WARN: 15, INFO: 4 }, recent: [...] // 最近100条错误 } ``` ### 实际使用场景 #### 场景1:接口参数验证 ```javascript // export.js - Export.makewar() export.makewar = function(room, roomtype) { try { // 参数验证 if (!room) { throw ErrorHandler.createError( ErrorHandler.ERROR_CODES.INTERFACE_INVALID_PARAMS, 'room参数不能为空', { functionName: 'makewar' } ); } if (!roomtype) { throw ErrorHandler.createError( ErrorHandler.ERROR_CODES.INTERFACE_INVALID_PARAMS, 'roomtype参数不能为空', { functionName: 'makewar' } ); } // 验证roomtype格式 var validation = RoomConfigUtils.validate(roomtype); if (!validation.isValid) { throw ErrorHandler.createError( ErrorHandler.ERROR_CODES.VALIDATION_ROOMTYPE, 'roomtype格式错误: ' + validation.errors.join(', '), { roomtype: roomtype, errors: validation.errors } ); } // 创建游戏 Logger.info('Export', '开始创建游戏'); var gameState = GameStateManager.createGameState( room.roomcode, roomtype, room.getPlayerIds() ); return { success: true, gameState: gameState }; } catch (error) { Logger.error('Export', 'makewar失败', { error: error.message, code: error.code }); return { success: false, error: ErrorHandler.formatErrorMessage(error) }; } }; ``` #### 场景2:游戏逻辑错误处理 ```javascript // OperationManager.js OperationManager.prototype.validateDiscard = function(playerId, tileCode) { // 检查游戏是否开始 if (this.gameState.phase !== 'PLAYING') { return ErrorHandler.createError( ErrorHandler.ERROR_CODES.GAME_WRONG_PHASE, '游戏未在进行中', { currentPhase: this.gameState.phase, expectedPhase: 'PLAYING' } ); } // 检查是否轮到该玩家 if (this.gameState.currentPlayerIndex !== this._getPlayerIndex(playerId)) { return ErrorHandler.createError( ErrorHandler.ERROR_CODES.GAME_INVALID_ACTION, '不是该玩家的回合', { playerId: playerId, currentPlayer: this.gameState.players[this.gameState.currentPlayerIndex].id } ); } // 检查玩家是否持有该牌 var player = this._getPlayer(playerId); if (!this._hasCard(player.handTiles, tileCode)) { return ErrorHandler.createError( ErrorHandler.ERROR_CODES.GAME_INVALID_CARD, '玩家手中没有该牌', { playerId: playerId, tileCode: tileCode, handTiles: player.handTiles.map(function(t) { return t.code; }) } ); } // 验证通过 return null; }; // 使用验证结果 OperationManager.prototype.performDiscard = function(playerId, tileCode) { var validationError = this.validateDiscard(playerId, tileCode); if (validationError) { Logger.warn('OperationManager', '出牌验证失败', { error: validationError.message, code: validationError.code }); return { success: false, error: validationError }; } // 执行出牌操作 // ... return { success: true }; }; ``` #### 场景3:安全执行包装 ```javascript // GameController.js GameController.prototype.processPlayerAction = function(playerId, action) { Logger.info('GameController', '处理玩家操作', { playerId: playerId, action: action.type }); // 使用安全执行包装 var result = ErrorHandler.safeExecute( function() { // 可能抛出异常的代码 return this._executeAction(playerId, action); }, this, [] ); if (!result.success) { // 操作失败,记录错误 Logger.error('GameController', '操作执行失败', { playerId: playerId, action: action.type, error: result.error.message }); // 尝试恢复 var recovered = ErrorHandler.tryRecover(result.error, function() { // 恢复到上一个有效状态 return this._rollbackToLastState(); }.bind(this)); if (recovered) { Logger.info('GameController', '已成功从错误中恢复'); } return { success: false, error: ErrorHandler.formatErrorMessage(result.error) }; } Logger.debug('GameController', '操作执行成功', { playerId: playerId, result: result.result }); return { success: true, result: result.result }; }; ``` --- ## 🛠️ shared/utils - 共享工具类 ### ArrayUtils.js - 数组操作工具 **功能**:提供数组操作的辅助方法 **常用方法**: ```javascript // 数组去重 ArrayUtils.unique(array) // 数组打乱(洗牌) ArrayUtils.shuffle(array) // 数组查找 ArrayUtils.findIndex(array, predicate) // 数组分组 ArrayUtils.groupBy(array, keyFunc) ``` ### CardSourceInfoHelper.js - 牌源信息辅助 **功能**:管理麻将牌的来源信息 **主要方法**: ```javascript // 创建牌源信息 CardSourceInfoHelper.create(source, round, turn) // 验证牌源信息 CardSourceInfoHelper.validate(sourceInfo) ``` ### GameContextHelper.js - 游戏上下文辅助 **功能**:管理游戏上下文信息 **主要方法**: ```javascript // 创建游戏上下文 GameContextHelper.create(roomcode, round, phase) // 更新上下文 GameContextHelper.update(context, updates) ``` ### GameStateHelper.js - 游戏状态辅助 **功能**:游戏状态的辅助操作 **主要方法**: ```javascript // 验证游戏状态 GameStateHelper.validate(gameState) // 克隆游戏状态 GameStateHelper.clone(gameState) // 比较游戏状态 GameStateHelper.compare(state1, state2) ``` ### MahjongCardUniqueId.js - 麻将牌唯一ID **功能**:生成和管理麻将牌的唯一标识 **主要方法**: ```javascript // 生成唯一ID(10-145) MahjongCardUniqueId.generate(code, index) // 从唯一ID获取牌码 MahjongCardUniqueId.getCode(uniqueId) // 验证唯一ID MahjongCardUniqueId.validate(uniqueId) ``` ### RoomConfigUtils.js - 房间配置工具 **功能**:房间配置的解析和验证(详见 [06-规则配置系统.md](../core/06-规则配置系统.md)) --- ## 💡 使用最佳实践 ### 1. 日志级别选择 ```javascript // ✓ 推荐:根据环境设置日志级别 if (process.env.NODE_ENV === 'production') { Logger.setLevel('WARN'); // 生产环境只记录警告和错误 } else { Logger.setLevel('DEBUG'); // 开发环境记录调试信息 } // ✓ 推荐:使用合适的日志级别 Logger.trace('Module', '详细跟踪信息'); // 非常详细,仅开发调试用 Logger.debug('Module', '调试信息'); // 开发时使用 Logger.info('Module', '正常运行信息'); // 关键流程节点 Logger.warn('Module', '警告信息'); // 可能的问题 Logger.error('Module', '错误信息'); // 功能异常 Logger.fatal('Module', '致命错误'); // 系统无法继续 // ✗ 不推荐:滥用日志级别 Logger.error('Module', '玩家进入房间'); // 这应该是INFO级别 Logger.info('Module', '系统崩溃'); // 这应该是FATAL级别 ``` ### 2. 日志标签规范 ```javascript // ✓ 推荐:使用清晰的标签 Logger.info('GameController', '游戏开始'); Logger.debug('MahjongService', '发牌完成'); Logger.warn('OperationManager', '操作超时'); // ✗ 不推荐:标签不清晰 Logger.info('GC', 'start'); // 太简短 Logger.info('游戏', '开始'); // 使用中文不便于过滤 ``` ### 3. 附加数据使用 ```javascript // ✓ 推荐:提供有用的上下文信息 Logger.error('RpcHandler', 'RPC调用失败', { method: 'player_discard', playerId: 'player001', errorCode: 2001, timestamp: Date.now() }); // ✗ 不推荐:数据过多或无用 Logger.info('Module', '操作', { // 包含整个gameState对象(太大) gameState: this.gameState }); ``` ### 4. 性能考虑 ```javascript // ✓ 推荐:仅在需要时记录日志 if (Logger.config.currentLevel <= Logger.LOG_LEVELS.DEBUG) { var expensiveData = this._calculateExpensiveData(); Logger.debug('Module', '详细数据', expensiveData); } // ✗ 不推荐:总是计算数据 var expensiveData = this._calculateExpensiveData(); Logger.debug('Module', '详细数据', expensiveData); // DEBUG被禁用时也会计算 ``` ### 5. 错误处理策略 ```javascript // ✓ 推荐:创建标准化错误 throw ErrorHandler.createError( ErrorHandler.ERROR_CODES.GAME_INVALID_ACTION, '具体的错误描述', { 有用的上下文信息 } ); // ✓ 推荐:使用安全执行 var result = ErrorHandler.safeExecute(riskyFunction, context, args); if (!result.success) { // 处理错误 } // ✗ 不推荐:直接抛出字符串 throw '出错了'; // 没有错误码,无法分类 // ✗ 不推荐:忽略错误 try { riskyOperation(); } catch (error) { // 什么都不做,错误被吞噬 } ``` ### 6. 日志和错误配合使用 ```javascript // ✓ 推荐:记录错误日志 try { performOperation(); } catch (error) { var standardError = ErrorHandler.createError( ErrorHandler.ERROR_CODES.GAME_INVALID_ACTION, error.message, { operation: 'performOperation' } ); Logger.error('Module', '操作失败', { error: standardError.message, code: standardError.code }); throw standardError; } // ✗ 不推荐:只记录日志或只抛出错误 try { performOperation(); } catch (error) { Logger.error('Module', error.message); // 只记录,不抛出 // 或 throw error; // 只抛出,不记录 } ``` --- ## 🔍 调试技巧 ### 1. 启用详细日志 ```javascript // 临时启用TRACE级别查看所有日志 Logger.setLevel('TRACE'); // 执行需要调试的代码 someFunction(); // 恢复原来的级别 Logger.setLevel('INFO'); ``` ### 2. 使用日志分组 ```javascript // 使用分组整理相关日志 Logger.group('===== 玩家操作处理 ====='); Logger.info('Operation', '验证玩家'); Logger.debug('Operation', '检查游戏状态'); Logger.debug('Operation', '执行操作'); Logger.info('Operation', '操作完成'); Logger.groupEnd(); ``` ### 3. 性能分析 ```javascript // 使用计时功能分析性能 Logger.time('完整游戏流程'); Logger.time('初始化'); initialize(); Logger.timeEnd('初始化'); Logger.time('发牌'); dealTiles(); Logger.timeEnd('发牌'); Logger.time('游戏进行'); playGame(); Logger.timeEnd('游戏进行'); Logger.timeEnd('完整游戏流程'); ``` ### 4. 查看错误历史 ```javascript // 获取最近的错误 var errors = ErrorHandler.getStatistics().recent; Logger.table(errors); // 分析错误分布 var stats = ErrorHandler.getStatistics(); Logger.info('ErrorStats', '错误统计', { total: stats.total, byLevel: stats.byLevel, topErrors: Object.keys(stats.byCode) .sort(function(a, b) { return stats.byCode[b] - stats.byCode[a]; }) .slice(0, 5) }); ``` ### 5. 日志过滤 ```javascript // 只查看特定级别的日志 var errorLogs = Logger.getHistory(null, Logger.LOG_LEVELS.ERROR); var warnLogs = Logger.getHistory(null, Logger.LOG_LEVELS.WARN); // 只查看最近的日志 var recent = Logger.getHistory(50); // 最近50条 // 自定义过滤 var gameLogs = Logger.getHistory().filter(function(log) { return log.tag.indexOf('Game') !== -1; }); ``` --- ## 📚 完整使用示例 ### 示例1:模块初始化 ```javascript // 模块入口文件 (function() { 'use strict'; // 配置Logger Logger.configure({ currentLevel: Logger.LOG_LEVELS.DEBUG, enableTimestamp: true, enableColors: true, moduleName: 'JinXianMahjong' }); Logger.info('System', '======= 进贤麻将系统启动 ======='); Logger.info('System', '版本:', '1.0.0'); Logger.info('System', '环境:', process.env.NODE_ENV || 'development'); // 初始化系统 try { Logger.group('系统初始化'); Logger.time('加载配置'); var config = loadConfiguration(); Logger.timeEnd('加载配置'); Logger.debug('System', '配置加载完成', config); Logger.time('初始化数据库'); initializeDatabase(); Logger.timeEnd('初始化数据库'); Logger.info('System', '数据库初始化完成'); Logger.time('注册游戏模块'); registerGameModule(); Logger.timeEnd('注册游戏模块'); Logger.info('System', '游戏模块注册完成'); Logger.groupEnd(); Logger.info('System', '======= 系统启动完成 ======='); } catch (error) { var systemError = ErrorHandler.createError( ErrorHandler.ERROR_CODES.SYSTEM_INITIALIZATION_FAILED, '系统初始化失败: ' + error.message, { error: error.stack }, ErrorHandler.ERROR_LEVELS.FATAL ); Logger.fatal('System', '系统启动失败', { error: systemError.message, code: systemError.code }); process.exit(1); } })(); ``` ### 示例2:完整的操作流程 ```javascript // 玩家出牌操作的完整流程 function handlePlayerDiscard(playerId, tileCode) { Logger.info('DiscardHandler', '======= 处理玩家出牌 =======', { playerId: playerId, tileCode: tileCode }); try { Logger.group('出牌验证'); // 1. 参数验证 Logger.debug('Validation', '验证参数'); if (!playerId || !tileCode) { throw ErrorHandler.createError( ErrorHandler.ERROR_CODES.INTERFACE_INVALID_PARAMS, '出牌参数无效', { playerId: playerId, tileCode: tileCode } ); } // 2. 玩家验证 Logger.debug('Validation', '验证玩家'); var player = getPlayer(playerId); if (!player) { throw ErrorHandler.createError( ErrorHandler.ERROR_CODES.INTERFACE_PLAYER_NOT_FOUND, '玩家不存在', { playerId: playerId } ); } // 3. 游戏状态验证 Logger.debug('Validation', '验证游戏状态'); var gameState = getGameState(); if (gameState.phase !== 'PLAYING') { throw ErrorHandler.createError( ErrorHandler.ERROR_CODES.GAME_WRONG_PHASE, '游戏未在进行中', { currentPhase: gameState.phase } ); } // 4. 回合验证 Logger.debug('Validation', '验证玩家回合'); if (!isPlayerTurn(playerId)) { throw ErrorHandler.createError( ErrorHandler.ERROR_CODES.GAME_INVALID_ACTION, '不是该玩家的回合', { playerId: playerId, currentPlayer: getCurrentPlayer() } ); } Logger.groupEnd(); Logger.info('Validation', '验证通过'); // 5. 执行出牌 Logger.group('执行出牌'); Logger.time('出牌操作'); var result = ErrorHandler.safeExecute( function() { return performDiscard(player, tileCode); }, this, [] ); Logger.timeEnd('出牌操作'); if (!result.success) { throw result.error; } Logger.info('Discard', '出牌成功', { playerId: playerId, tileCode: tileCode, discardedTile: result.result }); Logger.groupEnd(); // 6. 检查其他玩家的响应 Logger.group('检查玩家响应'); var responses = checkPlayerResponses(tileCode); Logger.debug('Response', '玩家响应', responses); Logger.groupEnd(); // 7. 广播消息 Logger.debug('Broadcast', '广播出牌消息'); broadcastDiscard(playerId, tileCode, responses); Logger.info('DiscardHandler', '======= 出牌处理完成 ======='); return { success: true, result: { playerId: playerId, tile: tileCode, responses: responses } }; } catch (error) { Logger.error('DiscardHandler', '出牌处理失败', { error: error.message, code: error.code, playerId: playerId, tileCode: tileCode }); // 记录错误统计 var stats = ErrorHandler.getStatistics(); Logger.warn('DiscardHandler', '当前错误统计', { total: stats.total, recent: stats.recent.length }); return { success: false, error: ErrorHandler.formatErrorMessage(error) }; } } ``` --- ## 🔗 相关文档链接 - **上一篇**:[06-规则配置系统.md](../core/06-规则配置系统.md) - 规则配置解析 - **下一篇**:[08-游戏流程概述.md](../architecture/08-游戏流程概述.md) - 完整游戏流程 - **参考**: - [03-RPC处理机制.md](../framework/03-RPC处理机制.md) - RPC错误处理 - [04-游戏核心服务.md](../core/04-游戏核心服务.md) - 服务层日志使用 --- ## 📝 附录 ### A. Logger配置选项完整列表 ```javascript { currentLevel: 0-5, // 当前日志级别 enableConsole: true/false, // 启用控制台输出 enableColors: true/false, // 启用颜色 enableTimestamp: true/false, // 启用时间戳 enableModuleName: true/false, // 启用模块名 enableStackTrace: true/false, // 启用堆栈跟踪 timestampFormat: 'ISO'/'LOCAL'/'TIMESTAMP', // 时间戳格式 moduleName: 'string', // 模块名 maxLogHistory: number, // 最大历史记录数 flushInterval: number // 刷新间隔(毫秒) } ``` ### B. 错误代码快速查询 | 范围 | 类别 | 说明 | |------|------|------| | 1000-1999 | 框架接口错误 | Export/Import接口相关 | | 2000-2999 | 游戏逻辑错误 | 游戏规则和操作相关 | | 3000-3999 | 数据验证错误 | 参数和数据格式验证 | | 4000-4999 | 业务逻辑错误 | 业务规则和状态相关 | | 5000-5999 | 网络通信错误 | 网络连接和通信相关 | | 9000-9999 | 系统错误 | 系统级别的严重错误 | ### C. 常用日志模式 ```javascript // 1. 方法入口/出口模式 function someMethod(param) { Logger.debug('Module', 'someMethod 入口', { param: param }); try { // 方法逻辑 var result = doSomething(param); Logger.debug('Module', 'someMethod 出口', { result: result }); return result; } catch (error) { Logger.error('Module', 'someMethod 异常', { error: error.message }); throw error; } } // 2. 状态变更模式 function changeState(newState) { Logger.info('StateManager', '状态变更', { from: this.currentState, to: newState }); this.currentState = newState; } // 3. 性能监控模式 function performanceMonitor(operation) { Logger.time(operation.name); var result = operation.execute(); Logger.timeEnd(operation.name); return result; } ``` --- **文档版本**:v1.0 **最后更新**:2025年10月15日 **维护者**:进贤麻将开发团队