37 KiB
37 KiB
工具模块详解
📋 文档概述
本文档详细说明进贤麻将的工具模块系统,包括:
- 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 # 房间配置工具
设计原则
- 分离关注点:日志和错误处理分开管理
- ES5兼容:兼容浏览器和Node.js环境
- 统一接口:提供统一的API调用方式
- 性能优化:最小化性能开销
- 易于使用:简单直观的API设计
📝 Logger.js - 日志管理器
核心功能
文件位置:server/games2/jinxianmahjong/utils/Logger.js
版本:v1.0.0
总行数:606行
Logger提供了完整的日志管理功能:
- ✅ 6级日志系统(TRACE/DEBUG/INFO/WARN/ERROR/FATAL)
- ✅ 日志格式化和美化输出
- ✅ 日志历史记录和查询
- ✅ 日志统计分析
- ✅ 性能计时功能
- ✅ 分组日志输出
- ✅ 控制台颜色支持
日志级别定义
LOG_LEVELS: {
TRACE: 0, // 最详细的信息,用于跟踪程序执行
DEBUG: 1, // 调试信息,开发时使用
INFO: 2, // 一般信息,记录程序运行状态
WARN: 3, // 警告信息,可能的问题
ERROR: 4, // 错误信息,程序运行错误
FATAL: 5 // 致命错误,程序无法继续运行
}
级别控制:只有 >= currentLevel 的日志才会输出
颜色编码:
| 级别 | 颜色 | 说明 |
|---|---|---|
| TRACE | 白色 | 详细跟踪信息 |
| DEBUG | 青色 | 开发调试信息 |
| INFO | 绿色 | 正常运行信息 |
| WARN | 黄色 | 警告提示信息 |
| ERROR | 红色 | 错误异常信息 |
| FATAL | 紫色 | 致命错误信息 |
配置参数
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. 设置日志级别
/**
* 设置日志级别
* @param {number|string} level - 日志级别(数字或字符串)
*/
Logger.setLevel(level)
// 使用示例
Logger.setLevel('DEBUG'); // 使用字符串
Logger.setLevel(1); // 使用数字
Logger.setLevel('INFO'); // 只输出INFO及以上级别
2. 记录日志
基础日志方法:
/**
* 记录日志
* @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)
使用示例:
// 基础日志
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. 分组日志
/**
* 分组日志(用于组织相关日志)
*/
Logger.group(groupName) // 开始分组
Logger.groupEnd() // 结束分组
Logger.groupCollapsed(groupName) // 开始折叠分组(默认折叠)
// 使用示例
Logger.group('游戏初始化');
Logger.info('Init', '加载配置');
Logger.info('Init', '创建游戏状态');
Logger.info('Init', '初始化牌墙');
Logger.groupEnd();
4. 性能计时
/**
* 性能计时
*/
Logger.time(label) // 开始计时
Logger.timeEnd(label) // 结束计时并输出
// 使用示例
Logger.time('胡牌检测');
var result = WinDetectionFactory.detectWin(handTiles, jingInfo);
Logger.timeEnd('胡牌检测'); // 输出: 胡牌检测: 3.142ms
5. 表格输出
/**
* 表格输出(用于输出结构化数据)
* @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. 日志历史
/**
* 获取日志历史
* @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. 清空历史
/**
* 清空日志历史
*/
Logger.clearHistory()
8. 日志统计
/**
* 获取日志统计信息
* @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. 重置统计
/**
* 重置日志统计
*/
Logger.resetStatistics()
10. 配置管理
/**
* 配置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:游戏流程日志
// 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:调试日志
// 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:错误日志
// 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提供了完整的错误管理功能:
- ✅ 标准化错误代码系统
- ✅ 错误分类和级别管理
- ✅ 错误创建和格式化
- ✅ 错误统计和追踪
- ✅ 安全执行包装
- ✅ 错误恢复策略
错误代码范围
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)
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)
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)
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)
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)
NETWORK_CONNECTION_LOST: 5001, // 连接丢失
NETWORK_TIMEOUT: 5002, // 网络超时
NETWORK_INVALID_RESPONSE: 5003, // 无效响应
NETWORK_SERVER_ERROR: 5004, // 服务器错误
6. 系统错误(9000-9999)
SYSTEM_UNKNOWN: 9001, // 未知系统错误
SYSTEM_TIMEOUT: 9002, // 系统超时
SYSTEM_MEMORY_ERROR: 9003, // 内存错误
SYSTEM_CONFIGURATION_ERROR: 9004, // 配置错误
SYSTEM_INITIALIZATION_FAILED: 9005, // 初始化失败
SYSTEM_DEPENDENCY_ERROR: 9006 // 依赖错误
错误严重级别
ERROR_LEVELS: {
FATAL: 'FATAL', // 致命错误,系统无法继续运行
ERROR: 'ERROR', // 错误,功能无法正常执行
WARN: 'WARN', // 警告,可能影响功能
INFO: 'INFO' // 信息,仅用于记录
}
核心API
1. 创建错误对象
/**
* 创建错误对象
* @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. 安全执行函数
/**
* 包装函数执行,自动捕获错误
* @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. 格式化错误信息
/**
* 格式化错误信息用于显示
* @param {Object} error - 错误对象
* @returns {string} 格式化后的错误信息
*/
ErrorHandler.formatErrorMessage(error)
// 使用示例
var error = ErrorHandler.createError(2001, '无效操作');
var message = ErrorHandler.formatErrorMessage(error);
// 输出: "[ERROR:2001] 无效操作"
4. 错误恢复
/**
* 尝试从错误中恢复
* @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. 错误统计
/**
* 获取错误统计信息
* @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:接口参数验证
// 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:游戏逻辑错误处理
// 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:安全执行包装
// 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 - 数组操作工具
功能:提供数组操作的辅助方法
常用方法:
// 数组去重
ArrayUtils.unique(array)
// 数组打乱(洗牌)
ArrayUtils.shuffle(array)
// 数组查找
ArrayUtils.findIndex(array, predicate)
// 数组分组
ArrayUtils.groupBy(array, keyFunc)
CardSourceInfoHelper.js - 牌源信息辅助
功能:管理麻将牌的来源信息
主要方法:
// 创建牌源信息
CardSourceInfoHelper.create(source, round, turn)
// 验证牌源信息
CardSourceInfoHelper.validate(sourceInfo)
GameContextHelper.js - 游戏上下文辅助
功能:管理游戏上下文信息
主要方法:
// 创建游戏上下文
GameContextHelper.create(roomcode, round, phase)
// 更新上下文
GameContextHelper.update(context, updates)
GameStateHelper.js - 游戏状态辅助
功能:游戏状态的辅助操作
主要方法:
// 验证游戏状态
GameStateHelper.validate(gameState)
// 克隆游戏状态
GameStateHelper.clone(gameState)
// 比较游戏状态
GameStateHelper.compare(state1, state2)
MahjongCardUniqueId.js - 麻将牌唯一ID
功能:生成和管理麻将牌的唯一标识
主要方法:
// 生成唯一ID(10-145)
MahjongCardUniqueId.generate(code, index)
// 从唯一ID获取牌码
MahjongCardUniqueId.getCode(uniqueId)
// 验证唯一ID
MahjongCardUniqueId.validate(uniqueId)
RoomConfigUtils.js - 房间配置工具
功能:房间配置的解析和验证(详见 06-规则配置系统.md)
💡 使用最佳实践
1. 日志级别选择
// ✓ 推荐:根据环境设置日志级别
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. 日志标签规范
// ✓ 推荐:使用清晰的标签
Logger.info('GameController', '游戏开始');
Logger.debug('MahjongService', '发牌完成');
Logger.warn('OperationManager', '操作超时');
// ✗ 不推荐:标签不清晰
Logger.info('GC', 'start'); // 太简短
Logger.info('游戏', '开始'); // 使用中文不便于过滤
3. 附加数据使用
// ✓ 推荐:提供有用的上下文信息
Logger.error('RpcHandler', 'RPC调用失败', {
method: 'player_discard',
playerId: 'player001',
errorCode: 2001,
timestamp: Date.now()
});
// ✗ 不推荐:数据过多或无用
Logger.info('Module', '操作', {
// 包含整个gameState对象(太大)
gameState: this.gameState
});
4. 性能考虑
// ✓ 推荐:仅在需要时记录日志
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. 错误处理策略
// ✓ 推荐:创建标准化错误
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. 日志和错误配合使用
// ✓ 推荐:记录错误日志
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. 启用详细日志
// 临时启用TRACE级别查看所有日志
Logger.setLevel('TRACE');
// 执行需要调试的代码
someFunction();
// 恢复原来的级别
Logger.setLevel('INFO');
2. 使用日志分组
// 使用分组整理相关日志
Logger.group('===== 玩家操作处理 =====');
Logger.info('Operation', '验证玩家');
Logger.debug('Operation', '检查游戏状态');
Logger.debug('Operation', '执行操作');
Logger.info('Operation', '操作完成');
Logger.groupEnd();
3. 性能分析
// 使用计时功能分析性能
Logger.time('完整游戏流程');
Logger.time('初始化');
initialize();
Logger.timeEnd('初始化');
Logger.time('发牌');
dealTiles();
Logger.timeEnd('发牌');
Logger.time('游戏进行');
playGame();
Logger.timeEnd('游戏进行');
Logger.timeEnd('完整游戏流程');
4. 查看错误历史
// 获取最近的错误
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. 日志过滤
// 只查看特定级别的日志
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:模块初始化
// 模块入口文件
(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:完整的操作流程
// 玩家出牌操作的完整流程
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 - 规则配置解析
- 下一篇:08-游戏流程概述.md - 完整游戏流程
- 参考:
- 03-RPC处理机制.md - RPC错误处理
- 04-游戏核心服务.md - 服务层日志使用
📝 附录
A. Logger配置选项完整列表
{
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. 常用日志模式
// 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日
维护者:进贤麻将开发团队