How to get console.log line numbers shown in Nodejs?
Having full stack trace for each call is a bit noisy. I've just improved the @noppa's solution to print only the initiator:
['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}`]);
};
});
It also patches other methods (useful for Nodejs, since warn
and error
don't come with a stack trace as in Chrome).
So your console would look something like:
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)
For a temporary hack to find the log statements that you want to get rid of, it's not too difficult to override console.log
yourself.
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();
That will print something like
Foobar
Trace
at Console.console.log (index.js:4:13)
at foo (index.js:10:13)
at Object.<anonymous> (index.js:12:1)
...
A lot of noise in there but the second line in the call stack, at foo (index.js:10:13)
, should point you to the right place.
All solutions to this question so far rely on splitting and matching the stack trace as a string, which will break in (the unlikely) case the format of that string is changed in the future. Inspired by this gist on GitHub and the other answers here, I want to provide my own solution:
'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'});
Unlike the other solutions, this script
- outputs no additional lines and
- handles string substitutions correctly.
You can color the prefix
with chalk or color.js, but I didn't want to introduce dependencies for this here.
The above script uses the V8 API to customize stack traces. The callee
is a CallSite
object with the following methods in case you want to customize the prefix
:
getThis
: returns the value ofthis
getTypeName
: returns the type ofthis
as a string. This is the name of the function stored in the constructor field ofthis
, if available, otherwise the object’s[[Class]]
internal property.getFunction
: returns the current functiongetFunctionName
: returns the name of the current function, typically itsname
property. If aname
property is not available an attempt is made to infer a name from the function’s context.getMethodName
: returns the name of the property ofthis
or one of its prototypes that holds the current functiongetFileName
: if this function was defined in a script returns the name of the scriptgetLineNumber
: if this function was defined in a script returns the current line numbergetColumnNumber
: if this function was defined in a script returns the current column numbergetEvalOrigin
: if this function was created using a call toeval
returns a string representing the location whereeval
was calledisToplevel
: is this a top-level invocation, that is, is this the global object?isEval
: does this call take place in code defined by a call toeval
?isNative
: is this call in native V8 code?isConstructor
: is this a constructor call?isAsync
: is this an async call (i.e.await
orPromise.all()
)?isPromiseAll
: is this an async call toPromise.all()
?getPromiseIndex
: returns the index of the promise element that was followed inPromise.all()
for async stack traces, ornull
if theCallSite
is not aPromise.all()
call.
This answer is a cross-post of an answer I just gave to a similar question as more people might find this page.