Documentation Menu

Javascript Try Catch

The JavaScript Try Catch statement lets you handle exceptions that are thrown in your code:

try {
    ...
    code that may throw exceptions
    ...
} catch (e) {
    ...
    code that somehow handles the exception, as passed in e
    ...
}

What to do when an exception happens? You'll probably want to show an error message to the user. But you'll also want to log the exception and its stack trace in your server side log with the fatalException method. That way, you at least know about it:

try {
    ...
    code that may throw exceptions
    ...
} catch (e) {
    ...
    show message to user
    ...
    // Log the exception and its stack trace
    JL().fatalException("something went wrong!", e);
}

More information, but only when you need it

Often you need more information than provided in the exception to track down the cause of the JavaScript error. You might wind up logging debug information in various places in your JavaScript:

function f1(a, b) {
    JL("f1").debug("information that may be useful when an exception happens");
    ...
}
 
function f2(c, d) {
    JL("f2").debug("more information that may be useful when an exception happens");
    ...
}
 
function f3() {
    try {
        ...
    } catch(e) {
        JL().fatalException("exception message", e);
    }
}

The problem is that now you'll be receiving lots of debug messages, even if there is no error. You do want the debug messages, but only if there is a JavaScript error.

The solution is to have JSNLog store the debug messages in browser memory, and only send them when there is a fatal message.

To make that happen, you can configure your loggers like so (details):

<jsnlog>
    <!-- Create appender that stores debug messages in memory until fatal log message is sent -->
    <ajaxAppender 
        name="appender1" 
        storeInBufferLevel="DEBUG" 
        level="FATAL" 
        sendWithBufferLevel="FATAL" 
        bufferSize="20"/>

    <!-- Get the loggers to use the new appender -->
    <logger appenders="appender1"/>
</jsnlog>
// Use in Configure method in Startup class
        var jsnlogConfiguration = 
  // Create appender that stores debug messages in memory until fatal log message is sent
  new JsnlogConfiguration {
      ajaxAppenders=new List<AjaxAppender> {
          new AjaxAppender {
              name="appender1", 
              storeInBufferLevel="DEBUG", 
              level="FATAL", 
              sendWithBufferLevel="FATAL", 
              bufferSize=20
          }
      },
  
      // Get the loggers to use the new appender
      loggers=new List<Logger> {
          new Logger {
              appenders="appender1"
          }
      }
  };

This creates an AjaxAppender, the component that actually sends log messages to the server. Here it is configured to store the last 20 log messages with severity DEBUG or higher in browser memory. When there is a log message with severity FATAL, both the fatal message itself and the stored messages are sent to the server. If there is no fatal message, nothing is sent to the server.

The last line associates the root logger with the new appender. Because all loggers inherit from the root logger, they will now all send their messages through the new AJAX Appender.

Exception Object

Similar to the standard JavaScript Error object, JSNLog's Exception object allows you to throw a custom JavaScript exception when things go wrong:

function f(i, j) {
    if (i < 10) { 
        throw new JL.Exception("i is too small!");
    }
    ...
}

Add information to aid in debugging

However, instead of a string, you can also pass in a JSON object with more information, to help you fix the issue:

function f(i, j) {
    if (i < 10) { 
        throw new JL.Exception({
            "message": "i is too small!",
            "i": i,
            "j": j    
        });
    }
    ...
}

Add inner exceptions

The Exception object supports inner exceptions - essentially storing one exception within another. To see how this works, consider this code:

function f2(v) {
    var x, y;
    ... some code
    if (somethingWentWrong) {
        throw new JL.Exception({ "x": x, "y": y, "v": v });
    }
}
function f1(anArray) { var i; for(i = 0; i < anArray.length; i++) { f2(anArray[i]); } }
try { f1([1, 2, 3]); } catch(e) { JL().fatalException("Exception was thrown!", e); }

Function f2 can throw an exception. If it does, it puts all relevant information in the exception to make fixing the problem easier.

However, function f1 also has information that might be relevant, such as the value of the index i. It would be good to somehow add that information to the exception.

It would be possible to catch the exception in function f1 and then throw a new exception that contains all the information stored by function f2 plus the index i. But that would be complicated, and we'd lose the stack trace of the original exception.

The easiest solution is to catch the JavaScript exception in function f2, and then throw a new one that contains the original exception, plus any additional information. That way, we get to keep all the information stored in the original exception, including its stack trace. The changes are below in red:

function f2(v) {
    var x, y;
    ... some code
    if (somethingWentWrong) {
        throw new JL.Exception({ "x": x, "y": y, "v": v });
    }
}
function f1(anArray) { var i; try { for(i = 0; i < anArray.length; i++) { f2(anArray[i]); } } catch(e) { // Throw new JavaScript exception that contains the original one throw new JL.Exception({ "i": i }, e); } }
try { f1([1, 2, 3]); } catch(e) { JL().fatalException("Exception was thrown!", e); }

The fatalException function knows how to read the additional information and the inner exception from the Exception object, and will log it all.

If you want to, there is nothing stopping you from having an inner exception that itself has an inner exception. You can go as deep as you want.

More about JavaScript try catch