Sentry: [Suggestion] Javascript source map support for browser extensions

Created on 17 May 2016  Â·  11Comments  Â·  Source: getsentry/sentry

I have read the source map docs at https://docs.getsentry.com/hosted/clients/javascript/sourcemaps/
For a source map to work, one have to provide the full url of the source map file including the domain

Sentry will resolve sourceMappingURL relative to https://example.com/dist/js/ (the root path from which app.min.js was served). You will again need to name your source map with the full URL: https://example.com/dist/js/app.min.js.map.

Unfortunately, within browser extension, url of a asset looks like:

chrome-extension://negengibeecgiokdifncekejdgkcoono/background.js

The negengibeecgiokdifncekejdgkcoono part is randomly genreated on install, thus no way to provide full url for a map file. Also extension content script can run on any website, in those case, source map url's domain will change based on current website, which is completely irrelevant.

Is there anyway to workaround this now or provide more flexible matching of source map in Sentry? Thanks!

All 11 comments

@d6u – you can use setDataCallback to inspect the outbound stack trace and strip the generated ID from each URL / normalize it to work with your source maps. There's an example of this in the React Native plugin.

Note that the filename doesn't strictly have to be a valid URL ... it can still be chrome-extension:///background.js.

Basically, this should be possible today, but you will have to do some munging of the data first.

@benvinegar thanks! I have tried your approach, but sourcemaps still won't show up

image

Upload commands:

curl https://app.getsentry.com/api/0/projects/cmedia/pn-chrome-extension/releases/ \
    -u xxxxxxxxxx \
    -X POST \
    -d '{"version": "2.0.1"}' \
    -H 'Content-Type: application/json'

curl https://app.getsentry.com/api/0/projects/cmedia/pn-chrome-extension/releases/2.0.1/files/ \
  -u xxxxxxxxxx \
  -X POST \
  -F file=@target/background.js.map \
  -F name="chrome-extension:///background.js.map"

The correct stack trace should looks something like

image

My data mutation callback is:

    dataCallback: function (data) {
      if (data.culprit) {
        data.culprit = normalizeUrl(data.culprit);
      }

      if (data.exception) {
        // if data.exception exists,
        // all of the other keys are guaranteed to exist
        data.exception.values[0].stacktrace.frames.forEach(function (frame) {
          frame.filename = normalizeUrl(frame.filename);
        });
      }
    },

/**
 * Remove random string generated by Chrome in error stacktrace
 *
 * @param {string} url The url on exception data
 *
 * @return {string} Url where domain portion is removed
 */
