JavaScript Logging with a bit of Sanity

Rob Ballou

Browsers have come a long way in giving web developers a set of useful tools for debugging JavaScript (JS): gone are the days of littering alert() statements throughout your code. We now have inspectors, debuggers, and other tools that make things much easier. And you've probably encountered console.log and the power of JS consoles. This can provide some good insight into what's going on with your code, but there is a catch.

  1. The console isn't alway present. Some browsers do not expose a console if the user has not enabled it (or doesn't have an extension that does that for them). This means that if you have some calls to console.log scattered in your code, this will break JS on that page.
  2. Having your log statements run when you're not actively inspecting those logs (like when your code is in production) can slow down the JS on your site, affecting all your users.

Log smarter

To solve these two problems, here's a common template I use for JavaScript functionality:

/**
 * My JS functionality
 */
(function() {
 
  var my_functionality = {
 
    // should we log messages to the console
    debug: true,
 
    // can we log messages to the console
    can_log: (typeof console !== 'undefined' && typeof console.log !== 'undefined'),
 
    /**
     * Debug aware debugging
     */
    log: function() {
      if (!this.debug || !this.can_log) { return; }
      console.log.apply(console, arguments);
    }
  };
 
})();

Now, we can use my_functionality.log within our code:

awesome_functionality: function() {
  my_functionality.log('awesome_functionality!');
}

When we're done with debugging, we can switch the debug property to false. What does this do? Well, primarily it will check to see if there is a console.log to interact with before using it (fixing problem #1) and we also have a debug flag that allows us to turn off logging for production (fixing problem #2).

Working with teams

This works pretty well as-is but it can be a bit better. On projects with several people working on JS you may find yourself constantly changing those debug flags over to true (or false if you commit that to your codebase!). To help with this, let's change this based on local settings and then inject this into the site's JavaScript based on this.

This part will vary depending on your application stack, but here's an example of how to handle this for Drupal.

First, we want to add this to our sites/default/settings.php file:

$conf['js_debug'] = true;

Next, we'll add a call to hook_js_alter in our template.php file in our theme. This will add the debug setting to Drupal.settings.

/**
 * Implements hook_js_alter().
 */
function my_theme_js_alter(&$javascript) {
  drupal_add_js(
    array('js_debug' => variable_get('js_debug', FALSE)),
    'setting'
  );
}

Finally, we'll update our template with:

// use the settings.php js_debug setting in my_functionality
jQuery(document).ready(function() {
  if (typeof Drupal.settings.js_debug !== 'undefined') {
    my_functionality.debug = Drupal.settings.js_debug;
  }
});

For projects with multiple JS libraries and multiple "debug" settings, it may be worthwhile to make this js_debug variable an array with settings for specific libraries.