81

How can one get the name and line of a function that called the current one? I would like to have a rudimentary debugging function like this (with npmlog defining log.debug):

function debug() {
  var callee, line;
  /* MAGIC */
  log.debug(callee + ":" + line, arguments)
}

When called from another function it would be something like this:

function hello() {
   debug("world!")
}
// outputs something like:
// "hello:2 'world!'"

For clarity, what I want is essentially analogous to this in Python:

import inspect
def caller():
    return inspect.stack()[2][3]
// line no from getframeinfo().lineno

Is there a Node equivalent to accomplish this?

Community
  • 1
  • 1
Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343
  • 1
    This might be useful, I asked a similar question but not node related: http://stackoverflow.com/questions/6885659/determining-source-line-and-file-of-function-reference-how-does-firebug-do-it – anthony sottile Jan 05 '13 at 14:22

6 Answers6

96

Using info from here: Accessing line number in V8 JavaScript (Chrome & Node.js)

you can add some prototypes to provide access to this info from V8:

Object.defineProperty(global, '__stack', {
get: function() {
        var orig = Error.prepareStackTrace;
        Error.prepareStackTrace = function(_, stack) {
            return stack;
        };
        var err = new Error;
        Error.captureStackTrace(err, arguments.callee);
        var stack = err.stack;
        Error.prepareStackTrace = orig;
        return stack;
    }
});

Object.defineProperty(global, '__line', {
get: function() {
        return __stack[1].getLineNumber();
    }
});

Object.defineProperty(global, '__function', {
get: function() {
        return __stack[1].getFunctionName();
    }
});

function foo() {
    console.log(__line);
    console.log(__function);
}

foo()

Returns '28' and 'foo', respectively.

Community
  • 1
  • 1
