如何实现ConsoleLogger:保留控制台日志行信息同时完成日志存储
问题描述
我正在开发一个用于控制台日志记录的ConsoleLogger单例类,要求每次调用日志方法时,将新的日志对象添加到控制台历史数组中。当前实现代码如下:
export class ConsoleLogger { private static instance: ConsoleLogger | null = null; private logs: LogEntry[] = []; private maxLogs = 50; public static getInstance(): ConsoleLogger { if (this.instance === null) { this.instance = new ConsoleLogger(); } return this.instance; } private addLog(type: LogType, message: string): void { if (this.logs.length >= this.maxLogs) { this.logs.shift(); } this.logs.push({ type, message, timestamp: new Date() }); } public log = (function () { return Function.prototype.bind.call(console.log, console); })(); public error = (function () { return Function.prototype.bind.call(console.error, console); })(); }
我可以在.vue和.ts文件中通过ConsoleLogger.getInstance().log('console msg');调用该方法,此时日志的行信息能正确指向调用所在的.ts/.vue文件。但我无法在当前的log方法中调用this.addLog来存储日志;若修改log方法以使其能调用this.addLog,日志的行信息就会指向ConsoleLogger.ts文件,丢失正确的调用处信息。
我尝试了以下绑定写法:
public log = function (...args: any[]) { this.addLog("log", args.join(" ")); return Function.prototype.bind.call(console.log, console); }.bind(this)();
但调用consoleLogger.log("test test")时,args参数仍为空,无法保存日志内容。请问如何才能既不丢失正确的日志行信息,又能正常调用addLog方法完成日志存储?
解决方案
核心思路是通过Proxy代理原生console.log/console.error的绑定函数,既能在每次调用时执行日志存储逻辑,又能保留原生控制台输出的行号指向。
完整实现代码
// 先定义必要的类型 type LogType = 'log' | 'error'; interface LogEntry { type: LogType; message: string; timestamp: Date; } export class ConsoleLogger { private static instance: ConsoleLogger | null = null; private logs: LogEntry[] = []; private maxLogs = 50; public static getInstance(): ConsoleLogger { if (this.instance === null) { this.instance = new ConsoleLogger(); } return this.instance; } // 私有构造函数,确保单例模式无法被外部实例化 private constructor() {} private addLog(type: LogType, message: string): void { if (this.logs.length >= this.maxLogs) { this.logs.shift(); } this.logs.push({ type, message, timestamp: new Date() }); } // 用Proxy代理原生console.log,拦截调用执行日志存储 public log = new Proxy(console.log.bind(console), { apply: (target, _, args) => { this.addLog('log', args.join(' ')); return Reflect.apply(target, console, args); } }); // 同理处理error方法 public error = new Proxy(console.error.bind(console), { apply: (target, _, args) => { this.addLog('error', args.join(' ')); return Reflect.apply(target, console, args); } }); // 可选:提供获取日志历史的方法 public getLogs(): LogEntry[] { return [...this.logs]; } }
关键说明
- Proxy代理机制:通过
Proxy拦截log/error方法的调用,先执行addLog完成日志存储,再调用原生控制台方法。 - 保留原生行号:代理的是
console.log.bind(console)后的原生函数,调用时会保留原调用处的行号信息,不会指向ConsoleLogger.ts文件。 - 修复之前的写法错误:你之前的尝试中,立即执行函数导致
addLog仅在类实例化时执行一次,而非每次调用log时执行,Proxy的方式完美解决了这个问题。
内容的提问来源于stack exchange,提问作者Jaisy
相关产品推荐
相关产品推荐

