Source Maps

Overview

Source maps are a useful tool for debugging and fixing minified JavaScript code without the hassle of determining where in the original code an error occurred. They provide a way to translate between minified code, designed to decrease webpage load times, to the source code the developer writes.

When the information supplied in stack traces does not provide accurate line/column information to track down the source of the error, you can use a source map. Which will provide the translation between the original JavaScript source and the minified version.

Raygun supports source map decoding for the information supplied by browsers which produce a line and column number for error information. Typically any errors which are specifically raised by you will contain this information. Recent versions of Chrome will also produce this information for window.onError global errors.

When an error occurs in the JavaScript of a website using Raygun, an error report including stacktrace is sent to us.

Contained within the stacktrace are the URLs of the scripts in which the error occurred.

Given these URLs, Raygun can download each script file and check for a source map comment. If the comment exists, it tries to map the line of the stacktrace onto source code. This is done by downloading the map file specified.

If a map file is available, Raygun can then perform the mapping which converts the line numbers of the stacktrace on the minified code into line numbers on the source files, it also resolves the source files and symbol names.

Given the source file URL, we can make additional requests for the source files. If we can retrieve these, we insert a snippet of the source code into the stacktrace to give more context to the error.

All of this can be done across the public internet if the files are hosted at the correct URLs and are publicly available.

For many businesses, hosting JavaScript files publicly is not in their best interests. For this reason Raygun provides a source map center, this is a private store of JavaScript files to be used in the source mapping process. It allows source mapping to work without hosting anything publicly.

If a file is uploaded into the source map center, it will be used instead of requesting a file from the internet. All that is required is that the file be labeled with the URL it would be accessed from if it were to be publicly hosted. In most cases this is easy to figure out. For minified files it’s simply the address your minified files are served from.

Determining the address of the map and source files may be a bit harder, but can be determined by looking at the relative URLs in the generated sourceMappingURL comment in the minified files, as well as the sources section of the map files and figuring out the absolute URL based on the address of the hosted minified files.

Next we’ll learn how to get this set up.


Requirements

Raygun requires the following to complete the decode process:

  • An accessible minified JavaScript file as specified in the stack trace. This is either downloaded or retrieved from the source map center to determine if there is map file present
  • The map file to be correctly indicated in the minified JavaScript file using a footer comment
  • An accessible source map file (either publicly hosted or uploaded to the source map center)
  • To provide inline code snippets we also require the sources content field of the map file to be present or for the map file to point to publicly accessible unminified source JavaScript files. If neither of these are provided decoding of line numbers and variable names can still succeed but no inline code will be displayed with the stack trace
  • Recommended minifier tooling
  • We recommend using UglifyJS2 to generate a source map file.

Both the minified JS and its map file should use relative pathing. Look into the -p and –source-map-root options.


Private Source Maps

If you do not wish to have publicly accessible JavaScript files you can upload them directly to Raygun from the JS Source Map Center. This is within the Application Settings submenu on the main dashboard.

Within the Source Map Center you can upload files and label them with the URL they would normally be accessed from. When a JavaScript error is mapped we will use the files you uploaded instead of trying to access them from your servers.

Here you can simply select the minified, mapping and source files to upload. After uploading you can type the URL the resource is mapped to so when our source mapping process tries to retrieve the file, it uses the file you uploaded instead.

You can determine the URL to enter by looking at the map file’s “file” and “sources” properties. You can then prepend the URL of the minified file on production minus the filename.

For instance, we have a minified file example.min.js located at http://example.com/scripts/example.min.js and we upload example.min.js, example.js and example.min.js.map to Raygun.

After the files are uploaded they will be used in the source mapping process.

To get started you will need a tool which can help produce a source map as part of producing a minified version of your JavaScript. We personally use UglifyJS and find it to be a great tool. To install it you will need Node.js installed, you can then run:

npm install uglify-js -g

This will install Uglify so that it’s globally available which is what you will typically want.

To produce minified JavaScript we can simply run

uglifyjs example.js -o example.min.js

To produce minified JavaScript with a mapping file we can run

uglifyjs example.js -o example.min.js --source-map example.min.js.map

This produces the minified file “example.min.js” as well as a source map file “example.min.js.map”

API endpoint

In addition to the upload page we offer an API endpoint, the URL is displayed in the Source Map Center and is specific to each application.

Simply send a post request with “URL” and “file” parameters specified to upload the file. Any existing files with the same URL will be overwritten.

The request must be authenticated by adding either a basic auth header for a user with access to the application (eg. “Authorization: Basic MyBasicAuthToken”) or an External Access Token appended as a querystring parameter (eg. "https://app.raygun.io/upload/jssymbols/MyAppId?authToken=MyExternalAuthToken").

External Access Tokens can be generated from your user settings page.

Here is a cURL example request using Basic Authorization:

curl
  -X POST
  -u email@example.com:password
  -F "url=http://example.com/myjs.min.js"
  -F "file=@C:\website\js\myjs.min.js"  
  https://app.raygun.io/upload/jssymbols/MyAppId

Here is an example using the Grunt plugin grunt_http_upload using an External Access Token:

http_upload: {
  your_target: {
    options: {
      url: 'https://app.raygun.io/upload/jssymbols/MyAppId?authToken=MyExternalAuthToken',
      method: 'POST',
      data: {
        url: 'http://example.com/myjs.min.js'
      }
    },
    src: 'C:\website\js\myjs.min.js',
    dest: 'file'
  }
}

