['log', 'warn', 'error'].forEach((methodName) => {
const originalMethod = console[methodName];
console[methodName] = (...args) => {
let initiator = 'unknown place';
try {
throw new Error();
} catch (e) {
if (typeof e.stack === 'string') {
let isFirst = true;
for (const line of e.stack.split('\n')) {
const matches = line.match(/^\s+at\s+(.*)/);
if (matches) {
if (!isFirst) { // first line - current function
// second line - caller (what we are looking for)
initiator = matches[1];
break;
}
isFirst = false;
}
}
}
}
originalMethod.apply(console, [...args, '\n', ` at ${initiator}`]);
};
});
Loading settings.json
at fs.readdirSync.filter.forEach (.../settings.js:21:13)
Server is running on http://localhost:3000 or http://127.0.0.1:3000
at Server.app.listen (.../index.js:67:11)
Loading settings.json
at fs.readdirSync.filter.forEach (D:\Users\Piyin\Projects\test\settings.js:21:13)
Server is running on http://localhost:3000 or http://127.0.0.1:3000
at Server.app.listen (D:\Users\Piyin\Projects\test\index.js:67:11)
['log','warn','error'].forEach((methodName) => {
const originalMethod = console[methodName];
console[methodName] = (...args) => {
try {
throw new Error();
} catch (error) {
originalMethod.apply(
console,
[
(
error
.stack // Grabs the stack trace
.split('\n')[2] // Grabs third line
.trim() // Removes spaces
.substring(3) // Removes three first characters ("at ")
.replace(__dirname, '') // Removes script folder path
.replace(/\s\(./, ' at ') // Removes first parentheses and replaces it with " at "
.replace(/\)/, '') // Removes last parentheses
),
'\n',
...args
]
);
}
};
});
下面是新的输出:
fs.readdirSync.filter.forEach at settings.js:21:13
Loading settings.json
Server.app.listen at index.js:67:11
Server is running on http://localhost:3000 or http://127.0.0.1:3000
下面是手工缩小的代码(240字节):
['log','warn','error'].forEach(a=>{let b=console[a];console[a]=(...c)=>{try{throw new Error}catch(d){b.apply(console,[d.stack.split('\n')[2].trim().substring(3).replace(__dirname,'').replace(/\s\(./,' at ').replace(/\)/,''),'\n',...c])}}});
8条答案
按热度按时间xmq68pz91#
对每个调用进行全栈跟踪有点麻烦,我刚刚改进了@noppa的解决方案,只打印发起者:
它还修补了其他方法(对Nodejs很有用,因为
warn
和error
不像Chrome那样带有堆栈跟踪)。因此,您的控制台将类似于:
9njqaruj2#
对于临时黑客来说,找到您想要删除的日志语句并不太困难,您可以自己覆盖
console.log
。它会打印出
这里有很多噪音,但是调用堆栈中的第二行
at foo (index.js:10:13)
应该会将您指向正确的位置。qoefvg9y3#
到目前为止,这个问题的所有解决方案都依赖于将堆栈跟踪作为一个字符串进行拆分和匹配,这将在将来该字符串的格式发生变化的情况下(不太可能)中断。受this gist on GitHub和这里的其他答案的启发,我想提供自己的解决方案:
与其他解决方案不同,此脚本
您可以使用chalk或color.js为
prefix
着色,但我不想在这里介绍这方面的依赖关系。上述脚本使用V8 API自定义堆栈跟踪。
callee
是一个CallSite
对象,如果要自定义prefix
,请使用以下方法:getThis
:返回this
的值getTypeName
:以字符串形式返回this
的类型。这是存储在this
的构造函数字段中的函数的名称(如果可用),否则是对象的[[Class]]
内部属性。getFunctionName
:返回当前函数的名称,通常是其name
属性。如果name
属性不可用,则尝试从函数的上下文推断名称。getMethodName
:返回this
的属性的名称或保存当前函数的原型之一getFileName
:如果此函数是在脚本中定义的,则返回脚本的名称getLineNumber
:如果此函数是在脚本中定义的,则返回当前行号getColumnNumber
:如果此函数是在脚本中定义的,则返回当前列号getEvalOrigin
:如果此函数是通过调用eval
创建的,则返回表示调用eval
的位置的字符串isToplevel
:这是顶级调用吗?也就是说,这是全局对象吗?isEval
:此调用是否发生在对eval
的调用所定义的代码中?isNative
:此调用是否使用本地V8代码?isConstructor
:这是构造函数调用吗?isAsync
:这是异步调用吗(即await
或Promise.all()
)?isPromiseAll
:这是对Promise.all()
的异步调用吗?getPromiseIndex
:返回在Promise.all()
中跟随的promise元素的索引,用于异步堆栈跟踪,或者如果CallSite
不是Promise.all()
调用,则返回null
。这个答案是an answer I just gave对类似问题的交叉发布,因为更多的人可能会找到这个页面。
wfypjpf44#
我发现Dmitry Druganov's answer非常不错,但是我在Windows 10(节点为8.9.4)上尝试了一下,效果不太好。它打印的是完整路径,类似于:
因此,我采取了说的答案,并作出了这些改进(从我的Angular 来看):
Error
,第二行是放置此脚本的位置)D:\Users\Piyin\Projects\test
)。* 注意:要使其正常工作,脚本应位于项目的主Javascript * 中at
Class.method at path/to/file:line:column
这就是:
下面是新的输出:
下面是手工缩小的代码(240字节):
oymdgrw75#
稍微修改了noppa的答案,这个版本将输出如下内容:
这是干净和方便的(特别是在VSCode中使用-这将把文件路径变成一个链接)。
这个代码段只能在NodeJS环境中正常工作...
dojqjjoe6#
将行号附加到日志的末尾
muk1a3rh7#
简单和详尽的解决方案,如果你想暂时找到日志的来源:
2wnc66cl8#
常量显示名称=(名称)=〉{
}
显示名称(“挤压”)