June 3, 2015

Client-side Errors in Rich Internet Application-Architectures

Problem

When building applications with frameworks like AngularJS, we usually create so called “Rich Internet Applications” (RIA). The architecture of a RIA differs from a “traditional” application in several aspects:

  1. The server is (almost) stateless
  2. The client is stateful
  3. Server and client communicate over a coarse-grained interface
  4. The client contains (more) business logic

With good reason, AngularJS get’s hyped quite a lot and the blogosphere is full with articles explain how to implement a RIA architecture with AngularJS. AngularJS is particularly good at implementing e.g. the client state and business logic. Take for example the whole dependency injection mechanism and the support for test-driven development in the API.

However, one important aspect often gets overlooked, simply because we, the developers, are not used it: Dealing with errors on the client side. Why are we not used to it? In old/traditional architectures the UI state and business logic is is implemented on the server. Every (non-trivial) click, state transition, interaction etc. is handled on the server meaning unexpected errors, bugs, etc. are always caught and end up in our Logback/SLF4J/whatever log file. Often the default behavior is already enough (Servlet engine catches the error, Log library handles the error) and we rarely design this part of our application explicitly.

Back to AngularJS. As stated above, writing applications in AngularJS means writing business logic and UI state in the client. What if your code contains an error? What if one of your dependencies contain an error? Well, we will never know (unless you call your users and ask them check the browser’s JavaScript console, of course).

Solution

Closing this gap involves three tasks:

  1. Catching all errors on the client side
  2. Sending them to the server
  3. Logging them

This blog article will concentrate on the first item. Implementing item two and three should be obvious. However, for the second item, instead of sending error messages to your server, you could use services like:

These services are very nice and worth a look. However, I prefer to first collect all relevant log entries in one place and use a tool on top of this.

Catching AngularJS errors

Catching errors in AngularJS applications is quite simple due to the included dependency mechanism.

Option 1

The default module ng includes a $log service that is used by AngularJS itself and (hopefully) by your AngularJS libraries as well as your own code.  Instead of writing something like

you would inject the $log service and write

In AngularJS’ DI framework, you can easily override a provided services. We can use this to override the default $log implementation and to provide a custom service which sends the error message to our server:

 

In the error function, we additionally send the message to our server.

Option 2

The disadvantage of option 1 is that we loose information about the error, e.g. the stack strace. The solution is too hook into AngularJS a bit deeper by decorating the $exceptionHandler service:

This allows us to process the exception message as well as the stack trace.

Catching generic JavaScript errors

Both options above work, because AngularJS takes care to catch the exceptions in our code. Possible sources are

  • controller/service/directive construction function
  • event handlers, e.g. ng-click
  • callbacks in promises

However, everything that happens outside of an AngularJS digest cycle/callback/etc. can’t be intercepted. For example, this could be a DOM callback function registered with jQuery. Fortunately, JavaScript allows us to register a global error handler:

The return code tells the Browser, if the error should be further processed (e.g. call the default handler).

 

 

 

 

 

 

Roman Roelofsen
Developer at thecodecampus </>


Leave a Reply

Add code to your comment in Markdown syntax.
Like this:
`inline example`

```
code block
example
```

Your email address will not be published.