paint-brush
Pinpoint Those Pesky Minified Javascript Errors With Sentryby@pezcoder
522 reads
522 reads

Pinpoint Those Pesky Minified Javascript Errors With Sentry

by Rahul GuptaJune 19th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Source Maps help you fix Javascript errors with the help of the tool Sentry. They map errors to the source map before it becomes a mess while shipping to the client. Source maps can be pushed to sentry with a release tag or pushed to a specific release page. The solution for this is to set up a VPN so these are not accessible anywhere else other than your company. We’ll discuss the latter approach here. The articles originate from the problems I faced when trying to upload source maps to Sentry & how I made it work.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Pinpoint Those Pesky Minified Javascript Errors With Sentry
Rahul Gupta HackerNoon profile picture

Source Maps to the rescue.!

So you’ve set up Sentry for you javascript project & released it to production, excited about the first error that’ll come up & you got this!

Uh oh, so “name of undefined”, oh I know, an

If check
is all I need.

where again? — In:

chatModule-322816772254.js in h.e.uploadFile at line 1:27468.

Wait, where’s that? — Darn!

That’s exactly what source maps help you fix, to map this error location to the line of code you typed before it became a mess while shipping to the client.

The Approach

Sentry provides multiple approaches from where it can read source maps:

  1. Host the source maps along with your minified files
  2. Push the source maps to sentry

Hosting your source maps seems like the easiest task but it also exposes your un-minified code to the public, making it easier to find security loopholes. One way to tackle this is to set up a VPN so these are not accessible anywhere else other than your company.

Since we want the freedom to be able to read those sentry errors from anywhere, we’ll discuss the latter approach here.

The articles originate from the problems I faced when trying to upload source maps to sentry & how I made it work in the end.

The Steps

  1. Generating source maps
  2. Pushing them to sentry with a release tag
  3. Label the issues to tie up with the same release tag

Generating source maps

This step will vary based on your build system. If you’re using browserify, gulp, uglify, here’s how you should do it:

  1. Add
    debug:true
    in your browserify options
  2. Import
    gulp-sourcemaps
    & initialise the same before minifying the code

Final code might look something like:

var sourcemaps   = require('gulp-sourcemaps');

browserify(path, { 
    paths: ['./node_modules'],
    // Enables source maps
    debug: true,
})

// Any babel config may come here
// --- 

// Initialises & writes source maps
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(uglify())
.pipe(sourcemaps.write('.'))

// Used to export generated minified files & source maps
.pipe(gulp.dest('./web/js'));

Pushing them to sentry with a release tag

At this point we have our minified files & source maps generated in the

/web/js
folder like
/web/js/file.min.js
&
/web/js/file.js
which are hosted as
https://{your_domain}/js/file.min.js
&
https://{your_domain}/js/file.js
respectively.

Here’s a script which uses

sentry-cli
to upload the javascript files from a folder to sentry & remove the source maps files post that:

// Credit: https://github.com/supriya-raj
var spawn = require('child_process').spawnSync;
var sentryHost, sentryAuthToken, sentryProjectName, sentryOrg, release;

/*
  Change based on your requirement:
  sourceMapsPath: relative path to the folder that contains your minified scripts & source maps
  jsUrlPrefix:  ~ (tilde) -> Acts as your domain. Ex: if the source maps are hosted on domain.com/js/file.js.map
    this should be '~/js'
  release: source maps will be pushed under this release name
  sentryConfig: {
    org: 'dummyorg'
    project: 'my-js-project'
    host: 'https://sentry9.dummyorg.com'
    auth_token: ~
  }
  ^ Example configuration for the sentry url: https://sentry9.dummyorg.com/dummyorg/my-js-project
*/
var sourceMapsPath = 'web/js';
var jsUrlPrefix = '~/js';
var sentryConfig = require('./sentry-config.js')

if(sentryConfig) {
  sentryHost = sentryConfig['host'];
  sentryProjectName = sentryConfig['project'];
  sentryOrg = sentryConfig['org'];
  sentryAuthToken = sentryConfig['auth_token'];
}
release = process.env.COMMIT_HASH;

if (!sentryAuthToken || !sentryHost || !sentryProjectName || !sentryOrg) {
  console.log(console.log('[Error] One or more config parameters required for source map upload are missing!'));
  process.exit(1);
}
if (!release) {
  console.log(console.log('[Error] Environment variable COMMIT_HASH does not exist!'));
  process.exit(1);
}

spawn(
  './node_modules/.bin/sentry-cli', 
  [
    '--auth-token',  sentryAuthToken, '--url', sentryHost, 'releases',
    '--org',  sentryOrg, '--project',  sentryProjectName, 
    'files', release, 'upload-sourcemaps', sourceMapsPath,
    '--url-prefix', jsUrlPrefix, "--no-sourcemap-reference"
  ],
  {stdio: "inherit"}
);

spawn(
  'find', 
  [sourceMapsPath, '-type' ,'f', '-iname', '\*.js.map', '-delete'],
  {stdio: "inherit"}
);

Auth token can be generated by:

Sentry Project -> Settings -> Search (Auth Tokens) -> Create new token

For the above project, we’re using the latest commit hash to mark release tag so, that needs to be passed as an environment variable before executing the provided script:

COMMIT_HASH=${COMMIT_HASH} node ./upload-source-maps-to-sentry.js

Verify the source maps in Sentry should now be available under:

Project -> Releases -> {Commit_Hash} -> Artifacts

Label the issues to tie up with the same release tag

At this point, the source maps are pushed to sentry but for a particular issue, sentry doesn’t know where to find them to un-minify & give you those pretty errors.

To achieve that, send a

release
attribute along with the sentry init configuration, Here’s a sample config for
@Sentry/browser 5.7.1

window.Sentry.init({
    dsn: '<sentry_dsn>',
    release: '<COMMIT_HASH>',
})

& Voila! There you have it, the same error but this time identifiable 👁, thanks to source maps.

Troubleshooting

Here’s a good resource to detect what might have gone wrong with your implementation.