Tracking errors in Sentry (in node.js)
Tags: node, javascript, logging
Published: 2018-03-27 15:53:52

---
title: Tracking errors in Sentry (in node.js)
published: true
description: "Sentry is an open source error tracking tool, that helps developers monitor and fix crashes in real time. It's really fast to set up, and supports many platforms and programming languages. Here's an intro."
tags: node, javascript, logging
---

# Introduction

Sentry is an open source error tracking tool, that helps developers monitor and fix crashes in real time. It's really fast to set up, and supports many platforms and programming languages.

On the Web UI of sentry, you can see all errors that your code captured, a stack trace, context and breadcrumbs (if you implement it).

# Setting up your Sentry project

After you've registered on [Sentry](https://sentry.io) and logged in, press New Project in the top-right corner.

![alt-image](https://i.gyazo.com/ab375fd14b1381590eb2d62a7f1356e5.png)

In this tutorial, we're covering the generic Node.js usage of Sentry, so make sure to choose Node.js instead of Express.

After giving your project a name and clicking on the shiny Create button, a documention page should pop up.

![alt-image](https://i.gyazo.com/24f3e14b1606c9f353d9650eb6761f30.png)

Below the "Configuring the Client" headline, there should be a codeblock containing your Sentry DSN. **Save that code** somewhere, because **we'll need it later**, so we can communicate with Sentry via our client module, Raven.

# Setting up Raven

After creating your project on the Sentry website, it's time to spin up your editor and start coding.

First, you'll need to add the Raven module to your project.

```
npm install raven --save
```

Open the code you've saved earlier from the Sentry website, and paste it along the other require statements in your file. To make the code **ready for public Git** providers, don't forget to **remove the hardcoded Sentry DSN**, and move it to an external gitignored config file, or to **environment variables**.

# Capturing errors

Let's present the different methods of capturing errors with a sample code.

```js
function doSomething(data) {
  if (data) { // Check if our data is defined or not
    return data; // Return data
  } else {
    throw new Error("Missing required data parameter."); // Throw error
  }
}

doSomething(); // Call doSomething without defining data -> error thrown
```

Our function `doSomething` will check if it's parameter `data` is defined: if it is, it just returns it, but if it isn't, **it throws an error**. Just after defining `doSomething`, our code calls it, **without supplying the data parameter**, therefore throwing an error.

There are two main methods for capturing issues with the Raven client:

 1. Wrapping code into a context, or
 2. Manually capturing errors.

## Method 1 - Wrapping code into a context

There are two methods of wrapping code into a context: `Raven.context`, and `Raven.wrap`. There aren't many differences, except that `Raven.wrap` **returns a function**, while `Raven.context` doesn't, meaning that you'd want to use `Raven.wrap` for code like callbacks, and `Raven.context` for just generic code wrapping.

```js
var Raven = require('raven');

Raven.config('https://13c3c4f3c6094d749436722b3031f787:6fe68b76c7e34d2cb69831355d6e3beb@sentry.io/301629').install();

function doSomething(data) {
  if (data) { // Check if our data is defined or not
    return data; // Return data
  } else {
    throw new Error("Missing required data parameter."); // Throw error
  }
}

Raven.context(function() { // Wrap the following code with Raven.context
  doSomething(); // Call doSomething without defining data -> error thrown
});

setTimeout(Raven.wrap(function() {
  doSomething(); // Call doSomething without defining data -> error thrown
}, 1000); // Run doSomething delayed with setTimeout and Raven.wrap
```

There's a major advantage for using this method instead of manually capturing errors: code inside a context (a.k.a. code inside `Raven.wrap` or `Raven.context`) has **access to methods that enable associating data with the context**, which can be useful for debugging.

```js
var Raven = require('raven');

Raven.config('https://13c3c4f3c6094d749436722b3031f787:6fe68b76c7e34d2cb69831355d6e3beb@sentry.io/301629').install();

function doSomething(data) {
  if (data) { // Check if our data is defined or not
    return data; // Return data
  } else {
    throw new Error("Missing required data parameter."); // Throw error
  }
}

Raven.context(function() { // Wrap the following code with Raven.context
  Raven.setContext({ // Set context data with the specified object
    tags: {
      component: 'main'
    }
  });
  Raven.mergeContext({ // Extend context data with the specified object
    tags: {
      method: 'doSomething'
    }
  });
  console.log(Raven.getContext());
  
  doSomething(); // Call doSomething without defining data -> error thrown
});
```

Keep in mind that `setContext`, `mergeContext` and `getContext` **only work** in code **inside a context**.

You can store anything inside your context data, for example, you could associate errors with user data, like this:

```js
Raven.setContext({
  user: {
    email: 'matt@example.com',
    id: 123
  }
});
```

## Method 2 - Manually capturing errors

With Raven's `caputreException`, you can capture **non-thrown exceptions**, or you can capture thrown exceptions using a **try-catch** block.

```js
var Raven = require('raven');

Raven.config('https://13c3c4f3c6094d749436722b3031f787:6fe68b76c7e34d2cb69831355d6e3beb@sentry.io/301629').install();

function doSomething(data) {
  if (data) { // Check if our data is defined or not
    return data; // Return data
  } else {
    throw new Error("Missing required data parameter."); // Throw error
  }
}

try {
  doSomething(); // Call doSomething without defining data -> error thrown
} catch (e) {
  Raven.captureException(e, function(sendErr) { // Capture exception
    if (sendErr) { // Check if send failed
      console.error("Failed to send exception to Sentry.");
    } else {
      console.log("Exception sent to Sentry.");
    }
  });
}
```

You can use `captureMessage` to **send non-exception error messages** to Sentry.

```js
var Raven = require('raven');

Raven.config('https://13c3c4f3c6094d749436722b3031f787:6fe68b76c7e34d2cb69831355d6e3beb@sentry.io/301629').install();

function doSomething(data) {
  if (data) { // Check if our data is defined or not
    return; // Return nothing
  } else {
    return "Missing required data parameter."; // Return error
  }
}

var err = doSomething(); // Call doSomething without defining data -> error returned
if (err) {
  Raven.captureMessage(err, function(err) {
    // Sent!
  });
}
```

Although you **do not have access** to `setContext` and other context-only functions, you can **supply additional data** to `captureException` and `captureMessage` that should be captured.

```js
Raven.captureException(err, {
  user: { id: 123 }, // User-related info
  req: req, // Request object from HTTP web server (handled by Raven Express)
  tags: { component: 'main', method: 'doSomething' }, // Tags
  extra: { catType: cat.type }, // Any other data you'd specify with setContext
  level: 'error' // Event level
});
```

For more additional information you can provide, visit [the Raven Node docs](https://docs.sentry.io/clients/node/usage/#additional-data).

# Breadcrumbs, breadcrumbs everywhere!

For additional info on data *before* the exception happened, Breadcrumbs are the solution. Breadcrumbs are a **trail of events** that occurred in your application **leading up to a captured error**. They can be as simple as **a log message**, or they can contain **rich metadata** about the state of your appliction, like network requests, database queries, or even earlier occurring errors.

Raven for Node supports a feature called `autoBreadcrumbs`, which **automatically records** useful breadcrumbs, like HTTP(S) requests, log statements (with `console.log`, `.warn`, etc.), and PostgreSQL queries via the `pg` module.

To enable it, add this setting to `Raven.config`, and you're good to go!

```js
Raven.config('https://13c3c4f3c6094d749436722b3031f787:6fe68b76c7e34d2cb69831355d6e3beb@sentry.io/301629', {
  autoBreadcrumbs: true // Enable autoBreadcrumbs
}).install();
```

This may be just enough for you, but if it isn't, fear not! There's an easy way of capturing breadcrumbs manually.

```js
var Raven = require('raven');

Raven.config('https://13c3c4f3c6094d749436722b3031f787:6fe68b76c7e34d2cb69831355d6e3beb@sentry.io/301629').install();

function doSomething(data) {
  if (data) { // Check if our data is defined or not
    return data; // Return data
  } else {
    throw new Error("Missing required data parameter."); // Throw error
  }
}

var ourJson = "{}"; // Blank JSON object, not containing needed data.

Raven.context(function() { // Wrap the following code with Raven.context
  var parsedJson = JSON.parse(ourJson); // Parse JSON
  Raven.captureBreadcrumb({ // Capture breadcrumb
    message: 'Parsed JSON',
    category: 'log',
    data: {
      raw: ourJson,
      parsedJson: parsedJson
    }
  });
  doSomething(parsedJson.data); // Call doSomething with non-existing data -> error thrown
});
```

If we view the error on the Web UI, we can see our breadcrumb containing our raw JSON and our parsed JSON, this way we can see if the JSON doesn't have the needed data (which is the case here), or if we parsed the JSON wrong.

This can be really useful for debugging info that is coming from an outside source, so you can see if your code is wrong, or if the users' input is wrong.