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:
- Host the source maps along with your minified files
- 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
- Generating source maps
- Pushing them to sentry with a release tag
- 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:
- Add
in your browserify optionsdebug:true
- Import
& initialise the same before minifying the codegulp-sourcemaps
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.