Joe
  • 41,484
  • 20
  • 104
  • 125
  • 1
    Thanks Joe. Perhaps `__line` and `__function` might be better named `__parent_line` and `__parent_function_name`, but otherwise this is great (and it is, incidentally, how `stack-trace` works in my answer). – Brian M. Hunt Jan 05 '13 at 14:57
  • They use the 2nd frame otherwise you'd just get info about __line and __function. :) Interesting on `stack-trace` will have to check out. – Joe Jan 05 '13 at 16:40
  • 3
    While this solution is great, it is expensive. I would not use it in production code. If you use bunyan logger, you can turn on [src option](https://github.com/trentm/node-bunyan#src). But again, they also dont recommend that option for production code. I would love a transpiler that can replace each logger call with the information about function name and line number before I run my code. Anyone?? – Vibgy Jan 06 '17 at 01:42
  • Vibgy, if you are using TypeScript you can write your own custom transformer to do exactly that. – Ghepes Doru May 14 '19 at 12:10
  • 4
    Just a note that if you are using strict mode, this method won't work due to `aguments.callee` – myol Dec 18 '20 at 07:29
  • In my case, this is returning the name of the current function and not the parent one that called id. – radar May 05 '22 at 09:21
  • Then you might need stack[2] but curious what your stack looks like – Joe May 05 '22 at 13:57
  • Things may have changed in (does math) 9 years – Joe May 05 '22 at 13:57
35

The following code uses only core elements. It parses the stack from an error instance.

"use strict";
function debugLine(message) {
    let e = new Error();
    let frame = e.stack.split("\n")[2]; // change to 3 for grandparent func
    let lineNumber = frame.split(":").reverse()[1];
    let functionName = frame.split(" ")[5];
    return functionName + ":" + lineNumber + " " + message;
}
function myCallingFunction() {
    console.log(debugLine("error_message"));
}
myCallingFunction();

It outputs something like myCallingFunction:10 error_message

I've extracted the elements of the error as variables (lineNumber, functionName) so you can format the return value any way you want.

As a side note: the use strict; statement is optional and can be used only if your entire code is using the strict standard. If your code is not compatible with that (although it should be), then feel free to remove it.

Cosmin Staicu
  • 1,809
  • 2
  • 20
  • 27
  • Great, it gives location of error when rethrowing and doesn't need additional dependencies. https://gist.github.com/mpapec/27f55da1cb2bb92eb36ea0c12d421274 – mpapec May 16 '19 at 12:25
  • Agreed. This is a nice little solution. – Reece Daniels Jun 09 '19 at 14:01
  • 1
    I just noticed it is very difficult to find the function from which it is called. Say you are creating a logger module, then the pattern of split changes from 'at' to '\n' and the sequence of the line also changes. Now further imagine having try-catch blocks. It becomes much more difficult. – Tarun Apr 19 '20 at 23:02
  • 1
    Line number will give a different result on Windows because of `C:/...` path. Better might be to work backwards from the semi colon split results. Very clever solution though. Thank you! – isick Feb 26 '21 at 19:55
  • I've modified the code, making it compatible with windows also. The search for the line number is now starting from the end of the error line (the array is reversed prior to getting the line value). – Cosmin Staicu May 13 '21 at 05:30
  • This is super nice. – Alan Spurlock Sep 09 '21 at 19:37
19

I also had similar requirement. I used stack property of Error class provided by nodejs.
I am still learning node so, there may be the chances of error.

Below is the explanation for the same. Also created npm module for the same, if you like, you can check at:
1. npm module 'logat'
2. git repo

suppose we 'logger' object with method 'log'

var logger = {
 log: log
}
function log(msg){
  let logLineDetails = ((new Error().stack).split("at ")[3]).trim();
  console.log('DEBUG', new Date().toUTCString(), logLineDetails, msg);
}

Example:

//suppose file name: /home/vikash/example/age.js
function getAge(age) {
    logger.log('Inside getAge function');    //suppose line no: 9
}

Output of above Example:

    DEBUG on Sat, 24 Sept 2016 12:12:10 GMT at getAge(/home/vikash/example/age.js:9:12)
    Inside getAge function
Vikash
  • 473
  • 7
  • 15
16

Here is a one liner for quick debugging purposes:

console.log("DEBUG", (new Error().stack.split("at ")[1]).trim());

This will log something like this with Node.js:

DEBUG SomeObject.function (/path/to/the/code.js:152:37) 

--

You can also add custom args at the end, e.g.

console.log("DEBUG", (new Error().stack.split("at ")[1]).trim(), ">>>", myVar);

Note that if you put this into a helper function, adjust the stack index from e.g. [1] to [2].

Alexander Klimetschek
  • 3,585
  • 31
  • 36
14

I found and installed the node-stack-trace module (installed with npm install stack-trace), and then defined echo as:

function echo() {
  var args, file, frame, line, method;
  args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];

  frame = stackTrace.get()[1];
  file = path.basename(frame.getFileName());
  line = frame.getLineNumber();
  method = frame.getFunctionName();

  args.unshift("" + file + ":" + line + " in " + method + "()");
  return log.info.apply(log, args); // changed 'debug' to canonical npmlog 'info'
};
Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343
  • This is very nice, for other people change the above lines to this: args = 1 <= arguments.length ? [].slice.call(arguments, 0) : []; return console.log.apply(this, args); – Kus Mar 22 '14 at 07:16
  • Thanks, I'm running Titanium and got this function to log origin of message: ```function log(msg) { stackTrace = require("stack-trace/lib/stack-trace"); var thistrace = stackTrace.get(); var parent_name = thistrace[1].getFunctionName(); var parent_eval = thistrace[1].getEvalOrigin(); msg = sprintf("[%s][%s]: %s", parent_eval, parent_name, msg); Ti.API.info(msg); }``` – Fernando Fabreti Mar 26 '14 at 14:31
1

Here is a way to get the filename when an error occurs. you have to wrap the function in the onErrorReturnFileName. Here I am wrapping func() from otherNode file.

const {func} = require('./otherNode')

function onErrorReturnFileName(funcToRead) {
    let defaultPrepareStackTrace = Error.prepareStackTrace
    try {
        let getStack = function (err, stack) { return stack; };
        Error.prepareStackTrace = getStack
        return {result: funcToRead(), name: null}
    }catch (ex) {
        return {name: ex.stack.shift().getFileName(), result: null}
    }
    Error.preppareStackTrace = defaultPrepareStackTrace
}
console.log(onErrorReturnFileName(func))
Jackstine
  • 486
  • 4
  • 12