You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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];
  }
}

关键说明

  1. Proxy代理机制:通过Proxy拦截log/error方法的调用,先执行addLog完成日志存储,再调用原生控制台方法。
  2. 保留原生行号:代理的是console.log.bind(console)后的原生函数,调用时会保留原调用处的行号信息,不会指向ConsoleLogger.ts文件。
  3. 修复之前的写法错误:你之前的尝试中,立即执行函数导致addLog仅在类实例化时执行一次,而非每次调用log时执行,Proxy的方式完美解决了这个问题。

内容的提问来源于stack exchange,提问作者Jaisy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 01:29:51