function normalizeUrl(url) {
  return url.replace(/chrome-extension:\/\/\w+?\//, 'chrome-extension:///');
}

@d6u just to confirm, you're binding it with a release, correct? it definitely should work as long as:

  1. the minified source has the sourcemap indicator
  2. the release identifier is passed in the SDK
  3. the minified source is uploaded with the exact name shown as the "absPath" in the json payload for a frame
  4. the sourcemap is uploaded and either contains the source or the source itself is also uploaded

@dcramer yes, I was binding it to a release. Here are the steps I followed:

  1. Create a release 2.0.2 and upload sourcemap for background.js

``` sh
curl https://app.getsentry.com/api/0/projects/cmedia/pn-chrome-extension/releases/
-u 9e5c145978a1408291c69066b77a7b09
-X POST
-d '{"version": "2.0.2"}'
-H 'Content-Type: application/json'

curl https://app.getsentry.com/api/0/projects/cmedia/pn-chrome-extension/releases/2.0.2/files/
-u 9e5c145978a1408291c69066b77a7b09
-X POST
-F file=@target/background.js.map
-F name="chrome-extension:///background.js.map"
```

  1. background.js has a sourcemap comment at bottom

js //# sourceMappingURL=background.js.map

  1. Because background.js run as Chrome extension, so I have to mutate the urls in stacktrace:

``` js
dataCallback: function (data) {
if (data.culprit) {
data.culprit = normalizeUrl(data.culprit);
}

 if (data.exception) {
   // if data.exception exists,
   // all of the other keys are guaranteed to exist
   data.exception.values[0].stacktrace.frames.forEach(function (frame) {
     frame.filename = normalizeUrl(frame.filename);
   });
 }

}

...

function normalizeUrl(url) {
return url.replace(/chrome-extension://w+?//, 'chrome-extension:///');
}
```

Here is the raw JSON data in Sentry:

json { "id": "845a2b261dd24c9fb93c040d314cea80", "project": 78730, "release": "2.0.2", "platform": "javascript", "culprit": "chrome-extension:///background.js in wrapped", "message": "Error: test sourcemap", "datetime": "2016-05-20T18:22:53.000000Z", "time_spent": null, "tags": [ [ "level", "error" ], [ "logger", "javascript" ], [ "sentry:release", "2.0.2" ], [ "browser", "Chrome 50.0" ], [ "device", "Other" ], [ "os", "Mac OS X 10.11.4" ], [ "url", "chrome-extension://negengibeecgiokdifncekejdgkcoono/_generated_background_page.html" ], [ "sentry:user", "ip:50.233.46.94" ] ], "errors": [ { "url": "chrome-extension:///background.js", "type": "js_no_source" } ], "extra": { "session:duration": 1013 }, "fingerprint": [ "{{ default }}" ], "received": 1463768573.0, "sdk": { "version": "3.0.4", "name": "raven-js" }, "sentry.interfaces.Exception": { "exc_omitted": null, "values": [ { "stacktrace": { "has_system_frames": false, "frames": [ { "function": "wrapped", "abs_path": "chrome-extension:///background.js", "in_app": false, "lineno": 474, "colno": 30, "filename": "chrome-extension:///background.js" } ], "frames_omitted": null }, "type": "Error", "value": "test sourcemap", "module": null } ] }, "sentry.interfaces.Http": { "url": "chrome-extension://negengibeecgiokdifncekejdgkcoono/_generated_background_page.html", "headers": [ [ "User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36" ] ] }, "sentry.interfaces.User": { "ip_address": "50.233.46.94" }, "type": "error", "version": "7" }

  1. Stacktrace is not available in Sentry

image

At the same time in Chrome, it has correct stacktrace

image

@d6u – if you are a hosted Sentry customer, could you email [email protected]? We could take a look at the issue directly.

I would love whatever the answer to this was to be posted in here... my chrome extension's sourcemaps are also broken.

Source maps should work, regardless of platform, because they are simple artifact lookups. If you have a frame with URL chrome-extension:///background.js, you need to upload that artifact with the exact same artifact name: chrome-extension:///background.js (and probably also chrome-extension:///background.js.map). Sentry will find it and apply it – it doesn't matter what environment it came from (browser, React Native, browser extension, etc).

and probably also chrome-extension:///background.js.map

Correction to anyone reading this thread: there is currently a bug in Sentry where chrome-extension:// prefixed URLs are not resolving correctly when determining source map location.

See #5459

For anyone struggling: you need to add --url-prefix when uploading sourcemaps, like this:

`sentry-cli releases -o ${org} -p ${project} files ${version} upload-sourcemaps ${distPath} --url-prefix "chrome-extension://ckcfjkiackeclohodgajcpenmphxjkkb/"`

(insert your extn id)

For cases where --url-prefix is not known before hand, using an event processor is the modern equivalent of setDataCallback. Updated version of @d6u's callback is thus:

const normalizeUrl = (url) => {
  return url.replace(/(webpack_require__@)?(moz|chrome)-extension:\/\/[^\/]+\//, '~/');
}

Sentry.configureScope(scope => {
  scope.addEventProcessor(async (event: any) => {

    if (event.culprit) {
      event.culprit = normalizeUrl(event.culprit);
    }

    if (event.exception) {
      event.exception.values[0].stacktrace.frames = event.exception.values[0].stacktrace.frames.map((frame) => {
        frame.filename = normalizeUrl(frame.filename);
        return frame;
      });
    }

    return event;
  });
});

Was this page helpful?
0 / 5 - 0 ratings