Michal Zalecki
Michal Zalecki
software development, testing, JavaScript,
Ethereum, and other stuff developers do

Upload source maps to Rollbar

In production, the most important advantage of using a build tool like webpack is a smaller size and thus improved overall application performance. Although beneficial for our users, the bundled and minified code is harder to debug for developers as the code they have written doesn't map 1:1 to error stack traces. Multiple tools for minifying, bundling and transpiring JavaScript modules can generate source maps along with the resulting code. Source maps allow developer tools (Chrome DevTools, test runners, error reporting software) to find a reverse mapping from the optimized code back to the source code.

Rollbar (and often other error tracking software) supports source maps to display error stack traces which point to the erroneous lines in the source code instead of an optimized client-size bundle your users download.

The easiest, although not recommended, way to provide Rollbar with source maps is to make them available for download from your production server. Once Rollbar receives the error, it will schedule the download of the source map file.

A reliable and recommended way to make source maps available for Rollbar is to upload them pre-deploy. It's also the only way if you don't wish to share the source code of your app by exposing it via publicly-available source maps. In this article, we will create a required configuration and write a script for uploading source maps to Rollbar.

Set a code version

I don't want to repeat what's already available in the Rollbar documentation so for starters go and read Enabling Source Map Translation in Rollbar section.

The first obstacle is a requirement for providing a code version string for both Rollbar configuration and for the script that's going to upload source maps. Typically, you can use a unique abbreviation of a git hash, and in general, I would recommend doing so. Using git hashes make sense as no additional effort is required to increment the version (changes with every new commit) and it's easy to find the link between the release and error.

git rev-parse --short HEAD

A typical scenario in which git hash won't work is building your application within a Docker container and ignoring .git directory at the same time. In such a situation you can try to provide the git hash as a --build-arg to your docker build. If for some reason this is still not an option then you can consider creating a plain text file named VERSION within your repository and keep maintaining it by making sure that each pull request to the master or deploy branch increments the VERSION. This approach doesn't scale very well with the team size, so I encourage you to jump a few additional hoops and keep relying on git hashes.

To read a git hash with Node.js we can use git command using the execSync function from the child_process module. The output should be then trimmed to remove the newline character.

const { execSync } = require("child_process");

function codeVersion() {
  return execSync("git rev-parse --short HEAD").toString().trim();
}

Then we want to configure webpack, so CODE_VERSION environment variable is available during the build time.

new webpack.DefinePlugin({
  'process.env.CODE_VERSION': JSON.stringify(codeVersion()),
});

A shared CODE_VERSION can be used in the code that initializes the Rollbar instance.

import Rollbar from "rollbar";

export const rollbar = new Rollbar({
  accessToken: process.env.ROLLBAR_ACCESS_TOKEN,
  captureUncaught: true,
  captureUnhandledRejections: true,
  enabled: process.env.NODE_ENV !== "development",
  payload: {
    environment: process.env.NODE_ENV,
    client: {
      javascript: {
        source_map_enabled: true,
        code_version: process.env.CODE_VERSION,
        guess_uncaught_frames: true,
      },
    },
  },
});

Generate source maps

There are many ways in which webpack allows you to generate source maps. In the case of the production build, you should consider two options: source-map and hidden-source-map. The difference is that hidden-source-map doesn't include a reference comment with a source map path which may look like that:

//# sourceMappingURL=app.10b68db1f21770fbad54.js.map

You need a source map path only when you expect Rollbar to fetch source maps for you. For our use case in which we upload source maps pre-deploy, I'm going to use hidden-source-map as the reference path comment doesn't matter and I can avoid a Chrome DevTools warning about unreachable source maps.

// somewhere in your production webpack config
{
  devtool: "hidden-source-map"
}

I still have to make sure not to upload source maps to the web server or remove them after the build completes on the web server/within a Docker container.

Upload source maps

Source maps can be uploaded to Rollbar using Rollbar API. I've created a simple shell script you can use in Linux and macOS environments. Follow the comments and adjust the code to match your build config (domain name, output directory, and source map path if necessary).

#!/bin/sh

# Save a short git hash, must be run from a git
# repository (or a child directory)
version=$(git rev-parse --short HEAD)

# Use the post_server_time access token, you can
# find one in your project access token settings
post_server_item=$ROLLBAR_POST_SERVER_ITEM

echo "Uploading source maps for version $version!"

# We upload a source map for each resulting JavaScript
# file; the path depends on your build config
for path in $(find dist -name "*.js"); do
  # URL of the JavaScript file on the web server
  url=https://example.com/${path}

  # a path to a corresponding source map file
  source_map="@$path.map"

  echo "Uploading source map for $url"

  curl --silent --show-error https://api.rollbar.com/api/1/sourcemap \
    -F access_token=$post_server_item \
    -F version=$version \
    -F minified_url=$url \
    -F source_map=$source_map \
    > /dev/null
done

Upload with Docker

Most of the time, I will upload source maps along building the Docker image. It might be a part of a CI/CD process (still in Docker) or during the deployment. As far as I'm aware, there is no harm done when uploading the same source map twice to Rollbar and making source map upload a part of building the image enforces that once the deployment completes, source maps are already in place.

FROM node:10.16.0-alpine
RUN apk add --no-cache curl git

EXPOSE 3000
WORKDIR /home/app

ARG NODE_ENV
ARG ROLLBAR_POST_SERVER_ITEM

ADD ./package.json /home/app/
ADD ./package-lock.json /home/app/
RUN npm ci --production
ADD . /home/app
RUN npm run build
RUN ./upload_sourcemaps.sh
RUN rm -rf .git
RUN rm build/*.map
CMD npm run start

This is an example of a Dockerfile that you might use to deploy the source maps. Initially the .git directory is copied into the image but we don't need it after the upload is done. We also don't need source map files any more so they can be removed as well.

Photo by João Silas on Unsplash.