After reading this article, you will GET the skills:

● Collect front-end errors (native, React, Vue)

● Write error reporting logic

● Write an error log collection service using egg.js

● Write webpack plug-in to automatically upload sourcemap

● Use Sourcemap to restore the source location of compressed code

● Use Jest for unit testing

Is there a feeling of heart? Hurry up and learn!

How to catch exceptions

JS exception:

Js exceptions do not cause the JS engine to crash, but only terminate the currently executing task.

For example, a page has two buttons. If clicking the buttons causes an exception, the page will not crash.

Only this button function is disabled, other buttons will still be effective ☟

In the example above we started two separate tasks with setTimeout.

Although the first task performed a wrong method. The execution of the program stopped. But the other task was not affected.

In fact, if you don’t open the console you won’t even see the error. It’s as if the mistake happened in silence.

Let’s look at how such errors can be collected.

Try-catch:

JS as a high-level language we first thought of try-catch collection.

If the error is not caught in a function, the error is thrown.

The console prints error messages and error stacks, respectively.

Now, you might be thinking, well, why don’t you just make a try-catch error at the bottom.

Indeed, as a programmer coming from Java, I feel the same way.

But the ideal is full, the reality is very skinny. Let’s look at the next example.

Notice that the exception is not caught.

And that’s because JS has very limited try-catch capabilities and it doesn’t work very well with asynchrony.

You can’t add a try-catch to all asynchrony to collect errors.

In fact, if you think about it, asynchronous tasks are not actually called by a higher level of code like setTimeout in this case.

If you think about eventloop, these asynchronous functions are the equivalent of a bunch of motherless children who make a mistake and can’t find the parents.

Of course, I have also thought about some dark arts to deal with this problem such as proxy execution or asynchronous methods used.

You know what? Let’s just take a look.

Exception Task capture

window.onerror:

The greatest benefit of window. onError is that both synchronous and asynchronous tasks can be caught.

Onerror return value

One other problem with onError is that if you return true you don’t get thrown up.

Otherwise you’ll see the error log in the console.

Listen for error events:

Location in the file ☟

window.addEventListener('error',() => {})

Well, onError is great but there’s still a class of exceptions that you can’t catch. This is a network exception error.

Take the following example.

<img src="./xxxxx.png">
Copy the code

Imagine if the image we wanted to display on the page suddenly stopped showing and we didn’t even know it was a problem.

AddEventListener is ☟

The result is as follows: ☟

Promise exception catch:

Promise came along primarily to allow us to address the issue of callback geography. It’s basically standard for our program development.

Although we advocate writing using es7 async/await syntax.

But a lot of the ancestral code still exists in the Promise format.

new Promise((resolve, reject) => {
  abcxxx()
});

Copy the code

Neither onError nor listening error events can be caught in this case.

Unless each Promise adds a catch method.

But obviously, we can’t do that.

window.addEventListener("unhandledrejection", e => {
 console.log('unhandledrejection',e)
});

Copy the code

We can consider throwing errors caught by the UnHandledrejection event to be handled by the error event.

Async /await exception catch:

In fact the async/await syntax is essentially a Promise syntax.

The difference is that async methods can be caught by a superimposed try/catch.

If not, it will be captured with an unhandledrejection event, just like a Promise.

In this case, we just need to add unHandlerejection globally.

Summary:

In fact, we can throw the exception thrown by the unhandledrejection event again and we can handle it with the error event.

The final code is as follows:

Front-end engineering

Webpack engineering:

Now is the era of front-end engineering, engineering exported code is generally compressed and confused.

Such as:

setTimeout((a)= > {
    xxx(1223)},1000)

Copy the code

The offending code points to a compressed JS file that looks like this.

If you want to associate errors with the original code, you need the Sourcemap file to help.

What is sourceMap?

Simply put, sourceMap is a file that stores location information.

And, to be more careful, what this file contains is the position of the code after the transformation, and the corresponding position before the transformation.

How to use sourceMap to restore the location of the exception code will be covered in exception analysis.

VUE engineering

Create a project directly using vue-CLI tools.

We’re going to turn off ESLint temporarily for testing purposes and it’s recommended that you turn esLint on at all times.

Configure it in vue.config.js

We deliberately placed the (file location ☟)

src/components/HelloWorld.vue

At this point the error is printed in the console, but the error event is not listened for.

ErrorHandle handle:

To uniformly report exceptions on the Vue, use the errorHandle provided by the Vue.

This method is called whenever a Vue exception occurs.

We are in the SRC/main. Js

The React engineering:

npx create-react-app react-sample

cd react-sample

yarn start

Copy the code

We make an error with useEffect hooks:

Add error event listening logic to SRC /index.js:

window.addEventListener('error', args => {
    console.log('error', error)
})

Copy the code

But from the run result, although the output error log is still captured by the service.

Error Boundary components

Error bounds can only catch errors of its children.

Error boundaries cannot catch errors of their own.

If an error boundary cannot render the error message, the error bubbles up to the nearest error boundary.

