Winston: Log caller function name and line number

Created on 15 Jul 2016  路  6Comments  路  Source: winstonjs/winston

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.

duplicate

Most helpful comment

please provide a option to enable this feature, like log4js

All 6 comments

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pocesar picture pocesar  路  3Comments

Buzut picture Buzut  路  3Comments

kjin picture kjin  路  3Comments

bertolo1988 picture bertolo1988  路  3Comments

mohanen picture mohanen  路  4Comments