有一个旧应用程序,使用console.log
打印出大量消息,但我找不到调用哪些文件和行console.log
。
有没有办法挂钩应用并显示文件名和行号?
答案 0 :(得分:13)
每次通话都有完整的堆栈跟踪有点吵。我刚刚改进了@noppa的解决方案,只打印启动器:
['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}`]);
};
});
它还修补了其他方法(对于Nodej很有用,因为warn
和error
没有像Chrome一样提供堆栈跟踪)。
所以你的控制台看起来像是:
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)
答案 1 :(得分:10)
对于临时hack来找到你想要删除的日志语句,自己覆盖console.log
并不太难。
var log = console.log;
console.log = function() {
log.apply(console, arguments);
// Print the stack trace
console.trace();
};
// Somewhere else...
function foo(){
console.log('Foobar');
}
foo();
这将打印类似
的内容Foobar
Trace
at Console.console.log (index.js:4:13)
at foo (index.js:10:13)
at Object.<anonymous> (index.js:12:1)
...
那里有很多噪音但是调用堆栈中的第二行at foo (index.js:10:13)
应该指向正确的位置。
答案 2 :(得分:5)
到目前为止,此问题的所有解决方案都依赖于将堆栈跟踪作为字符串进行拆分和匹配,这在将来不太可能更改该字符串的格式的情况下(不太可能)中断。受this gist on GitHub和其他答案的启发,我想提供自己的解决方案:
'use strict';
const path = require('path');
['debug', 'log', 'warn', 'error'].forEach((methodName) => {
const originalLoggingMethod = console[methodName];
console[methodName] = (firstArgument, ...otherArguments) => {
const originalPrepareStackTrace = Error.prepareStackTrace;
Error.prepareStackTrace = (_, stack) => stack;
const callee = new Error().stack[1];
Error.prepareStackTrace = originalPrepareStackTrace;
const relativeFileName = path.relative(process.cwd(), callee.getFileName());
const prefix = `${relativeFileName}:${callee.getLineNumber()}:`;
if (typeof firstArgument === 'string') {
originalLoggingMethod(prefix + ' ' + firstArgument, ...otherArguments);
} else {
originalLoggingMethod(prefix, firstArgument, ...otherArguments);
}
};
});
// Tests:
console.log('%s %d', 'hi', 42);
console.log({ a: 'foo', b: 'bar'});
与其他解决方案不同,此脚本
您可以使用chalk或color.js给prefix
上色,但是我不想在这里引入相关性。
上面的脚本使用V8 API to customize stack traces。 callee
是一个CallSite
对象,可以通过以下方法自定义prefix
:
getThis
:返回this
的值getTypeName
:以字符串形式返回this
的类型。这是存储在this
的构造函数字段中的函数的名称(如果有),否则是对象的[[Class]]
的内部属性。getFunction
:返回当前函数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元素的索引,如果null
不是{{1},则返回CallSite
}。
此答案是an answer I just gave的交叉帖子,与更多人可能会发现此页面的问题类似。
答案 3 :(得分:3)
我发现Dmitry Druganov's answer非常好,但我在Windows 10(使用Node 8.9.4)上尝试过它并且它运行良好。它打印了完整的路径,如:
CentOS 7
所以我接受了所说的答案并做了这些改进(从我的观点来看):
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)
,第二行是放置此脚本的地方)Error
)。 注意:为了使其运行良好,脚本应该位于项目的主要Javascript D:\Users\Piyin\Projects\test
at
这是:
Class.method at path/to/file:line:column
这是新输出:
['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
]
);
}
};
});
这里是手写代码(240字节):
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
答案 4 :(得分:0)
略微修改了noppa答案的版本,该版本将输出如下内容:
/file/in-which/console/is/called.js:75:23
The stuff you want to log.
这既干净又方便(特别是在VSCode中使用-它将把文件路径变成链接)。
const { log } = console;
function proxiedLog(...args) {
const line = (((new Error('log'))
.stack.split('\n')[2] || '…')
.match(/\(([^)]+)\)/) || [, 'not found'])[1];
log.call(console, `${line}\n`, ...args);
}
console.info = proxiedLog;
console.log = proxiedLog;
// test
console.log('Hello!');
该代码段仅在NodeJS环境中才能正常运行...