This is also similar to how catch {} works in JavaScript.

Create the ErrorBoundary component

Wrap the App tag ☟ in SRC /index.js

Final run result:

How to select a communication mode for reporting exceptions

Create img tag dynamically:

The purpose of reporting is to send the captured exception information to the back end. The most common method is to create a label dynamically.

There is no need to load any communication libraries and the page does not need to be refreshed.

Basically including Baidu statistics Google statistics are based on this principle to do the buried point.

new Image().src = 'http://localhost:7001/monitor/error'+ '? info=xxxxxx'

Copy the code

By dynamically creating an IMG, the browser sends a GET request to the server.

You can report errors to the server by placing the error data you need to report in a QueryString string.

Ajax report:

We can actually use Ajax to report errors, just like we would in a business application.

## What data are reported:

## What data are reported:

Let’s first look at the error event parameters:

One of the core should be the error stack, in fact, we locate the error is the most important error stack.

The error stack contains most debugging information. It includes the exception position (row number, column number), exception information

## serialization of reported data:

Since the communication can only be transmitted as strings, we need to serialize the object.

There are basically three steps:

1. Deconstruct the exception data from the attributes and store it in a JSON object

2. Convert the JSON object to a string

3. Convert the string to Base64

And of course you have to do the opposite on the back end and we’ll talk about that later.

Back-end server where the exception is reported

Build egg.js project:

The abnormal data must be received by a back-end service.

Take eggJS, a popular open source framework, as an example

Install the egg globally -cli
npm i egg-init -g 

Create a back-end project
egg-init backend --type=simple

cd backend
npm i

# Start a project
npm run dev

Copy the code

Write error upload interface:

Start by adding a new route to app/router.js

Create a new one:

controller (app/controller/monitor)

Take a look at the result after receiving ☟

To log:

The next step is to log the error. The implementation of the method can be written in FS, or with the help of log4JS such a mature log library.

Eggjs allows you to customize the log so use this functionality to customize a front-end error log.

In the/config/config. Default. Add a custom js logging configuration

In the/app/controller/monitor. Js logging is added:

The final effect achieved:

The Webpack plug-in implements SourceMap uploads

When it comes to exception analysis, the most important work is actually to restore the webPack obfuscated compression code.

To create a Webpack plug-in:

/source-map/plugin (file location)

Loading the Webpack plugin:

Webpack.config.js (file location)

Add sourceMap read logic:

Add logic to read sourcemap files in apply

/plugin/uploadSourceMapWebPlugin.js

HTTP upload function:

Add an upload interface on the server:

/backend/app/router.js (file location)

Add sourcemap upload interface:

/backend/app/controller/monitor.js

End result:

The call plug-in Sourcemap is uploaded to the server when performing webPack packaging.

Parsing ErrorStack

Considering that this feature requires a lot of logic, we are going to develop it as a separate function and use Jest for unit testing:

Let’s take a look at our requirements ☟

Build Jest framework:

First create a /utils/ stackParser.js file ☟

Create the test file stackParser.spec.js in the sibling directory

The above requirements are expressed by Jest

Sorted as follows:

Now let’s run Jest

npx jest stackparser --watch
Copy the code

The display fails simply because we haven’t implemented it yet.

So let’s implement this method

Antisequence Error objects:

Start by creating a new Error object to set the Error stack to Error.

The error-stack-parser NPM library is then used to convert stackFrame

The result is as follows: ☟

Parsing ErrorStack:

Next we convert the code location in the error stack to the source location

Let’s test again with Jest ☟

Now let’s look at the results:

Then the test passes

Log the source location:

After recording, let’s take a look at the effect:

With this step done, our ErrorStack work is done.

Two open source frameworks to work with

Fundebug:

Fundebug focuses on real-time BUG monitoring for JavaScript, wechat applets, wechat games, Alipay applets, React Native, Node.js and Java online applications.

Since its official launch on November 11, 2016, Fundebug has handled over 1 billion error events in total, and paid customers include Sunshine Insurance, Lychee FM, Zhangmen 1-on-1, Walnut Programming, Weomai and many other brand enterprises.

Sentry:

Sentry is an open source real-time error tracking system that helps developers monitor and fix exceptions in real time.

It focuses on continuous integration, improving efficiency, and improving the user experience.

Sentry is divided into server and client SDK. The former can directly use the online services provided by its home, or can be built locally.

The latter provides support for many major languages and frameworks, including React, Angular, Node, Django, RoR, PHP, Laravel, Android,.NET, JAVA, and more.

It also offers solutions to integrate with other popular services, such as GitHub, GitLab, Bitbuck, Heroku, Slack, Trello, and more.

The company’s current projects are also phasing in Sentry for error log management.

Conclusion:

So far, we’ve taken the basic functionality of front-end exception monitoring as an MVP(minimum viable Product).

There is much more to be done, and ELK can be used for analysis and visualization of error logs.

Docker can be used for publishing and deploying. Add permission control to upload and report EggJS.

This work is reproduced (read the original text)