Blog
ArchiveTwitterFeed

Trace Errors Through Your Stack Using Unique Identifiers in Sentry

Imagine you, dear reader, have an e-commerce website (it’s 2019, after all) where you sell artisanal hot dogs. This site is built on several services that talk to each other to make sure your customers can easily purchase their organic, grass-fed hot dogs.

But what happens when these services stop behaving as expected and suddenly your front-end shows “500: Internal Server Error”? Without monitoring for errors on each service, you’ll have to dig through the available logs and hopefully, eventually figure out that a bug within one of the many services causes this 500 error.

Ultimately, you can spend a lot of time and effort searching for bugs in this way — navigating back and forth between various monitoring and logging solutions trying to find the root cause. But why spend that time and effort when you don’t actually need to?

Tracing: uncover an error’s full story

Instead, you can send errors from each service to Sentry and correlate them using a unique identifier, allowing you to trace the error and pinpoint the service and code behaving unexpectedly.

You need to complete a few steps, including:

  1. Generate a unique transaction identifier and set as a Sentry tag in the service issuing the request.
  2. Set the transaction identifier as a custom header when making the request.
  3. Parse the transaction header and set as a Sentry tag in the receiving service.

In the diagram below, we use transactionId as the unique identifier.

https://blog.sentry.io/img/post-images/tracing/tracing-simplified.png

Now if there are errors on both services as a result of the failed transaction or request, the errors will have the same transactionId identifier set as a tag. Tracing the 500 error shown on the front-end to the actual error in back-end would look something like this:


Tracing + Sentry configuration

Let’s go through an example with our artisanal hot dog vendor site: a simple web application with a front-end written in JavaScript and server written in Node.js. Let’s assume that these services are both already configured with Sentry using Sentry’s JavaScript SDK.

1. Generate a unique identifier and set as a Sentry tag on issuing service

First, generate a unique identifier from the service issuing the request (e.g., your client JavaScript code). This unique identifier could be a transaction ID or a session ID created when the web application first loads. Set this value as a Sentry tag using the Sentry SDK. Below, we use a transactionId as our unique identifier:

// generate unique transactionId and set as Sentry tag
const transactionId = Math.random().toString(36).substr(2, 9);
Sentry.configureScope(scope => {
    scope.setTag("transaction_id", transactionId);
});

2. Transmit the unique identifier as custom header and handle error(s) appropriately

When initiating the request, set this same identifier as a custom request header. If the request fails, handle it in such a way that Sentry’s SDK will collect the error (either manually throw it, or capture it using Sentry.captureError or Sentry.captureMessage).

// perform request (set transctionID as header and throw error appropriately)
fetch('https://my.artisanal-hot-dogs.com/checkout', {  
    method: 'POST',  
    headers: {  
      "X-Transaction-ID": transactionId  
    },  
    body: order
})
.then(function (response) {
    if (response.status !== 200) {
        throw new Error(response.status + " - " + response.statusText);
    }
})  
.catch(function (error) {  
    throw error;  
});

3. Extract the header on the receiving service

In the receiving service (the server responding to the request), extract the unique identifier, in our case tranactionId custom header and set it as a Sentry tag. This means that both services should have set the same tag key/value pair.

let transactionId = request.header('X-Transaction-ID');

if (transactionId) {
    Sentry.configureScope(scope => {
        scope.setTag("transaction_id", transactionId);
    });
}

Et voilà! Now, errors caused in this transaction or session can be traced/correlated using the unique identifier.


While the tracing example used here correlates an error from the front-end to the backend, tracing can be applied to any thing that communicates to another thing, like one microservice to another microservice, and even correlate items or entries within developer tools. Take a look at how we used Sentry and nginx to trace errors to logs.

You can also see an example of how we implemented error tracing within Sentry here.

Your code is broken. Let's Fix it.
Start using Sentry