How to get JavaScript caller function line number and caller source URL
Asked Answered
M

17

134

I am using the following for getting the JavaScript caller function name:

var callerFunc = arguments.callee.caller.toString();
callerFuncName = (callerFunc.substring(callerFunc.indexOf("function") + 8, callerFunc.indexOf("(")) || "anoynmous")

Is there a way to discover the line number from which the method was called?

Also, is there a way to get the name of the JavaScript file the method was called from? Or the source URL?

Markusmarl answered 27/8, 2009 at 12:49 Comment(2)
I don't think this is possible in IE, or else we would have a way to get around there CRAPPY error messages that deliver no detail. But if it is possible I would LOVE to know as well!Portal
Yes. Here's a cross-browser function that makes use of each browsers' proprietary methods: github.com/eriwen/javascript-stacktrace [fixed link]Tallie
I
117

This works for me in chrome/QtWebView

function getErrorObject(){
    try { throw Error('') } catch(err) { return err; }
}

var err = getErrorObject();
var caller_line = err.stack.split("\n")[4];
var index = caller_line.indexOf("at ");
var clean = caller_line.slice(index+2, caller_line.length);
Impanel answered 27/9, 2010 at 18:14 Comment(8)
Works in node too. Pop this inside your custom log() function (which adds whatever other useful workarounds you need - eg fixed for Chrome array logging) and still line numbers from wherever you called log().Tuttifrutti
Don't need to throw the error; simply creating it is enough: var caller_line = (new Error).stack.split("\n")[4]Zoltai
Merged this suggestion with another similar answer to get an FF/Webkit "standardized" response -- see https://mcmap.net/q/134641/-a-proper-wrapper-for-console-log-with-correct-line-numberBlancablanch
Works in PhantomJS too, but you have to throw it, or else the "stack" attribute is not set on the error.Whack
@Zoltai - your solution is very elegant but doesn't work in IE. IMO this should be the accepted answer.Woodrowwoodruff
@Woodrowwoodruff which IEs? I'm … pretty sure I have production code depending on new Error().stack, somewhere, and I've never had problems that I can recall …Zoltai
@Zoltai - I just tested on IE11 and (new Error).stack returns undefined. Also MDN says that Error.prototype.stack is not standard and not on track for standards developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… (apparently everything but IE11 has support for it though)Woodrowwoodruff
@Zoltai actualy in some browsers like iOS safari you do need to throw the exception! So why not do it?Allium
S
29

If you need to access the line number of something in Firefox or Opera, just access

(new Error).lineNumber

Note: this does not work on Chrome/Safari.

Samadhi answered 28/8, 2009 at 17:16 Comment(3)
Hi, Thanks for that addon. do you know if its possible to get line number from previous call ? Lets say method A calls B , and now in B I would like to know in what line under A the call was made?Markusmarl
This is ticked, but doesn't answer the question, which is how to get the line number of the caller function.Tuttifrutti
Also, this is extremely limited. The best solution is to throw an error and use regex on the error.stack which is available in all modern browsers. You can easily extract that path, file, line, and column. No problem.Allium
A
20

I was surprised that most of these answers assumed that you wanted to handle an error rather than just output helpful debug traces for normal cases as well.

For example, I like using a console.log wrapper like this:

consoleLog = function(msg) {//See https://mcmap.net/q/138182/-how-can-i-determine-the-current-line-number-in-javascript
    var e = new Error();
    if (!e.stack) {
        try {
            // IE requires the Error to actually be thrown or else the 
            // Error's 'stack' property is undefined.
            throw e;
        } catch (e) {
            if (!e.stack) {
                //return 0; // IE < 10, likely
            }
        }
    }
    var stack = e.stack.toString().split(/\r\n|\n/);
    if (msg === '') {
        msg = '""';
    }
    console.log(msg, '          [' + stack[1] + ']');        
}

This ends up printing an output such as the following to my console:

