Know unknown unknowns with Sentry

Client side javascript error reporting using Sentry.

There are known unknowns, and then there are unknown unknowns ...

Donald Rumsfeld might have been describing releasing software into the wild. No matter if you design defensive and robust code, or if you do a lot of in the lab testing; once the software is released, the users will find its limit and break it. You need to be prepared. A huge part is designing a smooth workflow for you to find out something bad has happened, so you can react, patch, update and release again.

For my projects (nodejs on the server, angularjs on the frontend), I use Sentry to catch and report all run-time exceptions. I found the setup extremely simple and the reporting does an excellent job providing useful context for each error. Here is my typical setup for a project that uses Connect web server to serve web pages. Sentry will catch and report all errors that happen during a request processing and all global JavaScript exceptions. I will also configure client-side browser exception handler to send errors to Sentry.

As example I will add Sentry error reporting to my open source project proud-connect. It is simple Connect-based server returning a badge image given an NPM username with total number of downloads last month:

jashkenas bahmutov

First, create a new Sentry project and select the environment

new project

Once the project is created, the screen changes and gives you step by step instructions. Most important:

  • Each project has an identifier called DSN, used when initializing reporting
  • The reporting module is called Raven, and there are modules for every major platform (JavaScript, Java, Python, PHP, C#, Ruby and even R)

Server side error support

Lets install server-side error handlers

Global exception handler

npm install raven --save
1
2
3
4
5
6
7
if (process.env.NODE_ENV === 'production') {
var raven = require('raven');
var SENTRY_DSN = 'https://<DSN>@app.getsentry.com/...';
var client = new raven.Client(SENTRY_DSN);
client.patchGlobal();
}
foo.bar // this Error will be reported

That's it. I like loading raven as the first require in the application, so if there is an exception when loading anything else, it will be reported. client.patchGlobal(); installs global Nodejs exception handler. A typical error will generate a wealth of information

sentry error

Notice you have the stack information, and you can click on "Toggle context" button to expand each line. You can also inspect request parameters and cookies (if you have connect.bodyParser, .cookierParser and .query middleware in the chain).

sentry error 2

By default, the raven Connect reporting only happens in production, if you would like to test how well it works during the development, set the environment variable before running node NODE_ENV=production node script.js

Misconfigured requests

Lets add reporting misconfigured requests

1
2
3
4
5
var app = connect()
.use(connect.bodyParser())
.use(connect.cookieParser())
...
.use(raven.middleware.connect(RAVEN_URI));

Place Raven connect function last in the chain to report as errors any unhandled request. There will be a wealth of information, including stack, query params and cookies. For more details, see Integrations.

Client side error support

Before generating errors, add host domains to the Sentry Client Security list, for example http://proud.herokuapp.com. If you plan to check the error reporting from local server, you can add something like http://localhost:3000 as well.

The proud-connect client page is very simple, but I still will install a client side error handler

1
2
3
4
5
6
7
<script src="//cdn.ravenjs.com/1.1.2/jquery,native/raven.min.js"></script>
<script>
Raven.config(<DSN>, {}).install();
</script>
<script>
foo.bar(); // this error will be reported to Sentry
</script>

I recommend loading Raven script first, to catch possible errors in other scripts. If other libraries or frameworks on the page have global exception handler support, you can add sending the error to Sentry. For example, for AngularJs projects:

1
2
3
4
5
6
7
var app = angular.module('app', []);
app.factory('$exceptionHandler', function () {
return function blockBlogExceptionHandler(exception, cause) {
console.error(exception.stack);
Raven.captureException(exception);
};
});

You can add error reporting to any code block, pass extra information, or just send non error messages. See Raven-js usage doc.

Misc

By default the number of stack lines shown in the traceback is limited. You can increase the limit using Error.stackTraceLimit = 100; command.

I usually setup a /status url in my servers that responds with simple 200 "working" message to test successful deployment. I also setup /status/error url to generate and throw an error and test my Sentry connection and setup.

Conclusion

It is your responsibility to prepare for times when the deployed website fails. The users will not email / call you, unless they are paying a lot of money and need the feature. In any case, if you get the error report first (Sentry will notify you right away!), you would be in much better position to take action.

Sentry did very good job making the setup as easy as possible, and my experience as a developer was very positive.

Related - Catch all errors in Angular app