日志系统
ECS 框架提供了功能强大的分级日志系统,支持多种日志级别、颜色输出、自定义前缀和灵活的配置选项。日志系统可以帮助开发者调试代码和监控应用运行状态。
日志系统包含以下核心概念:
- 日志级别:Debug < Info < Warn < Error < Fatal < None
- 日志器:具名的日志输出器,每个模块可以有自己的日志器
- 日志管理器:全局管理所有日志器的单例
- 颜色配置:支持控制台颜色输出
import { LogLevel } from '@esengine/ecs-framework';
// 日志级别从低到高LogLevel.Debug // 0 - 调试信息LogLevel.Info // 1 - 一般信息LogLevel.Warn // 2 - 警告信息LogLevel.Error // 3 - 错误信息LogLevel.Fatal // 4 - 致命错误LogLevel.None // 5 - 不输出任何日志使用默认日志器
Section titled “使用默认日志器”import { Logger } from '@esengine/ecs-framework';
class GameSystem extends EntitySystem { protected process(entities: readonly Entity[]): void { // 输出不同级别的日志 Logger.debug('处理实体数量:', entities.length); Logger.info('系统正常运行'); Logger.warn('检测到性能问题'); Logger.error('处理过程中发生错误', new Error('示例错误')); Logger.fatal('致命错误,系统即将停止'); }}创建命名日志器
Section titled “创建命名日志器”import { createLogger } from '@esengine/ecs-framework';
class MovementSystem extends EntitySystem { private logger = createLogger('MovementSystem');
protected process(entities: readonly Entity[]): void { this.logger.info(`处理 ${entities.length} 个移动实体`);
for (const entity of entities) { const position = entity.getComponent(Position); const velocity = entity.getComponent(Velocity);
if (position && velocity) { position.x += velocity.dx * Time.deltaTime; position.y += velocity.dy * Time.deltaTime;
this.logger.debug(`实体 ${entity.id} 移动到位置 (${position.x}, ${position.y})`); } } }
protected onAdded(entity: Entity): void { this.logger.info(`实体 ${entity.name} 加入移动系统`); }
protected onRemoved(entity: Entity): void { this.logger.warn(`实体 ${entity.name} 离开移动系统`); }}系统内置日志器
Section titled “系统内置日志器”框架的各个系统都有自己的日志器:
// 框架内部使用示例class Scene { private static readonly _logger = createLogger('Scene');
public addSystem(system: EntitySystem): void { Scene._logger.info(`添加系统: ${system.systemName}`); // 系统添加逻辑 }
public removeSystem(system: EntitySystem): void { Scene._logger.warn(`移除系统: ${system.systemName}`); // 系统移除逻辑 }}设置全局日志级别
Section titled “设置全局日志级别”import { setGlobalLogLevel, LogLevel } from '@esengine/ecs-framework';
// 在开发环境显示所有日志setGlobalLogLevel(LogLevel.Debug);
// 在生产环境只显示警告及以上级别setGlobalLogLevel(LogLevel.Warn);
// 完全禁用日志输出setGlobalLogLevel(LogLevel.None);创建自定义配置的日志器
Section titled “创建自定义配置的日志器”import { ConsoleLogger, LogLevel } from '@esengine/ecs-framework';
class CustomLoggerExample { private debugLogger: ConsoleLogger; private productionLogger: ConsoleLogger;
constructor() { // 开发环境日志器 this.debugLogger = new ConsoleLogger({ level: LogLevel.Debug, enableTimestamp: true, enableColors: true, prefix: 'DEV' });
// 生产环境日志器 this.productionLogger = new ConsoleLogger({ level: LogLevel.Error, enableTimestamp: true, enableColors: false, prefix: 'PROD' }); }
public logDevelopmentInfo(): void { this.debugLogger.debug('这是调试信息'); this.debugLogger.info('开发环境信息'); }
public logProductionError(): void { this.productionLogger.error('生产环境错误'); this.productionLogger.fatal('致命错误'); }}使用预定义颜色
Section titled “使用预定义颜色”import { Colors, setLoggerColors } from '@esengine/ecs-framework';
// 自定义颜色方案setLoggerColors({ debug: Colors.BRIGHT_BLACK, info: Colors.BLUE, warn: Colors.YELLOW, error: Colors.RED, fatal: Colors.BRIGHT_RED});完整颜色示例
Section titled “完整颜色示例”import { LoggerManager, Colors, LogLevel } from '@esengine/ecs-framework';
class ColorLoggerDemo { private logger = createLogger('ColorDemo');
constructor() { // 设置自定义颜色 const manager = LoggerManager.getInstance(); manager.setGlobalColors({ debug: Colors.CYAN, info: Colors.GREEN, warn: Colors.YELLOW, error: Colors.RED, fatal: `${Colors.BOLD}${Colors.BRIGHT_RED}` }); }
public demonstrateColors(): void { this.logger.debug('这是蓝绿色的调试信息'); this.logger.info('这是绿色的信息'); this.logger.warn('这是黄色的警告'); this.logger.error('这是红色的错误'); this.logger.fatal('这是加粗的亮红色致命错误'); }
public resetToDefaults(): void { // 重置为默认颜色 LoggerManager.getInstance().resetColors(); }}import { LoggerManager } from '@esengine/ecs-framework';
class HierarchicalLoggingExample { private systemLogger = createLogger('GameSystems'); private movementLogger: ILogger; private renderLogger: ILogger;
constructor() { const manager = LoggerManager.getInstance();
// 创建子日志器 this.movementLogger = manager.createChildLogger('GameSystems', 'Movement'); this.renderLogger = manager.createChildLogger('GameSystems', 'Render'); }
public demonstrateHierarchy(): void { this.systemLogger.info('游戏系统启动');
// 子日志器会显示完整路径:[GameSystems.Movement] this.movementLogger.debug('移动系统初始化');
// 子日志器会显示完整路径:[GameSystems.Render] this.renderLogger.info('渲染系统启动'); }}集成第三方日志库
Section titled “集成第三方日志库”通过 setLoggerFactory 可以将业务代码中的日志器替换为第三方日志库(如 winston、pino、nestjs Logger 等)。
说明: 目前框架内部日志仍使用 ConsoleLogger,自定义日志器仅影响业务代码(如 EntitySystem)。
import { setLoggerFactory } from '@esengine/ecs-framework';
setLoggerFactory((name?: string) => { // 返回实现 ILogger 接口的日志器实例 return yourLogger;});// 集成 WinstonsetLoggerFactory((name?: string) => winston.createLogger({ /* ... */ }));
// 集成 PinosetLoggerFactory((name?: string) => pino({ name }));
// 集成 NestJS LoggersetLoggerFactory((name?: string) => new Logger(name));EntitySystem 中的使用
Section titled “EntitySystem 中的使用”EntitySystem 会自动使用类名创建日志器:
class PlayerMovementSystem extends EntitySystem { // this.logger 自动使用 'PlayerMovementSystem' 作为名称
protected process(entities: readonly Entity[]): void { this.logger.info(`处理 ${entities.length} 个玩家实体`); }}import { ConsoleLogger, LogLevel } from '@esengine/ecs-framework';
class CustomOutputLogger { private fileLogger: ConsoleLogger; private networkLogger: ConsoleLogger;
constructor() { // 输出到文件的日志器(模拟) this.fileLogger = new ConsoleLogger({ level: LogLevel.Info, output: (level: LogLevel, message: string) => { this.writeToFile(LogLevel[level], message); } });
// 发送到网络的日志器(模拟) this.networkLogger = new ConsoleLogger({ level: LogLevel.Error, output: (level: LogLevel, message: string) => { this.sendToServer(LogLevel[level], message); } }); }
private writeToFile(level: string, message: string): void { // 模拟文件写入 console.log(`[FILE] ${level}: ${message}`); }
private sendToServer(level: string, message: string): void { // 模拟网络发送 console.log(`[NETWORK] ${level}: ${message}`); }
public logToFile(message: string): void { this.fileLogger.info(message); }
public logCriticalError(error: Error): void { this.networkLogger.error('Critical error occurred', error); }}实际应用示例
Section titled “实际应用示例”游戏系统日志
Section titled “游戏系统日志”class GameWithLogging { private gameLogger = createLogger('Game'); private performanceLogger = createLogger('Performance'); private networkLogger = createLogger('Network');
constructor() { // 在开发环境启用详细日志 if (process.env.NODE_ENV === 'development') { setGlobalLogLevel(LogLevel.Debug); } else { setGlobalLogLevel(LogLevel.Warn); } }
public startGame(): void { this.gameLogger.info('游戏开始启动');
try { this.initializeSystems(); this.loadResources(); this.startGameLoop();
this.gameLogger.info('游戏启动成功'); } catch (error) { this.gameLogger.fatal('游戏启动失败', error); throw error; } }
private initializeSystems(): void { this.gameLogger.debug('初始化游戏系统');
const systems = [ new MovementSystem(), new RenderSystem(), new PhysicsSystem() ];
for (const system of systems) { const startTime = performance.now();
// 初始化系统 system.initialize();
const endTime = performance.now(); this.performanceLogger.debug( `系统 ${system.systemName} 初始化耗时: ${(endTime - startTime).toFixed(2)}ms` ); } }
private loadResources(): void { this.gameLogger.info('开始加载资源');
const resources = ['textures', 'sounds', 'data']; for (const resource of resources) { try { this.loadResource(resource); this.gameLogger.debug(`资源 ${resource} 加载成功`); } catch (error) { this.gameLogger.error(`资源 ${resource} 加载失败`, error); } } }
private startGameLoop(): void { this.gameLogger.info('启动游戏循环'); this.performanceLogger.debug('开始性能监控'); }
private loadResource(name: string): void { // 模拟资源加载 if (Math.random() < 0.1) { throw new Error(`Failed to load ${name}`); } }
public handleNetworkEvent(event: string, data: any): void { this.networkLogger.info(`网络事件: ${event}`, data);
if (event === 'connection_lost') { this.networkLogger.warn('网络连接丢失,尝试重连'); } else if (event === 'sync_error') { this.networkLogger.error('数据同步错误', data); } }}错误追踪和调试
Section titled “错误追踪和调试”class ErrorTrackingSystem extends EntitySystem { private logger = createLogger('ErrorTracker'); private errorCounts = new Map<string, number>();
protected process(entities: readonly Entity[]): void { for (const entity of entities) { try { this.processEntity(entity); } catch (error) { this.handleError(entity, error as Error); } } }
private processEntity(entity: Entity): void { // 模拟可能出错的实体处理 if (Math.random() < 0.01) { // 1% 概率出错 throw new Error(`Processing error for entity ${entity.id}`); }
this.logger.debug(`成功处理实体 ${entity.id}`); }
private handleError(entity: Entity, error: Error): void { const errorKey = error.message; const count = this.errorCounts.get(errorKey) || 0; this.errorCounts.set(errorKey, count + 1);
this.logger.error( `实体 ${entity.id} 处理失败 (第${count + 1}次): ${error.message}`, { entityId: entity.id, entityName: entity.name, componentCount: entity.components.length, errorStack: error.stack } );
// 如果同一类型错误发生太多次,升级为警告 if (count >= 5) { this.logger.warn(`错误 "${errorKey}" 已发生 ${count + 1} 次,可能需要关注`); }
// 如果错误次数过多,升级为致命错误 if (count >= 20) { this.logger.fatal(`错误 "${errorKey}" 发生次数过多,系统可能存在严重问题`); } }
public getErrorSummary(): void { this.logger.info('=== 错误统计 ==='); for (const [error, count] of this.errorCounts) { this.logger.info(`${error}: ${count} 次`); } }}1. 合理的日志级别选择
Section titled “1. 合理的日志级别选择”class LoggingBestPractices { private logger = createLogger('BestPractices');
public demonstrateLogLevels(): void { // ✅ Debug - 详细的调试信息 this.logger.debug('变量值', { x: 10, y: 20 });
// ✅ Info - 重要的状态变化 this.logger.info('系统启动完成');
// ✅ Warn - 异常但不致命的情况 this.logger.warn('资源未找到,使用默认值');
// ✅ Error - 错误但程序可以继续 this.logger.error('保存失败,将重试', new Error('Network timeout'));
// ✅ Fatal - 致命错误,程序无法继续 this.logger.fatal('内存不足,程序即将退出'); }}2. 结构化日志数据
Section titled “2. 结构化日志数据”class StructuredLogging { private logger = createLogger('Structured');
public logWithStructuredData(): void { // ✅ 提供结构化的上下文信息 this.logger.info('用户操作', { userId: 12345, action: 'move', position: { x: 100, y: 200 }, timestamp: Date.now() });
// ✅ 包含相关的错误上下文 this.logger.error('数据库查询失败', { query: 'SELECT * FROM users', parameters: { id: 123 }, connectionId: 'conn_456', retryCount: 3 }); }}3. 避免日志性能问题
Section titled “3. 避免日志性能问题”class PerformanceConsciousLogging { private logger = createLogger('Performance');
public efficientLogging(): void { // ✅ 检查日志级别避免不必要的计算 if (this.logger.debug) { const expensiveData = this.calculateExpensiveDebugInfo(); this.logger.debug('详细调试信息', expensiveData); }
// ❌ 避免:总是计算昂贵的日志数据 // this.logger.debug('调试信息', this.calculateExpensiveDebugInfo()); }
private calculateExpensiveDebugInfo(): any { // 模拟昂贵的计算 return { /* 复杂的调试数据 */ }; }}4. 日志配置管理
Section titled “4. 日志配置管理”class LoggingConfiguration { public static setupLogging(): void { // 根据环境配置日志级别 const isDevelopment = process.env.NODE_ENV === 'development'; const isProduction = process.env.NODE_ENV === 'production';
if (isDevelopment) { setGlobalLogLevel(LogLevel.Debug); setLoggerColors({ debug: Colors.CYAN, info: Colors.GREEN, warn: Colors.YELLOW, error: Colors.RED, fatal: Colors.BRIGHT_RED }); } else if (isProduction) { setGlobalLogLevel(LogLevel.Warn); // 生产环境禁用颜色 LoggerManager.getInstance().resetColors(); } }}
// 在应用启动时配置日志LoggingConfiguration.setupLogging();日志系统是调试和监控应用的重要工具,正确使用日志系统能大大提高开发效率和问题排查能力。