Send Node.js errors from AWS Lambda to Honeybadger

We are starting to make more and more use of AWS Lambda functions and so far we’ve had some very good success stories. Developing a Node.js function is pretty easy (depending on what you are trying to do of course!) and debugging in development is pretty easy too.

However, troubleshooting and debugging your Lambda functions is not very easy when running at scale. By default, AWS Lambda will log to Cloudwatch. Fine so far. If you have several thousand executions every minute, the volume of logs generated in Cloudwatch is simply unmanageable in terms of seeing patterns and aggregations around your exceptions.

I’m still at the beginning of my Node.js journey and I tried in vain to write exceptions to custom Cloudwatch logs, but it was in vain. Painful and not very well documented.

We also use (and love!) Honeybadger in all our Rails apps. Thankfully, around the time I was trying to implement better exception handling in my Lambda function, I came across a very good blog post describing exactly how to do this: How to report Node.js errors from AWS Lambda. Thanks Honeybadger!

The template code provided by the Honeybadger crew worked as expected on Lambda. It was almost exactly what I needed except for the fact that I am using the async package. Thankfully, Josh was very helpful when I opened a GitHub issue and he posted an updated template. Once again, it worked as expected.

My production code looked a little different to the Honeybadger template, and if I’m honest it’s not as elegant as the template. The reason we decided not to use the template provided was because it uses the domain package, which has been marked for deprecation.

So, here is a sanitised version of what we are doing in production. Hopefully someone finds this useful!

var async = require('async')
var Honeybadger = require("honeybadger");
var Promise = require("promise");

// Change to your Honeybadger.io API key.
const HB_API_KEY = '_____';

function do_stuff(cb){
  console.log('do_stuff')
  var ed = 123;
  return cb()
}

function break_stuff(cb){
  console.log('break_stuff')
  var ed = [];
  var kaboom = ed[0].some_id; // this line will raise an error
  return cb()
}

// Your handler function.
function handler(event, context) {
  console.log("Event:", event);
  console.log("Context:", context);

  var hb = new Honeybadger({
    apiKey: HB_API_KEY,
    server: { environment_name: "production" },
    logger: console
  });

  var send_to_honeybadger = function(err, opts) {
    return new Promise(function(resolve, reject) {
      hb.once("error", reject).
         once("remoteError", reject).
         once("sent", resolve);
      hb.send(err, opts);
    });
  };

  async.series([
    function (cb) { do_stuff(cb) },
    function (cb) { do_stuff(cb) },
    function (cb) { break_stuff(cb) }
  ], function(err, results){
    if (err) {
      send_to_honeybadger(err, { context: { event: event } }).then(function() {
        context.fail(err);
      }).catch(function(sendErr) {
        console.error("Unable to report error to Honeybadger:", sendErr)
        context.fail(err);
      });
    } else context.succeed('multipart: ok');
  });
}

// Build and export the function.
exports.handler = handler;
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s