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

Object.defineProperty覆写console.debug时如何为整段日志着色

TypeScript日志类实现TODO日志全着色+正确行号方案

问题背景

我编写了一个日志类,支持在保留代码中日志调用语句的前提下关闭日志输出,同时可正确显示日志触发对应的行号与文件路径。
基于该类,我希望为TODO类型的日志设置醒目的显示颜色,但遇到两个核心问题:

  • 原生类方法todo()可正常给日志上色,但控制台输出的行号是类内部调用console.debug的位置,不是实际调用logger.todo()的业务代码位置
  • 通过Object.defineProperty覆写todo属性、用bind绑定控制台方法后,可以拿到正确的行号,但只有[TODO]标记被上色,其余日志文本是控制台默认颜色,且剩余参数会被打包成数组打印

原有问题代码

class Logger {
    // 注意:控制台需要勾选「All levels」才会显示debug级别日志
    public OFF = 0;
    public LOG = 1;
    public ALL = 9;
    
    private level = 1;
    
    public constructor() {
        /*this.init();*/ // 测试阶段暂时注释
    }
    
    // 原方法:颜色显示正常,行号定位错误
    public todo(message?: string, ...optionalParams: any[]) {
        const css = 'background: #FF0000; color: #0000FF';
        optionalParams.length != 0
            ? console.debug('%c[TODO] '+message, css, optionalParams) 
            : console.debug('%c[TODO] '+message, css);
    }    
     
    // 覆写方法:行号定位正确,颜色失效、参数格式错误
    /*private*/ public init() {
        Object.defineProperty(this, "todo", {
            get: function () {
                return this.level >= this.LOG 
                    ? console.debug.bind(console, '%c[TODO]', 'background: #FF0000; color: #0000FF') 
                    : function(){};
            },
            configurable: true
        });     
    }
    
    public setLevel(level: Level) {
        this.level = level;
        this.init();
    }
}

测试代码:

const logger = new Logger();

logger.todo('normal todo().');
logger.todo('normal todo() with data.', 666, 777);

logger.init();

logger.todo('modified todo().');
logger.todo('modified todo() with data.', 666, 777);

运行效果参考:
控制台运行截图


解决方案

完全可以在Object.defineProperty的实现方案下实现整条日志着色,只需要修正bind时传入的参数格式即可,不需要额外增加包装函数(包装函数会导致行号定位到包装函数内部,失去正确行号的能力)。

问题根因

  1. 颜色失效原因:控制台%c格式化标记的生效规则是:消耗紧随其后的第一个参数作为样式值,将样式应用到格式串中%c位置之后的所有字面量内容;如果后续还有格式串未匹配的额外追加参数,这些参数会默认使用控制台基础样式输出。原有代码中绑定的格式串是%c[TODO],因此只有格式串里的[TODO]字面量会应用样式,后续调用时传入的消息、额外参数都属于追加参数,走默认样式。
  2. 参数数组问题:参数被打包成数组是原有未覆写的todo方法写法导致的——方法内用剩余参数收集到optionalParams数组后,直接把数组作为单个参数传入console.debug,没有做展开处理。bind返回的函数本身不会做参数打包,调用时传入的所有参数会自然追加到绑定参数列表后。

修正后代码

只需要修改init方法中bind的入参,把样式标记单独作为格式串的第一个占位符,将[TODO]文本作为绑定的普通参数放在样式参数之后即可:

/*private*/ public init() {
    Object.defineProperty(this, "todo", {
        get: function () {
            const todoStyle = 'background: #FF0000; color: #0000FF';
            return this.level >= this.LOG 
                // 关键修改:%c单独占格式串第一位,后接样式、[TODO]标记,后续调用传入的所有参数都会自动应用该样式
                ? console.debug.bind(console, '%c', todoStyle, '[TODO] ') 
                : function(){};
        },
        configurable: true
    });     
}

可以删掉原来写的todo类方法避免初始化前逻辑不一致,记得在构造函数中调用this.init()完成初始化即可。

实现效果

  • 整条TODO日志(包括[TODO]标记、消息文本、所有额外参数)都会应用红底蓝字的醒目样式
  • 控制台输出的行号、文件路径完全对应调用logger.todo()的业务代码位置
  • 额外参数会自动展开输出,不会被打包成数组
  • 日志等级设为低于LOG时,todo方法为空函数,不会输出任何内容,符合日志开关需求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 09:24:19