Tuesday, November 8, 2016

Logging Errors in Client-Side Applications

Logging is an important part of any software application, both during active development and when it's running in production mode.

When you're working on the server, there are hundreds of libraries available to you regardless of your server-side language of choice, a wide range of storage mechanisms, and all sorts of tools you can use to work with the resulting logs.

However, when it comes to client-side applications, logging is something that often gets overlooked, and the options open to you are rather more limited.

In this article I'll look at some of the ways in which you can implement logging in a client-side application; particularly in a JavaScript-heavy, single-page application (SPA).

The Console

Perhaps the most common and obvious way to log errors and messages is the console. While it might appear a primitive solution, there's absolutely no doubt that it's an invaluable tool for debugging during development, so it's probably a good place to start.

The implementation of console isn't always consistent — particularly in IE, perhaps unsurprisingly — but in general there are four key methods available to you:

console.log()
console.info()
console.warn()
console.error()

The output from each of these four methods is subtly different, and most web console implementations (i.e., Dev Tools) allow you to filter messages based on the method used; that is, the logging level.

In order to mitigate the differences between browsers, you can use a wrapper function — such as this one from Paul Irish. The WHATWG is attempting to standardize the console API, but the spec is still at an early stage and unlikely to be implemented for some time.

Tip: If you find that your code is littered with console.log() statements, you might find tools such as grunt-remove-logging or grunt-strip for Grunt, or gulp-strip-debug for Gulp useful for when you move an application into production.

Enhancing the console

There are a couple of libraries you can use to "super-charge" the console.

Logdown

Logdown is a tiny library which provides a few enhancements to the console. You'll find a demo here.

Logdown allows you to specify prefixes upon instantiation; one possible use for this is to separate out your log messages by module, for example:

var uiLogger = new Logdown({prefix: 'MyApp:UI'});
var networkServiceLogger = new Logdown({prefix: 'MyApp:Network'});

You can then enable or disable the loggers by their prefix, for example:

Logdown.disable('MyApp:UI');
Logdown.enable('MyApp:Network');
Logdown.disable('MyApp:*'); // wildcards are supported, too

Disabling a logger effectively silences it.

Once you've instatatied one or more loggers, you can log messages using the log(), warn(), info() and error() methods:

var logger = new Logdown();
logger.log('Page changed');
logger.warn('XYZ has been deprecated in favour of 123');
logger.info('Informational message here');
logger.error('Server API not available!');

Logdown also provides Markdown support:

var logger = new Logdown({markdown: true}); // Technically "markdown: true" isn't required; it's enabled by default
logger.warn('_XYZ_ has been *deprecated* in favour of _123_');

console.message

console.message is another library for beautifying the console's output.

Here's a quick animation from the documentation, that shows off some of its features:

console.message in action

Essentially the library provides a chainable interface with methods which allow you to format text, group messages together and make them collapsible, send interactive DOM elements or objects to the log — and even include images.

Limitations of the console

The console is great while you're building an application and you can have it open in front of you, but unless you happen to be looking over a user's shoulders, and they happen to have the web console open in their browser, you won't get to see the result.

What we can do instead is send any errors — or even debug messages during development — to a server somewhere, so that we can access them remotely.

Other Things to Consider

Now that we've looked at some of the solutions available to you, let's look at a few additional considerations.

Capturing global errors

At the very least, it's worth capturing and logging any unhandled exceptions. You can do this using window.onerror. Here's a really simple example:

window.onerror = function(message, file, line) {
  console.log('An error occured at line ' + line + ' of ' + file + ': ' + message);
};

Stack traces

Stack traces provide an additional level of detail when an error occurs, which you may wish to make use of in development. There are a couple of libraries that help to build them.

TraceKit

TraceKit allows you to inject stack traces into exceptions, and do something with them (e.g. send them to your server-side logging component) by subscribing to them.

Here's what the code might look like:

TraceKit.report.subscribe(function yourLogger(errorReport) {
  //send via ajax to server, or use console.error in development
  //to get you started see: http://ift.tt/2ejuu03
});

Then, in your application:

try {
  /*
   * your application code here
   *
   */
  throw new Error('oops');
} catch (e) {
  TraceKit.report(e); //error with stack trace gets normalized and sent to subscriber
}

stacktrace.js

stacktrace.js is, to quote the documentation , "[a] framework-agnostic, micro-library for getting stack traces in all web browsers".

It provides a method named printStackTrace() which you can use in an error handler to add a stack trace to your logging function. For example, we could enhance our server-side logger as follows:

Continue reading %Logging Errors in Client-Side Applications%


by Lukas White via SitePoint

No comments:

Post a Comment