Files
esengine/packages/editor-core/src/Services/LogService.ts
YHH 32460ac133 feat(editor): 优化编辑器UI和改进核心功能 (#234)
* feat(editor): 优化编辑器UI和改进核心功能

* feat(editor): 优化编辑器UI和改进核心功能
2025-11-23 21:45:10 +08:00

220 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { IService } from '@esengine/ecs-framework';
import { Injectable, LogLevel } from '@esengine/ecs-framework';
export interface LogEntry {
id: number;
timestamp: Date;
level: LogLevel;
source: string;
message: string;
args: unknown[];
clientId?: string; // 远程客户端ID
}
export type LogListener = (entry: LogEntry) => void;
/**
* 编辑器日志服务
*
* 捕获框架和用户代码的所有日志输出并提供给UI层展示
*/
@Injectable()
export class LogService implements IService {
private logs: LogEntry[] = [];
private listeners: Set<LogListener> = new Set();
private nextId = Date.now(); // 使用时间戳作为起始ID避免重复
private maxLogs = 1000;
private pendingNotifications: LogEntry[] = [];
private notificationScheduled = false;
private originalConsole = {
log: console.log.bind(console),
debug: console.debug.bind(console),
info: console.info.bind(console),
warn: console.warn.bind(console),
error: console.error.bind(console)
};
constructor() {
this.interceptConsole();
}
/**
* 拦截控制台输出
*/
private interceptConsole(): void {
console.log = (...args: unknown[]) => {
this.addLog(LogLevel.Info, 'console', this.formatMessage(args), args);
this.originalConsole.log(...args);
};
console.debug = (...args: unknown[]) => {
this.addLog(LogLevel.Debug, 'console', this.formatMessage(args), args);
this.originalConsole.debug(...args);
};
console.info = (...args: unknown[]) => {
this.addLog(LogLevel.Info, 'console', this.formatMessage(args), args);
this.originalConsole.info(...args);
};
console.warn = (...args: unknown[]) => {
this.addLog(LogLevel.Warn, 'console', this.formatMessage(args), args);
this.originalConsole.warn(...args);
};
console.error = (...args: unknown[]) => {
this.addLog(LogLevel.Error, 'console', this.formatMessage(args), args);
this.originalConsole.error(...args);
};
window.addEventListener('error', (event) => {
this.addLog(
LogLevel.Error,
'error',
event.message,
[event.error]
);
});
window.addEventListener('unhandledrejection', (event) => {
this.addLog(
LogLevel.Error,
'promise',
`Unhandled Promise Rejection: ${event.reason}`,
[event.reason]
);
});
}
/**
* 格式化消息
*/
private formatMessage(args: unknown[]): string {
return args.map((arg) => {
if (typeof arg === 'string') return arg;
if (arg instanceof Error) return arg.message;
try {
return JSON.stringify(arg);
} catch {
return String(arg);
}
}).join(' ');
}
/**
* 添加日志
*/
private addLog(level: LogLevel, source: string, message: string, args: unknown[]): void {
const entry: LogEntry = {
id: this.nextId++,
timestamp: new Date(),
level,
source,
message,
args
};
this.logs.push(entry);
if (this.logs.length > this.maxLogs) {
this.logs.shift();
}
this.notifyListeners(entry);
}
/**
* 添加远程日志(从远程游戏接收)
*/
public addRemoteLog(level: LogLevel, message: string, timestamp?: Date, clientId?: string): void {
const entry: LogEntry = {
id: this.nextId++,
timestamp: timestamp || new Date(),
level,
source: 'remote',
message,
args: [],
clientId
};
this.logs.push(entry);
if (this.logs.length > this.maxLogs) {
this.logs.shift();
}
this.notifyListeners(entry);
}
/**
* 通知监听器批处理日志通知以避免在React渲染期间触发状态更新
*/
private notifyListeners(entry: LogEntry): void {
this.pendingNotifications.push(entry);
if (!this.notificationScheduled) {
this.notificationScheduled = true;
queueMicrotask(() => {
const notifications = [...this.pendingNotifications];
this.pendingNotifications = [];
this.notificationScheduled = false;
for (const notification of notifications) {
for (const listener of this.listeners) {
try {
listener(notification);
} catch (error) {
this.originalConsole.error('Error in log listener:', error);
}
}
}
});
}
}
/**
* 获取所有日志
*/
public getLogs(): LogEntry[] {
return [...this.logs];
}
/**
* 清空日志
*/
public clear(): void {
this.logs = [];
}
/**
* 订阅日志更新
*/
public subscribe(listener: LogListener): () => void {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
/**
* 设置最大日志数量
*/
public setMaxLogs(max: number): void {
this.maxLogs = max;
while (this.logs.length > this.maxLogs) {
this.logs.shift();
}
}
public dispose(): void {
console.log = this.originalConsole.log;
console.debug = this.originalConsole.debug;
console.info = this.originalConsole.info;
console.warn = this.originalConsole.warn;
console.error = this.originalConsole.error;
this.listeners.clear();
this.logs = [];
}
}