How can I take a minified javascript stack trace and run it against a source map to get the proper error?
Asked Answered
C

6

72

On our production server, I have minified javascript published and I'm not including a map file with it, because I don't want the user to be able to understand what's happening based on the error.

I have a logging service I've written to forward the angular exceptions (caught by $exceptionHandler) to myself via email. However, this stack trace is near unreadable:

n is not defined
    at o (http://localhost:9000/build/app.min.js:1:3284)
    at new NameController (http://localhost:9000/build/app.min.js:1:3412)
    at e (http://localhost:9000/build/bower.min.js:44:193)
    at Object.g.instantiate (http://localhost:9000/build/bower.min.js:44:310)
    at b.$get (http://localhost:9000/build/bower.min.js:85:313)
    at d.compile (http://localhost:9000/build/bower.min.js:321:23333)
    at aa (http://localhost:9000/build/bower.min.js:78:90)
    at K (http://localhost:9000/build/bower.min.js:67:39)
    at g (http://localhost:9000/build/bower.min.js:59:410)
    at http://localhost:9000/build/bower.min.js:58:480 <ui-view class="ng-scope">

What I'm wondering is: Is there a program where I can analyze this stack trace against the actual non-minified source code via map file (or not via map file if there's another way)

Crannog answered 14/10, 2015 at 14:59 Comment(4)
I can find some proper tools for this problem: - stacktracejs.com - sourcemaps.info (on GitHub: github.com/bugsnag/sourcemaps.info) This can convert the minified stack trace to developement stack trace.Cerell
sourcemaps.info does exactly what the OP asked, and what I needed. Thanks! A tip because I was thrown at first: you do not need public-facing source maps. After pasting in your stack trace if it cannot find them, it gives you a box to paste in the sourcemap manually. Not a great UX, and it constantly consumes CPU, but functionality is spot on :).Yee
did you find a solution for this problem?Torbernite
Sorry friend, I'm not even at the same company anymore, nor do I use angularjs anymoreCrannog
R
38

I figured there was no super simple tool for converting a minified stack trace into a readable one using a source map (without having to use a web service), so I created a tool for it:

https://github.com/mifi/stacktracify

Install and use it as follows:

npm install -g stacktracify

Now copy a minified stacktrace to your clipboard - then run:

stacktracify /path/to/js.map
Remittent answered 28/1, 2020 at 12:50 Comment(1)
Perfect - just what I was looking for. Nice work!Fallen
S
34

What you want to do is parse the source maps. This has nothing to do with web browsers. All you need to do is translate the minified reference into the unminified resource.

If you have any experience with NodeJS there is already a package that does this for you.

https://github.com/mozilla/source-map/

To install the library

npm install -g source-map

or

yarn global add source-map

Create a file named "issue.js"

fs = require('fs');
var sourceMap = require('source-map');
var smc = new sourceMap.SourceMapConsumer(fs.readFileSync("./app.min.js.map","utf8"));
console.log(smc.originalPositionFor({line: 1, column: 3284}));

Run the file with node

node issue.js

It should output the location in the original file to the console for first line from the stack trace.

Note: I tell you install source-map globally for ease of use, but you could create a node project that does what you need and installs it locally.

Saltwort answered 14/10, 2015 at 15:43 Comment(7)
That's a great idea. I'm hoping there's something I can use to analyze an entire stack trace, but I suppose I could write something to read the stack trace line by line to do the same idea. Thanks for the suggestion!Crannog
Google nodejs read file one line at a time, and then regex match between (..) and you've got your stack trace from a text file.Saltwort
Yeah, that's exactly what I'm planning to do.Crannog
this only works till version 0.5.5 of source-map. after that version SourceMapConsumer returns a promise. It was not possible for me, to create a running version with the latest version of source-map (0.7.3)Ganister
// This seems to work: fs = require('fs'); var sourceMap = require('source-map'); const consumer = new sourceMap.SourceMapConsumer(fs.readFileSync("./main.xxxxx.js.map","utf8")); consumer.then(c => { console.log(c.originalPositionFor({line: 1, column: 340508})); })Smack
Why do you say a source map has nothing to do with web browsers? They were invented specifically for browser web tools to show readable code for web developers to trace in the browser.Soddy
Why specify global install for npm? It seems wiser by default to recommend non-global install so things outside of the project for the dev aren't affected. If the dev finds it annoying to have to do it for all of their projects then they can look up how to make a single install for all.Soddy
H
5

Adding to @Reactgular's answer, the below snippet will work with the latest version of source-map

  const rawSourceMap = fs.readFileSync("./app.min.js.map","utf8");

  const whatever = sourceMap.SourceMapConsumer.with(rawSourceMap, null, consumer => {
    console.log(consumer.originalPositionFor({
      line: 1,
      column: 3284
    }));
  });

And to add to the discussion on the thread a simple regex like /\/(\w*[-\.]?\w*).js:\d*:\d*/g

Below is a very simple regex to find all line numbers in a stacktrace.

  //regex for patterns like utils.js, utils123.js, utils-name.js, utils.version.js
  var patt = /\/(\w*[-\.]?\w*).js:\d*:\d*/g; 
  // returns matches like ['/app.min.js:1:3284', '/bower.min.js:44:193', ..]
  var errorPositions = line.match(patt);
  console.log(errorPositions);
  if(!errorPositions || errorPositions.length === 0) {
      console.log("No error line numbers detected in the file. Ensure your stack trace file is proper");
      return;
  }
  errorPositions.forEach(function(error) {
    findInSourceMap(error);
  });
});
Hoekstra answered 22/8, 2019 at 10:14 Comment(1)
Thanks for updating the example. Ran greatHotien
H
1

If you had access to the source map file externally and could get the same file structure you could work it out I guess, but I'm not aware of any tools outside the browser that will help you with that.

The added advantage of having the data in a running browser will allow checking of locals which you won't get even with a source map.

You might want to consider a tool such as rollbar to do error reporting. This will report all the locals in each frame to help debugging. It has support for sourcemaps outside the browser to address your security concerns.

Havoc answered 14/10, 2015 at 15:32 Comment(3)
Thanks for the suggestion! That would be pretty useful, but our company would probably require the enterprise license from rollbar, and I doubt I could convince my boss to spend $1000 a month on that. It's definitely a solution thoughCrannog
That's definitely a lot to pay just to check out where this error is occurring!Havoc
Yep, exactly. And this specific error is one I intentionally caused to prove a point. Our app is months from production, but I'm trying to nail down a good errorHandling solution early in the process.Crannog
B
0

I created a small web app exactly for this - https://sourcemap.tools/.

You can just put there your stack trace, provide your source maps and the app shows use "original" stack trace.

Everything happens on the client. It's open source.

Blackball answered 25/12, 2023 at 15:59 Comment(0)
L
-3

Append comment directive for the JS running in the page.

//# sourceMappingURL=/path/to/your/sourcemap.map

In firefox (not sure about chrome) to tell the Debugger to use source maps if they are available, click the "Debugger settings" button and select "Show original sources" from the list of settings that pops up:

Lazarus answered 14/10, 2015 at 15:24 Comment(1)
That's fine if I want the source maps to work when executing the application, but I don't want the user to have the source map. I just want to be able to analyze the stack traceCrannog

© 2022 - 2024 — McMap. All rights reserved.