1462567104174 [getAllPosts@http://me.com/helper.js:362:9]

See https://stackoverflow.com/a/27074218/ and also A proper wrapper for console.log with correct line number?

Axiomatic answered 6/5, 2016 at 20:45 Comment(8)
works for firefox browser, but does not work for node.js.Constrained
under node you have to log stack[2]Maritsa
Has anyone tested this code to see if it works? What does the if condition if (!e.stack) scope for? This looks like it has a python programmer's fingerprints all over it ;-)Giles
@Giles I've never liked Python, but as I mentioned in the code comment above, I got it from https://mcmap.net/q/138182/-how-can-i-determine-the-current-line-number-in-javascript I can't say whether that person likes Python. But yeah this code worked for me in 2016. I haven't used it in years though. https://mcmap.net/q/136832/-how-to-get-javascript-caller-function-line-number-and-caller-source-url looks interesting.Axiomatic
@Axiomatic I think the parsing is as follows: if (!e.stack)try{...} and the catch{...} is just dangling. Any pro js guys want to parse the above code? I am a C guy so to me this should throw an error with that dangling catch statementGiles
@Giles Good catch. I must not have run the code the way that I pasted it here, because I personally never try to use assumed/omitted braces. I edited to add braces above but haven't tested. You could try that.Axiomatic
@Ryan, the freedom to omit the curly braces after an if statement is IMO the only great sin C ever committedGiles
this not work, in modern browser updates there is no way to manage or read auto generated console logs because the browser use another built-in functions to write to console and if u override console functions it just affect on ur calls for console not browser callsSheldonshelduck
T
13

I realize this is an old question but there is now a method called

console.trace("Message");

that will show you the line number and the chain of method calls that led to the log along with the message you pass it. More info on javascript logging tricks are available here at freecodecamp and this medium blog post

Timekeeper answered 28/3, 2019 at 7:53 Comment(1)
the problem is that console.trace() doesn't return anything.Concomitant
P
5

This is often achieved by throwing an error from the current context; then analyzing error object for properties like lineNumber and fileName (which some browsers have)

function getErrorObject(){
  try { throw Error('') } catch(err) { return err; }
}

var err = getErrorObject();

err.fileName;
err.lineNumber; // or `err.line` in WebKit

Don't forget that callee.caller property is deprecated (and was never really in ECMA 3rd ed. in the first place).

Also remember that function decompilation is specified to be implementation dependent and so might yield quite unexpected results. I wrote about it here and here.

Pulpiteer answered 27/8, 2009 at 13:12 Comment(9)
Thanks, it seems a bit problematic to add this code in the application I need. (some js tracing framework) Do you know any other method which is not deprecated that I can use?Markusmarl
You should be able to inspect error object without relying on deprecated callee.caller.Pulpiteer
You don't need to throw the Error. You just use (new Error).lineNumber to access the current line number in a script.Samadhi
@Elijah That's what I see in FF3. WebKit, on the other hand, populates line only when error is thrown.Pulpiteer
What is the substitue for callee.caller ? If I need to get the function name?Markusmarl
You can use callee.caller, as long as you account for it being non-existent in some clients (due to its non-standard nature). Using function decompilation for debugging purposes is OK, but building actual application on top of it (and without fallback for clients that lack it) is not.Pulpiteer
Is it supported for IE/Firefox/Safari ? do you know any other standard method for fetching the caller function name?Markusmarl
Do you really need to throw the error? Can't you just create it?Snout
in node.js the proposal just returns undefined.Constrained
W
4

It seems I'm kind of late :), but the discussion is pretty interesting so.. here it goes... Assuming you want to build a error handler, and you're using your own exception handler class like:

function  errorHandler(error){
    this.errorMessage = error;
}
errorHandler.prototype. displayErrors = function(){
    throw new Error(this.errorMessage);
}

And you're wrapping your code like this:

try{
if(condition){
    //whatever...
}else{
    throw new errorHandler('Some Error Message');
}
}catch(e){
    e.displayErrors();
}

Most probably you'll have the error handler in a separate .js file.

You'll notice that in firefox or chrome's error console the code line number(and file name) showed is the line(file) that throws the 'Error' exception and not the 'errorHandler' exception wich you really want in order to make debugging easy. Throwing your own exceptions is great but on large projects locating them can be quite an issue, especially if they have similar messages. So, what you can do is to pass a reference to an actual empty Error object to your error handler, and that reference will hold all the information you want( for example in firefox you can get the file name, and line number etc.. ; in chrome you get something similar if you read the 'stack' property of the Error instance). Long story short , you can do something like this:

function  errorHandler(error, errorInstance){
    this.errorMessage = error;
    this. errorInstance = errorInstance;
}
errorHandler.prototype. displayErrors = function(){
    //add the empty error trace to your message
    this.errorMessage += '  stack trace: '+ this. errorInstance.stack;
    throw new Error(this.errorMessage);
}

try{
if(condition){
    //whatever...
}else{
    throw new errorHandler('Some Error Message', new Error());
}
}catch(e){
    e.displayErrors();
}

Now you can get the actual file and line number that throwed you custom exception.

Worst answered 5/3, 2011 at 1:59 Comment(0)
G
4

Line number is actually something static, so if you just want it for logging then it could be preprocessed with something like gulp. I've written a small gulp plugin that does exactly that:

var gulp = require('gulp');
var logLine = require('gulp-log-line');
gulp.task('log-line', function() {
    return gulp.src("file.js", {buffer : true})
    //Write here the loggers you use.
        .pipe(logLine(['console.log']))
        .pipe(gulp.dest('./build'))

})

gulp.task('default', ['log-line'])

This will attach the file name and line to all logs from console.log, so console.log(something) will become console.log('filePath:fileNumber', something). The advantage is that now you can concat your files, transpile them... and you will still get the line

Gereld answered 30/8, 2016 at 17:43 Comment(1)
This seems like a great suggestion for situations where a transpiler is used (e.g., when using TypeScript). Thank you!Papyrus
B
4

This is how I have done it, I have tested it in both Firefox and Chrome. This makes it possible to check the filename and line number of the place where the function is called from.

logFileAndLineNumber(new Error());

function logFileAndLineNumber(newErr)
{
   if(navigator.userAgent.indexOf("Firefox") != -1)
   {
      var originPath = newErr.stack.split('\n')[0].split("/");
      var fileNameAndLineNumber = originPath[originPath.length - 1].split(">")[0];
      console.log(fileNameAndLineNumber);
   }else if(navigator.userAgent.indexOf("Chrome") != -1)
   {
      var originFile = newErr.stack.split('\n')[1].split('/');
      var fileName = originFile[originFile.length - 1].split(':')[0];
      var lineNumber = originFile[originFile.length - 1].split(':')[1];
      console.log(fileName+" line "+lineNumber);
    }
}
Band answered 2/3, 2017 at 4:7 Comment(0)
M
3

If you would like to know the line number for debugging purposes, or only during development (For a reason or another), you could use Firebug (a Firefox extension) and throw an exception.

Edit:

If you really need to do that in production for some reason, you can pre-process your javascript files in order for each function to keep track of the line it is on. I know some frameworks that find the coverage of code use this (such as JSCoverage).

For example, let's say your original call is:

function x() {
  1 + 1;
  2 + 2;
  y();
}

You could write a preprocessor to make it into:

function x() {
  var me = arguments.callee;
  me.line = 1;
  1 + 1;
  me.line = 2;
  2 + 2;
  me.line = 3;
  y();
}

Then in y(), you could use arguments.callee.caller.line to know the line from which it was called, such as:

function y() {
  alert(arguments.callee.caller.line);
}
Massarelli answered 27/8, 2009 at 13:11 Comment(1)
Thanks, I would like to this in production for support reasons. I have found some code that enables you to see the call stack of all flow until the method, but it doesnt have the line numbers where methods were called. I guess to easy solution for that?Markusmarl
C
3

If you need something that is very complete and accurate, for v8 (meaning NodeJS and Chrome) there is the stack-trace package, which worked for me:

https://www.npmjs.com/package/stack-trace

This has several advantages: being able to produce deep stack-traces, being able to traverse async calls, and providing the full URL of scripts at every stack-frame.

You can find more information about the v8 stack-track API here.

If you need something that works in more browsers, there is this package:

https://www.npmjs.com/package/stacktrace-js

This library parses the formatted stack-trace strings you normally see in the devtools console, which means it only has access to information that actually appears in a formatted stack-track - unlike the v8 API, you can't get (among other things) the full URL of the script in a stack-frame.

Note that parsing a formatted stack-trace is a somewhat fragile approach, as this formatting could change in the browsers, which would break your app.

As far as I know, there is no safer bet for something cross-browser - if you need something fast, accurate and complete that works in all browsers, you're pretty much out of luck.

Clio answered 23/9, 2021 at 14:25 Comment(0)
D
2

Here is what I wrote based on info found on this forum:

This is part of a MyDebugNamespace, Debug is apparently reserved and won't do as namespace name.

    var DEBUG = true;

...

    if (true == DEBUG && !test)
    {
        var sAlert = "Assertion failed! ";
        if (null != message)
            sAlert += "\n" + message;
        if (null != err)
            sAlert += "\n" + "File: " + err.fileName + "\n" + "Line: " + err.lineNumber;
        alert(sAlert);
    }

...

How to call:

    MyDebugNamespace.Assert(new Error(""), (null != someVar), "Something is wrong!")

I included two functions with variable number of arguments calling this base code in my namespace so as to optionally omit message or error in calls.

This works fine with Firefox, IE6 and Chrome report the fileName and lineNumber as undefined.

Dismantle answered 4/6, 2011 at 0:40 Comment(0)
G
2

the following code works for me in Mozilla and Chrome.

Its log function that shows the file's name and the line of the caller.

log: function (arg) {
    var toPrint = [];
    for (var i = 0; i < arguments.length; ++i) {
        toPrint.push(arguments[i]);
    }

    function getErrorObject(){
        try { throw Error('') } catch(err) { return err; }
    }

    var err = getErrorObject(),
        caller;

    if ($.browser.mozilla) {
        caller = err.stack.split("\n")[2];
    } else {
        caller = err.stack.split("\n")[4];
    }

    var index = caller.indexOf('.js');

    var str = caller.substr(0, index + 3);
    index = str.lastIndexOf('/');
    str = str.substr(index + 1, str.length);

    var info = "\t\tFile: " + str;

    if ($.browser.mozilla) {
        str = caller;
    } else {
        index = caller.lastIndexOf(':');
        str = caller.substr(0, index);
    }
    index = str.lastIndexOf(':');
    str = str.substr(index + 1, str.length);
    info += " Line: " + str;
    toPrint.push(info);

    console.log.apply(console, toPrint);
}
Gerlac answered 5/11, 2013 at 13:47 Comment(2)
Looks like something is missing. I get: SyntaxError: function statement requires a name,log: function (arg) {Smithery
I like this idea, but the line numbers come out incorrect for me.Axiomatic
A
2

My contribution to custom errors in JavaScript:

  1. First, I agree with this @B T guy at Inheriting from the Error object - where is the message property?, we have to built it properly (actually you have to use a js object library, my favorite: https://github.com/jiem/my-class):

    window.g3 = window.g3 || {};
    g3.Error = function (message, name, original) {
         this.original = original;
         this.name = name || 'Error.g3';
         this.message = message || 'A g3.Error was thrown!';
         (original)? this.stack = this.original.stack: this.stack = null;
         this.message += '<br>---STACK---<br>' + this.stack;
     };
    
     var ClassEmpty = function() {};
     ClassEmpty.prototype = Error.prototype;
     g3.Error.prototype = new ClassEmpty();
     g3.Error.prototype.constructor = g3.Error;
    
  2. then, we should define a global error handling function (optional) or, they'll end up to the engine:

    window.onerror = printError; 
    function printError(msg, url, line){
        document.getElementById('test').innerHTML = msg+'<br>at: '+url+'<br>line: '+line;
        return true;
    }
    
  3. finally, we should throw our custom errors carefully:

    //hit it!
    //throw new g3.Error('Hey, this is an error message!', 'Error.Factory.g3');
    throw new g3.Error('Hey, this is an error message!', 'Error.Factory.g3', new Error());
    

Only, when passing the third parameter as new Error() we are able to see the stack with function and line numbers!

At 2, the function can also handle error thrown by the engine as well.

Of course, the real question is if we really need it and when; there are cases (99% in my opinion) where a graceful return of false is enough and leave only some critical points to be shown with the thrown of an error.

Example: http://jsfiddle.net/centurianii/m2sQ3/1/

Allomerism answered 22/3, 2014 at 12:58 Comment(0)
S
2
console.log(new Error);

It will show you the whole track.

Saurischian answered 27/2, 2018 at 14:6 Comment(0)
D
1

To determine which line something is on you have to search all the code for the code that occupies the particular line of interest and count the "\n" characters from the top to this of interest and add 1.

I am actually doing this very thing in an application I am writing. It is a best practices validator for HTML and is still heavily under development, but the error output process that you would be interested in is complete.

http://mailmarkup.org/htmlint/htmlint.html

Damian answered 27/8, 2009 at 13:1 Comment(2)
the particular line of interest can be many... If I have same method called several times from another method, how can I know (in that other method) where did the call arrive from?Markusmarl
You are not going to be able to analyze JavaScript interpretation at execution time from outside the interpreter. You could write a program to trace the execution path in your program, but it would be that program that is executing and not the code that you wish to analyze. This is typically a complicated task that is performed manually with the help of tools. If you really want to see what is happening in your code as it executes then have it write meta-data to the screen that tells you want decisions are executing which other parts.Damian
S
1

It was easier in the past, but at this time with browser updates:

This is the safe Multi Browser Solution

<!DOCTYPE html>
<html lang="en">
<head>
    <script>
        var lastErr;
        function errHand(e) {
            lastErr = e;
            switch (e.target.nodeName) {
                case 'SCRIPT':
                    alert('script not found: ' + e.srcElement.src);
                    break;
                case 'LINK':
                    alert('css not found: ' + e.srcElement.href);
            }
            return false;
        }
        window.onerror = function (msg, url, lineNo, columnNo, error) {
            alert(msg + ' - ' + url + ' - ' + lineNo + ' - ' + columnNo);
            return false;
        }
    </script>
    <script src="http://22.com/k.js" onerror="errHand(event)"></script>
    <link rel="stylesheet" href="http://22.com/k.css" onerror="errHand(event)" type="text/css" />
</head>
<body>
    <script>
        not_exist_function();
    </script>
</body>
</html>
  1. Dont try to override or read console auto generated error logs by browser, Not work at this time but in the past it was happened
  2. For control loading each external linked css/js/etc use onerror event property innertag
  3. For control inline/loaded scripts use window.onerror
  4. Place error handle functions in the top of your html code
  5. To develop this code for other special tags like "include" set onerror inline event like step 2 and after fire error use console.log(lastErr) to see error object fields
  6. If you have an error in source of attached js file, then window.onerror fetch the complete log just when the js have same origin host with the main html file, if you test this situation on local with no host then the logs not be complete because of the local-no-host environment not known as same origin
  7. For send Request error handling like Ajax/Websocket its better use their built-in error handle functions

For NodeJS - Global Error Handling Solution

process.on('uncaughtException', function (err) {
    console.error('uncaughtException:\n' + err.stack + '\n');
})

This is very useful, Because it will show up you errors that exist but dont break your main program process.

Sheldonshelduck answered 5/5, 2021 at 2:15 Comment(0)
C
-1

Answers are simple. No and No (No).

By the time javascript is running the concept of source files/urls from which the come is gone.

There is also no way to determine a line number because again by the time of execution the notion of code "lines" is no longer meaningful in Javascript.

Specific implementations may provide API hooks to allow priviledged code access to such details for the purpose of debugging but these APIs are not exposed to ordinary standard Javascript code.

Constantia answered 27/8, 2009 at 13:0 Comment(6)
In firefox the exceptions include such information... would it be possible in firefox at least?Portal
When you launch MS Script debugger and put some breakpoint, you see in call stack where exactly you came from. This is because of specialized hooks?Markusmarl
"again by the time of execution the notion of code "lines" is no longer meaningful in Javascript." Huh? JS already shows the line number everytime you run console.log()Tuttifrutti
@Constantia Yes. It neatly contradicts the somewhat absolute 'No and No (No).'Tuttifrutti
@nailer: Back in 2009 across all the major browsers, in what way does my answer contradict. Bear in mind the question at hand is about discovery, in running javascript, of the callee's line number?Constantia
@Constantia I don't think a conversation about why 'no no no... but sometimes' isn't a good answer will contribute anything here. Maybe post something to meta if you really want to discuss it.Tuttifrutti

© 2022 - 2024 — McMap. All rights reserved.