There does not seem to be a way to log the caller's method name and line number. I know that this has already been requested with #200, but that issue was locked with the reason that such a function would be _very_ slow. Given that a typical application would not excessively log thousands of log statements per second, it does not make a noticeably difference.
As you probably know, JavaScript provides the stack trace in the Error.stack property on construction of a new Error object. However, this is an unstructured string and needs to be parsed.
V8 on the other hand has a feature allowing to retrieve the stack trace in its original structured form (before stringifying). This is described here: https://github.com/v8/v8/wiki/Stack-Trace-API
My current implementation on top of Winston looks like this. (It may not be a good implementation, I'm very new to JavaScript and Node/V8. :) )
// ES2015
const path = require('path');
const winston = require('winston');
function CustomError() {
// Use V8's feature to get a structured stack trace
const oldStackTrace = Error.prepareStackTrace;
const oldLimit = Error.stackTraceLimit;
try {
Error.stackTraceLimit = 3; // <- we only need the top 3
Error.prepareStackTrace = (err, structuredStackTrace) => structuredStackTrace;
Error.captureStackTrace(this, CustomError);
this.stack; // <- invoke the getter for 'stack'
} finally {
Error.stackTraceLimit = oldLimit;
Error.prepareStackTrace = oldStackTrace;
}
}
function getAndFormatStackTraceElement() {
const stack = new CustomError().stack;
const CALLER_INDEX = 2; // <- position in stacktrace to find deepest caller
const element = stack[CALLER_INDEX];
const fileName = path.basename(element.getFileName());
return element.getFunctionName() + "(" + fileName + ":" + element.getLineNumber() + ")";
}
module.exports = {
info(msg, ...vars) {
winston.info(getAndFormatStackTraceElement() + ': ' + msg, ...vars);
}
}
Credits go to @addaleax from: https://github.com/nodejs/node/issues/7749#issuecomment-232972234
The way the string is formatted and the module exports are defined, sure needs reworking, I guess.
This ticket could serve as a discussion on how a good PR would look like for Winston, adding this feature to the library.
hi, I am trying to use your wrapper right now and get this error:
RangeError: Maximum call stack size exceeded
at CustomError.get stack [as stack] (native)
at new CustomError (<filename:lineNumber>)
at getAndFormatStackTraceElement (<filename:lineNumber>)
at logger.error (<filename:lineNumber>)
Any idea how I could resolve this?
Did it happen to you too? thanks
Try Easylogging++ binding for node. See original library docs to check the potential of this binding
A simple example
const easyloggingpp = require('easyloggingpp');
const ConfigType = easyloggingpp.ConfigType;
const Level = easyloggingpp.Level;
const logger = easyloggingpp.getLogger('mylogger'); // register logger
easyloggingpp.configureAllLoggers([
{
config: ConfigType.Format,
value: '%levshort %datetime %fbase:%line %msg', // <-- fbase or file for full path
},
{
config: ConfigType.Filename,
value: 'logs/output.log'
}
]);
logger.info('simple log');
logger.info('array %s', [1, 2, 3]);
var person = { 'name': 'Adam', 'age': 960, }
logger.info('obj %s', person);
logger.error('an error occurred %s', new Error("error msg"));
easyloggingpp.setLogErrorStack(false); // turn stack logging off
logger.fatal('serious error occurred %s', new Error("fatal msg"));
logger.error('an error occurred %s', new Error("error msg"));
Sample output
![sample-output]
hi, I am trying to use your wrapper right now and get this error:
RangeError: Maximum call stack size exceeded at CustomError.get stack [as stack] (native) at new CustomError (<filename:lineNumber>) at getAndFormatStackTraceElement (<filename:lineNumber>) at logger.error (<filename:lineNumber>)Any idea how I could resolve this?
Did it happen to you too? thanks
In the line
Error.captureStackTrace(this, CustomError)
CustomError is effectively calling itself, so infinite loop and max call stack exceeded.
I used @httpdigest solution to create a Static class for what I needed but you can use it as above with winston
usage
const StackElements = require('./stackElements');
consol.log(StackElements.getFormatedStackTraceElement(1)); // 0 for current level, 1 for parent ...
export
const path = require('path');
class V8StackElements {
static get stack() {
return this.customStack;
}
constructor(callerIndex) {
// Use V8's feature to get a structured stack trace
const oldStackTrace = Error.prepareStackTrace;
const oldLimit = Error.stackTraceLimit;
try {
Error.stackTraceLimit = callerIndex + 1; // <- we only want the top couple of elements
Error.prepareStackTrace = (err, structuredStackTrace) => structuredStackTrace;
Error.captureStackTrace(this);
this.customStack = this.stack; // <- invoke the getter for 'stack'
} finally {
Error.stackTraceLimit = oldLimit;
Error.prepareStackTrace = oldStackTrace;
}
}
}
class StackElements {
static getFormatedStackTraceElement(callerIndex) {
callerIndex = callerIndex + 2; //ignor v8 level and this level
const stack = new V8StackElements(callerIndex).stack;
const element = stack[callerIndex];
const fileName = path.basename(element.getFileName());
return element.getFunctionName() + "(" + fileName + ":" + element.getLineNumber() + ")";
}
}
module.exports = StackElements;
I split it to 2 classes to only expose the intended function on the static class
I ended up here as I got stuck with the
'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
when trying to use Function.caller
Thanks for the suggestion @httpdigest! But as you said this is a duplicate of #200, so going to close it. Will leave the issue unlocked to keep the discussion going this time.
please provide a option to enable this feature, like log4js
Most helpful comment
please provide a option to enable this feature, like log4js