JavaScript 更改控制台调用栈

10

正确的行为

当我直接从一个函数中调用console.log()时,在dev-tools控制台中,从哪个函数和文件中调用的堆栈是正确的,与预期相同。

main.js:

function main() {
    // Works correctly
    console.log('Hello from main()!');
}

控制台:

Hello from main()!    ...    main.js:3                       

我想要什么

现在,当我添加一个名为debug.js的第二个文件并从那里调用console.log时,文件来源于我调用的地方是debug.js,这是正确的...但我需要debug()记录日志就像它是在main.js中调用的一样。某种方式,我需要修改调用者、堆栈或跟踪,以欺骗console.log()认为它是从main.js中调用的,而实际上是从debug.js中调用的。

代码

debug.js

function debug(msg) {
   console.log(msg)
}

main.js

function main() {
   debug('Hello world!') // debug() in debug.js
}

行为

当前行为:

Hello world!    ...    debug.js:2

我想要的行为:

Hello world!    ...    main.js:3

如果所有日志都是这种情况,您可以使用在主JS中放置的函数覆盖console.log函数。类似于:console.log = function () {console.log(arguments)}。我们在一个应用程序中使用类似的方法,使用单独的Logger.js文件覆盖控制台方法并向每个日志条目添加日期。在开发工具中,源文件始终为Logger.js。 - NickG
2
我理解你的问题,但我不理解它的目的。为什么要编写一个函数来写入参数化的console.log,并从另一个文件中调用它呢?如果确实是为了在开发、维护或修复过程中帮助使用'console',那么... - Nicolás Alarcón Rapela
4个回答

8

下面原本推荐的解决方案似乎已不再适用:


我认为这里的问题在于console log通常是从它运行的位置执行的。但是,如果您传回一个执行console log的函数,那么这可能会按照您希望的方式工作。您可以尝试以下代码吗?

尝试 #1

debug.js:

function debug(msg) {
   return (function(msg) { console.log(msg) })(msg)
}

main.js:

function main() {
   debug('Hello world!')
}

如果那个方法不行,您可以尝试这个:
第二种尝试
debug.js:
function debug() {
   return function(msg) { console.log(msg) }
}

main.js:

function main() {
   debug()('Hello world!')
}

更新

由于以上解决方案不再适用,以下是一些可行的替代方案:

共有 2 种方法:

1. console.trace()

解决此问题的一个变通方法是仅使用 console.trace() 而非 console.log(), 这将记录整个调用堆栈以及被记录的数据,因此 debug() 的写法如下:

function debug(msg) {
  console.trace(msg);
}

2. Error().stack

或者,如果您只想获取呼叫栈中的第一个项目,则可以从新的Error()中提取整个呼叫栈,然后将其记录到您的数据中,可能类似于以下内容:

function debug(msg) {
  const stackTrace = Error().stack.split('\n    at ').slice(1);
  console.log(msg, `\n\ntriggered by ${stackTrace.at(-1)}`);
}

无论是哪种情况,main.js 只需使用 debug('Hello world!'); 即可。

尝试 #1 在 Firefox 87 中对我有效。非常感谢! - tscpp
不错!谢谢你告诉我 @tscpp。我自己也一直在想这个问题,所以很高兴看到有一个可行的解决方案。 - Brandon McConnell
1
@Sebastian 我可以确认这个...不再起作用了。不过别担心,这个问题似乎比我最初预期或描述的要容易解决得多。如果需要完整的调用堆栈,只需使用console.trace()而不是console.log(),或者如果你只想要调用堆栈的顶部,可以通过从新的Error()中获取调用堆栈来记录调用堆栈的最后一个项目。将所有这些注释添加到我的修订答案中。 - Brandon McConnell
1
@Sebastian 好主意。已回滚并将新解决方案包含在旧方案下面。 - Brandon McConnell
1
@BrandonMcConnell 嗯,我的理解是作者希望重新定义的log函数具有与原始log函数相同的行为,这将是[1]。但我同意,根据用例,(-1)也可能很有趣,因此可能需要解决哪个是所需的两个之一... - Sebastian
显示剩余7条评论

4
根据WHATWG规范,每个函数(错误、警告、日志等)的控制台输出是实现特定的:

打印机操作是实现定义的

正如发生的那样,基于Chromium的浏览器在打印console.log的结果时显示当前调用堆栈的frame(而不是完整的调用堆栈),您无法更改此行为,因为这与JavaScript引擎(基于Chromium的浏览器使用V8)有关,且无法通过JavaScript代码进行自定义。
唯一允许您显示完整调用堆栈的JavaScript标准是console.trace,其规范在此处:https://console.spec.whatwg.org/#trace 对于您的示例代码,它将显示类似于以下内容:

console.trace in a chromium based browser


1
这可以通过创建一个Error实例new Error()来完成,它将以字符串形式保存跟踪信息,如下所示。
function debug(...args) {
    const error = new Error();
    console.log(...args, error.stack.replace(/.+\n.+\n/, ''))
}

尽管MDN错误堆栈文档称其为非标准属性,但似乎所有浏览器都支持该浏览器兼容性


当然,我也是这么做的,error 包含完整的堆栈跟踪信息,以多行字符串格式呈现。用新的前两行替换它即可查看调用此 debug 方法的堆栈跟踪,我认为这就是你期望的结果。你可以按照自己的需求使用这个字符串。 - Ravikumar
正如我所说,我只想更改从console.log记录的调用堆栈。你的示例所做的是记录堆栈/其部分。 - tscpp

0
你可以做的是:
const log = console.log // for future use, preserve the default function
console.log = (...args) => {
    console.trace(...args)
}

On Node:

console.log = (...args) => {
    console.trace(...args, path.basename(__filename))
}

要得到像这样的东西:

const path = require("path");
console.log = (...args) => {
  console.trace(...args, path.basename(__filename))
}

function f() {
  return function g() {
    console.log("test")
  }
}

f()()

Guerric P已经介绍了console.trace。但那不是我想要的。我想要改变当前帧/堆栈/位置,从中调用console.log - tscpp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接