如何实现保留行号、支持前缀及字符串替换的console Proxy?
实现带前缀、保留行号且支持字符串替换的Console Proxy
问题背景
我实现了一个Console Proxy用于给每条控制台输出添加前缀,现有两种方案但各有缺陷:
- 基于
bind的方案:能保留原始行号,但前缀会成为第一个参数,导致含%c/%s等替换标记的消息无法正确匹配后续参数; - 参数捕获转发的方案:能正确处理替换标记,但会丢失原始调用的行号,控制台显示代理函数的行号。
希望找到能同时兼顾添加前缀、保留原始行号、支持字符串替换的方案,也可接受放弃替换的折中方案。
初始方案(保留行号但破坏替换)
const disabledMethodsInProduction = ['debug', 'log', 'trace']; logger = new Proxy(console, { get (target, method) { if (method in target) { if (!developmentMode && disabledMethodsInProduction.includes(method)) { return () => { /* NOP */ }; } // 'loggingID' 为外部定义的字符串标识 return target[method].bind(console, `${loggingID}:`); } throw new Error(`Method '${method}()' not implemented in console.`); } });
参数捕获方案(支持替换但丢失行号)
return function () { const args = Array.from(arguments); if (typeof args[0] === 'string') { args[0] = `${loggingID}: ` + args[0]; } else { args.unshift(`${loggingID}:`); } target[method](...args); }
解决方案
方案1:兼顾所有需求(推荐)
利用new Function动态生成处理函数,浏览器会将调用该动态函数的位置作为控制台消息的来源行号,同时在函数内部正确拼接前缀、处理替换标记:
const disabledMethodsInProduction = ['debug', 'log', 'trace']; const loggingID = 'YOUR_LOG_ID'; // 替换为实际的标识字符串 logger = new Proxy(console, { get(target, method) { if (!(method in target)) { throw new Error(`Method '${method}()' not implemented in console.`); } if (!developmentMode && disabledMethodsInProduction.includes(method)) { return () => {}; } // 动态生成包装函数,确保行号指向原始调用位置 const wrappedFn = new Function('target', 'method', 'loggingID', ` return function (...args) { // 给第一个字符串参数添加前缀,非字符串则插入前缀作为第一个参数 if (args.length > 0 && typeof args[0] === 'string') { args[0] = \`\${loggingID}: \${args[0]}\`; } else { args.unshift(\`\${loggingID}:\`); } // 调用原始console方法 target[method](...args); }; `)(target, method, loggingID); return wrappedFn; } });
测试效果:
logger.log('Hello %s', 'World'); // 输出:YOUR_LOG_ID: Hello World,行号为当前代码行 logger.log('Hello %cWorld', 'color: red'); // 输出带红色样式的文本,行号正确 logger.log({ foo: 'bar' }); // 输出:YOUR_LOG_ID: {foo: "bar"},行号正确
该方案在Chrome、Firefox等现代浏览器中均能正常工作,完美满足所有需求。
方案2:折中方案(保留行号,放弃替换)
如果可以接受%c等替换标记失效,直接使用初始的bind方案即可,代码简单且能完美保留原始行号:
const disabledMethodsInProduction = ['debug', 'log', 'trace']; logger = new Proxy(console, { get (target, method) { if (method in target) { if (!developmentMode && disabledMethodsInProduction.includes(method)) { return () => { /* NOP */ }; } return target[method].bind(target, `${loggingID}:`); } throw new Error(`Method '${method}()' not implemented in console.`); } });
内容的提问来源于stack exchange,提问作者Raúl Núñez de Arenas Coronado
相关产品推荐
相关产品推荐

