Angular + Sentry: Application Monitoring with Releases & Source Maps

How to use it in your application step-by-step

Leonardo Giroto
Loft
11 min readJun 29, 2020

--

Angular + Sentry: Error Monitoring with Releases & Source Maps

When building web applications, we expect them to be used in a great range of devices and browsers by our users. In our industry, where things have to move fast, no developer is safe from introducing bugs to a project, no matter how many tests you write or how much of an amazing QA you have.

Another critical factor is that, especially when working with B2C applications, most users are unlikely to report errors. They might either just ignore them (if they are not critical) or much worse: quit using your application without you even getting a chance to understand why.

Moreover, when users do report errors, the information is usually generic and insufficient. Therefore many times you are left without knowing what are the actual steps you need to reproduce in order to identify what in your project has originated that issue.

So what can we do about it? We can use tools to help us monitor our application and track for errors. We can improve our process of identifying and fixing bugs; and also learn from them.

Sentry

There are many tools available that can aid us on monitoring our applications and identifying problems. Sentry comes as a very interesting alternative to handle error tracking. It is easy to integrate with many kinds of applications (including Angular projects) and provides very powerful features. Moreover, it is an open source project and has a pretty fair free tier.

“Every developer writes tons of bugs. And many of those bugs get shipped to production. That’s unavoidable. But thanks to Sentry, you can find and fix those bugs before your customers even notice a problem. When things go to hell, we’ll help you fight the fires.” Sentry’s website.

Installing Sentry

Adding Sentry to our Angular project is pretty straigh-forward.
First, we have to install Sentry’s package in our project.

Then, we have to initialize it and add it to our AppModule.

So this is all we have to do get the basics of Sentry running. By initializing Sentry passing our DSN and providing an ErrorHandler implementation which calls Sentry’s captureException method, we are ready to go.

“After you completed setting up a project in Sentry, you’ll be given a value which we call a DSN, or Data Source Name. It looks a lot like a standard URL, but it’s actually just a representation of the configuration required by the Sentry SDKs.”

Additionaly, we have also provided information regarding the environment to Sentry, which will allow it to categorize the issue based on it. Our example configuration differs production and development environments, but you can configure as many environments as needed, with the desired name.

It is important to notice that with this configuration Sentry is going to intercept every error on our Angular application. But we can take advantage on our implementation of the SentryErrorHandler class to customize what we are capturing.

It is possible, for instance, to check if the captured error is a Server Error and prevent Sentry to log it based on its Status or else. Also, we could notify our user’s on some specific errors or even send the error somewhere else; whatever makes sense to your application.

“On its own, @sentry/browser will report any uncaught exceptions triggered from your application. Additionally, @sentry/browser can be configured to catch any Angular-specific (2.x) exceptions reported through the @angular/core/ErrorHandler component.”

obs: you can also check out all of Sentry’s package init options and hooks here: https://docs.sentry.io/error-reporting/configuration/?platform=javascript

Providing Context

In a real world application, most times we are going to need more information regarding that error; especially about the user who triggered it and what led the user to that.

Breadcrumbs

Firstly, an extremely useful functionality that Sentry provides us out of the box is the breadcrumbs of user actions. Like that, it makes it much easier to find out where the user was and what exactly was it doing when the error occurred; especially when it has something to do with the user’s interaction.

sentry’s breadcrumb functionality screen print
Sentry’s breadcrumb funcionality

SetUser

Sentry allows us to enrichen our information by providing more context about our application user. In order to do that, we would need to add a new configuration, by calling Sentry’s configureScope method, like shown below.

Here we call the setUser method passing an object containing information on our user. In the example it is fixed strings, but of course in a real application you would retrieve it from wherever your user informations are locally stored.

By doing so, we are now able to identify in an issue the provided information about the associated user and even query and group errors by an specific user or group of users.

Sentry’s context information

Sending users to Sentry will unlock a number of features, primarily the ability to drill down into the number of users affecting an issue, as well to get a broader sense about the quality of the application.

SetTag

We can also add tags (key/value pairs) to our scope configuration, which is going to attach it to our issue. Adding tags makes it easier to group issues by them and look for related ones. So going back to our app configuration, we would add them by calling the setTag method, like shown below:

“Several common uses for tags include:
- The hostname of the server
- The version of your platform (e.g. iOS 5.0)
- The user’s language

Once you’ve started sending tagged data, you’ll see it show up in a few places:
- The filters within the sidebar on the project stream page.
- Summarized within an event on the sidebar.
- The tags page on an aggregated event.”

Extra Content

Sometimes you might want to send some specific yet important information regarding that issue to Sentry. So besides the structured context that Sentry understands that was mentioned before, it is also possible to send key/value pairs of custom data alongside your issue.

Though this information is not indexed, it might be helpful to understand what might be happening regarding that issue. Again, going back to our app configuration, we would send these information like shown below.

