How to make React Create App Production Error Boundary map to source code
I found a solution to this using the library https://www.stacktracejs.com/.
The method StackTrace.report() method will fetch the map and get you the unminified info you need!
So now my React Boundary looks like this. I still use window.onerror to make sure I catch everything.
First, make sure to add stacktrace-gps
and stacktrace-js
to your package.json
import React, { Component } from "react";
import StackTrace from "stacktrace-js";
window.onerror = function(msg, file, line, col, error) {
StackTrace.fromError(error).then(err => {
StackTrace.report(
err,
`//${window.location.hostname}:${process.env.REACT_APP_LOGGER_PORT || 3334}/jsnlog.logger`,
{
type: "window.onerror",
url: window.location.href,
userId: window.userId,
agent: window.navigator.userAgent,
date: new Date(),
msg: msg
}
);
});
};
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { error: null };
}
componentDidCatch(error, errorInfo) {
this.setState({ error });
StackTrace.fromError(error).then(err => {
StackTrace.report(
err,
`//${window.location.hostname}:${process.env.REACT_APP_LOGGER_PORT || 3334}/jsnlog.logger`,
{
type: "React boundary",
url: window.location.href,
userId: window.userId,
agent: window.navigator.userAgent,
date: new Date(),
msg: error.toString()
}
);
});
}
render() {
if (this.state.error) {
//render fallback UI
return (
<div className="snap text-center">
<p>We're sorry — something's gone wrong.</p>
<p>Our team has been notified</p>
</div>
);
} else {
//when there's not an error, render children untouched
return this.props.children;
}
}
}
export default ErrorBoundary;
First, it is important to create source map. I did this by adding the devtools in webpack configuration for creating source map. Brief snippet of it is as follows:
devtools: "source-map",
new UglifyJsPlugin({
sourceMap: true
})
Once source maps were created, I used the library https://www.stacktracejs.com/.
However, to reduce the bundle size on production, I didn't import the whole bundle of stacktrace. I implemented by seperating client side code and server side.
Client Side: I imported error-stack-parser. This creates an object, which contains filename, line number, column number and function name. I send the object created using this to server.
import ErrorStackParser from "error-stack-parser";
componentDidCatch(error) {
let params = {stackframes: ErrorStackParser.parse(error)};
let url = 'https://example.com';
axios.post(url, params)
}
On the server side, I imported "stacktrace-gps" and "stackframe" and used it to find it, to get the line number and column of the actual code from the source map.
const StackTraceGPS = require("stacktrace-gps");
const request = require("request");
var logger = function(req, res) {
let stackframes = req.body.stackframes;
let stackframe = new StackFrame(
stackframes[0]
); /* Getting stack of the topmost element as it contains the most important information */
/* We send extra ajax function to fetch source maps from url */
const gps = new StackTraceGPS({
ajax: url => {
return new Promise((resolve, reject) => {
request(
{
url,
method: "get"
},
(error, response) => {
if (error) {
reject(error);
} else {
resolve(response.body);
}
}
);
});
}
});
gps.pinpoint(stackframe).then(
info => {
console.log(info); /* Actual file Info*/
},
err => {
console.log(err);
}
);
};
This reduces the bundle size, and gives you the ability to log error on server side.