15

I have a simple javascript error logging mechanism in place and it looks somewhhat like this:

window.onerror = function (ErrorMsg, Url, LineNumber, Col, Error) {
// ajax these to the server, including Error.stack}

The problem is that I'd also like to get the value of the local variables and function parameters when the error occurred. Is this even possible?

I'm thinking about modifying the Function prototype so that each time a function runs, its arguments are stored in a global array of strings and then the error handler would just add this array to the ajax call. Can JavaScript do this?

5
  • Interesting question. I don't think it's possible. Commented Dec 4, 2015 at 17:36
  • Yes, Interesting. Debuggers have instruments to stop a program at exceptions and can show all these stuff. There could be some API to do things which debugger does.
    – Dzenly
    Commented Dec 4, 2015 at 17:46
  • I think you'll find your answer in this link.
    – Pedram
    Commented Dec 17, 2015 at 12:47
  • What is the programming language used on the server side?
    – Pedram
    Commented Dec 17, 2015 at 12:52
  • So you can use the link that I mentioned above.
    – Pedram
    Commented Dec 19, 2015 at 7:24

5 Answers 5

11
+100

#1 Can local scope be recovered in onerror() without black magic?

Without this being bound in the scope of window.onerror() or the surrounding variables being directly accessible, it's impossible to regain access to the variables you had set.

What you're mostly wanting access to is this.arguments or arguments or the equivalent, but that's destroyed. Any hope of obtaining a key-value associative array or hash-like object would involve meta-programming ( i.e. reading the function definition to obtain the variable names, and obtaining an exception report to attempt to salvage data ).

See this answer for more on something similar:

But this "lacking functionality" is a good thing:

If you could gain access to what you're asking for, that would likely be a fault in the Javascript engine. Why? Because the variable states and contents themselves are what caused the exception/error, assuming bad code wasn't the issue to begin with.

In other words, if you could get access to a faulty variable, that might be a door into an infinite loop:

  1. Failure due to variable contents.
  2. Error handler triggered.
  3. Trace contents of variable.
  4. Failure due to variable contents.
  5. Error handler triggered.
  6. Trace contents of variable.
  7. Etc.

#2 Can Javascript store all arguments of every function call by voodoo?

Yes. It can. This is probably a really bad idea ( see #1 ) but it is possible. Here is a pointer on where to start:

From there, what you're wanting to do is push this.arguments or equivalent to a stack of function calls. But again, this is approaching insanity for many reasons. Not the least of which is the need to duplicate all the values, lest you reference mutated variables, or be unable to access the data at all... and like I said above, the problem of bad data in general. But still, it is possible.

1
  • arguments is not connected to this, and this is not something OP asked about. the main issue with the error event is that it's async, so your normal quirky back-drilling using arguments.callee.caller.caller-style chaining won't work like it does with try/catch error handling.
    – dandavis
    Commented Dec 18, 2015 at 4:15
5

Is this even possible?

No. A stack trace is proof that the stack has unwound, all stack frames and all the local variables they contained are gone. As for getting the name of a variable, that is not even possible at run time.

2

To start off i accept @Tomalak completely.

I was also put in your situation where i needed to debug a remote running app in case of crash.

As a work around I have forked my code for you in a fiddler. Please modify according to your need.

Caveat: You have to wrap the function body with try{..}catch(e){..} as illustrated in the fiddler code.

Please read the inline comments for understanding.

window.onerror = function (errorMsg, url, lineNumber, column, errorObj) {       
        console.log(errorObj);
}

window.addEventListener("reportOnError", function(e){   
    console.log(e.detail);    
  /*Send to the server or any listeners for analysis.*/  
  //Http.send(e.detail);  
});

function ExceptionReport(ex, args, scope) {
    var self = {};
   self.message = ex.message;
   self.stack = ex.stack;
   self.name = ex.name;
   self.whoCalled = args.callee.caller.name == "" ? "Window": args.callee.caller.name;
   self.errorInFunction = args.callee.name;
   self.instanceOf = scope.constructor;
   self.KeyPairValues = getParamNames(arguments.callee.caller.toString(), Array.prototype.slice.call(args)); //Contains the parameters value set during runtime
   window.dispatchEvent(new CustomEvent('reportOnError', {'detail':self}));
}

//Utilties 
function getParamNames(fnBody, values) {
  var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,
        ARGUMENT_NAMES = /([^\s,]+)/g,
        result = fnBody.slice(fnBody.indexOf('(')+1, fnBody.indexOf(')')).match(ARGUMENT_NAMES),
        obj={};

  fnBody.replace(STRIP_COMMENTS, '');  
  if(result !== null){      
        for(var i=0; i < result.length; i++){
        obj[result[i]] = values.length !==0 ? values[i] : null;
      }    
  }else{
    obj = null;
  }  
  return obj;
}

/*
    This is a testing/sample function that throws the error
*/
function testing(a,b,c){
    try{  
        dummy(1,2) ; //This line throws the error as reference error.
  }catch(e){    
        ExceptionReport(e, arguments, this);
  }
}

//Class Emulation: For instanceof illustration.
function testingClass(){
    this.testing = testing;
}

//Named self executing function: This calls the function
var myvar = (function myvar(){
    testing(1,2,3);
})();

//Illustrating instanceof in exception 
var myVar2 = new testingClass();
myVar2.testing(1,2,3);

//Calling from global scope this is Window
testing(1,2,3);

//Without variables
testing();

I have used examples to illustrate the behavior of functions called in different circumstances.

Below signifies the varialble used for

  • self.KeyPairValues : Used to store the function parameter set/passed during runtime
  • self.errorInFunction : This stores the name of the function error was caused in.
  • self.whoCalled : This stores the function name that invoked the defective function
  • self.instanceOf : This stores the name of the instance is called creating a new instance.

Other variables are same as in Error object

2

The others answers here are spot on, but I might be able to offer a suggestion for a slightly different way to accomplish this. Instead of trying to track all scope in your program, why not add a tagging function that tracks the scope of one function's parameters without affecting the runtime of the function. For for example:

var globalRecord = {};

function record(name, fn) {
    return function () {
        var args = [].slice.call(arguments);
        var record = globalRecord[name] = {
            args: args,
            arg: {}
        };  
        args.unshift(function (name, value) {
            return record[name] = value;
        });
        fn.apply(args, arguments);
    }
}

// Then, you track variables like this
var func = record("func", function (record, a, b, c) {
    record("a", a); // named parameters are accessible now
    record("b", b); // if some error occurs in the function body

    return a + b + c;
});

// Calling func still behaves as before.
func(1, 2, 3);

// Errors handled like this:
window.onerror = function () {
    globalRecord.func.args; // ==> last set of arguments past to function
    globalRecord.func.arg.a; // specific arguments recorded with names
};

You could even use this method to track scope without using a function by anonymously calling the recorded function.

record("test", function (record) {
    var a = record("a", /* whatever */);
    var b = record("b", /* ... */ );

    // do scope specific stuff that might fail
})();

Of course, this isn't a polished implementation by any stretch, but with a little work, I think you might be able to get the behavior you're looking for without any seriously black magic. By selectively adding and removing record calls as the need presents itself, you can have precise control over what is logged without any intrusive hacks.

1

You can find your answer in this link.

Before taking bundles from the server, you must modify them. For example, to solve your problem, you can do changes in the mentioned link as follows. In the BuildBundleContent Class make this change:

contents.Insert(blockContentIndex,
    string.Format("if(customErrorLogging)customErrorLogging({0}, this){1}",
    errVariable, hasContent ? ";" : ""));

If in the modules you have to use something like:

var self = this;

You can use:

contents.Insert(blockContentIndex,
    string.Format("if(customErrorLogging)customErrorLogging({0}, self ? self : this){1}",
    errVariable, hasContent ? ";" : ""));

And in added js file:

"use strict";
var customErrorLogging = function (ex, module) {
    console.log(module);
    //do something...
};

I hope help you.

Not the answer you're looking for? Browse other questions tagged or ask your own question.