“Be aware of maximum payload size — There are times, when you may want to send the whole application state as extra data. This is not recommended as application state can be very large and easily exceed the 200kB maximum that Sentry has on individual event payloads.”

Creating Releases

A release is a specific version of the code you deployed to an environment, in which Sentry can keep track of. By doing so, it is possible to relate issues to its release (version), which makes it way easier to identify which changes introduced the problem in our application.

Firstly, for our Angular app, in order to use releases we are going to need to install another package from Sentry.

Then, to create a release in Sentry we need to update our app’s Sentry initialization, like shown below.

The release name is a string which you can choose, as long as it is unique along the organization in Sentry. In our example, we would concat our app name to its version, which we are retriveing from our package.json.

“There are a few restrictions — the release name cannot contain newlines, spaces, or “\”, be “.”, “..”, or exceed 200 characters.”

By doing so, when a new issue is sent to Sentry we are already going to have it attached to an specific release. When the release name changes (in our example, when the app version changes), a new release is going to be created in Sentry and new issues are going to be associated with it.

Generating Source Maps

The Javascript that is executed by the browsers acessing our application often suffer transformations from the original source code. That is our case when working with Angular applications. We use Typescript, which is then compiled to Javascript, and we also have our sources combined and minified when generating our final build.

“In these situations, it’s much easier to debug the original source, rather than the source in the transformed state that the browser has downloaded. A source map is a file that maps from the transformed source to the original source, enabling the browser to reconstruct the original source and present the reconstructed original in the debugger.” MDN web docs

When working with Angular, we can have those source maps generated for us by changing our configurations on our project’sangular.json file. Under each project build’s configuration, you have a sourcemap property. It is originally set as false, but you can configure it as shown bellow:

obs: notice that here we are setting the source maps for our production builds, but you could set it for other configurations as well accordingly to your needs.

The scripts property says we want to generate source maps for our scripts and the styles property says we want to generate source maps for our styles. That way, we are able to generate just what we need.

The hidden property is very interesting: it allows us to generate the source maps but not reference them in the JavaScript files. It won’t ensure that the source maps are not provided on our final build, but it will ensure that the browser won’t attempt to fetch them. We will talk about in the next section on how to handle that.

obs: It is important to notice that this configuration has been available since Angular 7.2. In versions of the application prior to that, you are able to generate sourceMaps setting the sourceMap property as true, which will generate all source map files and we don’t get to take advtange of the hidden property.

Uploading Source Maps to Sentry

“Sentry supports un-minifying JavaScript via source maps. This lets you view source code context obtained from stack traces in their original untransformed form, which is particularly useful for debugging minified code (for example, UglifyJS), or transpiled code from a higher-level language (for example, TypeScript, ES6).” Sentry’s docs

Providing Sentry with our source map is extremely useful for tracking errors. If we need to dig deeper on the code to find where the problem was caused it is going to be quite difficult without them; it would be similar to trying to debug production code.

Without the source map, we are looking at the final generated bundle and not our original source code. By providing Sentry with our source code we can make our life a lot easier when trying to identify the issues root causes.

So, how can we do it?

In Sentry, you have two ways to handle Source Maps. You can either serve them publicly yourself over HTTP, along with your source files, or upload them directly to Sentry; the later being their recommended method. Here we are going to see how we can upload our source maps directly to Sentry, associating them with one of our releases.

Firstly, we need to create a release on our build system.

The release name must match the one in the release option in your SDK initialization code, as we have seen previously on this article.

In order to allow Sentry to generate correctly our stack traces we must provide both the minified files and their corresponding source maps. Then, we use the upload-sourcemaps command to scan a folder for source maps, process them and upload them to Sentry:

This command will upload all .js and .map files associated with the new release. In Angular, by default, our path would be dist/<my-project-name>.

As mentioned before, even though the browser won’t try to fetch the source maps, if we publish our project as it is, we are still going to be providing our source maps. So, one thing we could do here to avoid it is to add a final step to clean them up before publishing.

Lastly, once all files have been uploaded and your app has been published successfully, all you have to do is finalize the release by running the following command:

And that is it. We are now ready to track those errors!

“Sentry expects that source code and source maps in a given release are uploaded to Sentry before errors occur in that release. If you upload artifacts after an error is captured by Sentry, Sentry will not go back and retroactively apply any source annotations to those errors. Only new errors triggered after the artifact was uploaded will be affected.”

Sentry is one of the many tools we can use to aid us on monitoring our applications and trying to capture errors before our users do, along with providing more informations on them.

Error tracking and monitoring are an worthy investment that can provide us a lot of value. It is most perceivable in the long term when our application grows, introducing more features (and consequentially more bugs) and starting to bring more users along.

Hope it helps! 😉

References:

--

--