API endpoint

If you have a lot of source maps and JavaScript files to upload for each deployment of your site/application, then another option available to you is the bulk upload endpoint. This lets you upload a zip file containing many source maps and/or JavaScript files. All the details you need to use this endpoint are described below.

https://app.raygun.io/upload/jssymbols/bulk/<RAYGUN_APPLICATION_IDENTIFIER>

The Raygun application identifier is that of the application within Raygun that you’re uploading to. This can be found in the URL when looking at your application in Raygun.

Required parameters

  • “file” must be a zip file containing the source map files you are uploading.
  • “urlmap” is a JSON array of objects that map a source map or JavaScript file to a url. Each object must have a “filename” field that is the name of a file in the zip, and a “url” field that the file is to be mapped to.

Authentication

  • External access token: You can create an external access token in Raygun from your user settings page. This can be used by adding an “authToken” query parameter to the endpoint URL.
  • Basic authentication: Use your Raygun credentials to make a basic authentication token, and use this in an “Authorization” header.

Here is an example of using this endpoint with cURL and an external access token

curl
  -X POST
  -F 'urlmap=[{"filename":"<SOURCE_MAP_FILE_NAME>", "url":"<SOURCE_MAP_URL>"},{"filename":"<JAVA_SCRIPT_FILE_NAME>", "url":"<JAVA_SCRIPT_URL>"}]'
  -F 'file=@<PATH_TO_ZIP_FILE>'
  https://app.raygun.io/upload/jssymbols/bulk/<RAYGUN_APPLICATION_IDENTIFIER>?authToken=<EXTERNAL_ACCESS_TOKEN>
<SOURCE_MAP_FILE_NAME> The file name of a source map within the zip file. This is expecting just the file name, not a path.
<SOURCE_MAP_URL> The URL you want to map the source map to.
<JAVA_SCRIPT_FILE_NAME>strong> The file name of a JavaScript file within the zip file. This is expecting just the file name, not a path.
<JAVA_SCRIPT_URL> The URL you want to map the JavaScript file to.
<PATH_TO_ZIP_FILE> An absolute or relative path to the zip file to upload.
<RAYGUN_APPLICATION_IDENTIFIER> The identifier of the application in Raygun that you're uploading to. This can be found in the URL when looking at your application in Raygun.
<EXTERNAL_ACCESS_TOKEN> This can be obtained from Raygun by clicking your name in the top right corner, select "My settings" and then hit "Generate external access token" or copy it if you already have one.

Validating Source Maps

Ensure that your source maps are compatible with Raygun by using our validation tool. We have created a handy tool that allows you to validate your source map files for use with Raygun.


Limitations

If any of the following are not met, then the entire upload will fail and return an error.

  • The zip file must not exceed 65 MB when compressed.
  • The contents of the zip file must not exceed 128 MB when uncompressed.
  • The zip file must contain no more than 1000 files.
  • The “file” parameter must be present and the file must be a zip.
  • The “urlmap” parameter must be present and have the correct JSON structure described above.
  • A source map file must have the file extension of “.map”, ignoring case.
  • A JavaScript file must have the file extension of “.js”, ignoring case.
  • The file name of a source map or JavaScript file needs to be included in the “urlmap” parameter along with the URL to map it to.
  • A source map or JavaScript file must not exceed 65 MB uncompressed.

Mobile Apps

With many mobile applications being written in JavaScript within containers such as Apache Cordova, using Raygun4js is an appealing way to track errors across all platforms. However as the application is hosted on the phone instead of a website with a static URL you may find that error stacktraces contain files paths which differ across different platforms and devices. This introduces some difficulty when trying to utilize source maps which require the minifed and mapping files to be uploaded and labeled with exactly the same URL’s as would appear in a stacktrace.

For this reason we recommend adding a handler to the raygun4JS provider to rewrite file’s URLs before the error is reported back to Raygun. This means that errors will be reported from the same domains and not unique domains for every platform or device.

An example of such a handler is shown below:

//handler method
var beforeSend = function(payload) {
  var stacktrace = payload.Details.Error.StackTrace;

  var normalizeFilename = function(filename) {
      var indexOfJsRoot = filename.indexOf("js");
      return 'http://normalizedurl.com/' + filename.substring(indexOfJsRoot);
  }

  for(var i = 0 ; i < stacktrace.length; i++) {
      var stackline = stacktrace[i];
      stackline.FileName = normalizeFilename(stackline.FileName);
  }
  return payload;
}

//attaching the handler to the Raygun provider
Raygun.onBeforeSend(beforeSend);

This handler removes the platform and device specific paths from the URL’s present in an error’s stacktrace.

For instance an error occuring on an Android phone within the file

file://android_asset/www/js/myjsfile.min.js

and on an iOS device within the file:

file://accounts/1234/appdata/b12b33f1-519b-4d1c-8d68-315513ecbac1/www/js/myjsfile.min.js

will both report:

http://normalizedurl.com/js/myjsfile.min.js

as the URL of the file within which the error occured.

This allows the use of a single set of source maps for source mapping across all deployments.


Notes

There could be up to a 30 minute delay before the mapping process starts utilizing any uploaded files. To reprocess any errors which occur during this time simply hit the "Re-process this error for Source Maps" button on an error instance.

Code snippet insertion is only available if there are less than 50 source files referenced in the map file.