Blog address

“Use modular tools package their own JS library” article mentioned, at that time need to write an SDK, monitoring small program background interface call and page error, today on the realization of principle!

The principle of

I have done SDK data burying point reporting on the web end of the browser before. In fact, the principle is basically the same: the original method is hijacked to obtain the data to be reported, and then the original method is executed, so that no trace burying point can be achieved.

An example: I want to monitor ajax requests for all web pages, and every time I send an Ajax, I need to print the outgoing URL on the console

We usually use wrapped libraries to send Ajax, such as jQuery and Axios. However, these libraries still use the browser’s native XMLHttpRequest object at the bottom. Therefore, we only need to modify the XMLHttpRequest object

Note: Due to the flexibility of JS, it is easy to modify native methods, but this is not encouraged!

// By putting this code in front of all the JS code, we have implemented the ajax interception requirement
window.XMLHttpRequest.prototype.open = (function(originOpen) {
    return function(method, url, async) {
        
        console.log('Ajax sent, url is:', url);

        return originOpen.apply(this.arguments); }; }) (window.XMLHttpRequest.prototype.open);

Copy the code

In this immediate function, we store the native open method with originOpen temporarily, then wrap a function around it to print out the URL, and then run the native method with OriginOpen.apply to implement traceless intercepting.

Monitor applet

Intercept wx. Request

The running environment of the small program does not have window and Document objects, it only exposes a WX global object, send network request is through the Wx. request API, therefore, this time we need to intercept is the Wx. request method

Let’s try changing the Wx.request

wx.request = function() {
    console.log('66666');
}
Copy the code

TypeError: Cannot set property request of #which has only a getter

This property. This is because, wx request, only the get method without set method, we can through the Object. The getOwnPropertyDescriptor validation:

const des = Object.getOwnPropertyDescriptor(wx, 'request');

// des {
// configurable: true,
// enumerable: true,
// get: f(),
// set: undefined
// }
Copy the code

We can modify it in another way:

const originRequest = wx.request;
Object.defineProperty(wx, 'request', {
    configurable: true.enumerable: true.writable: true.value: function() {
        const config = arguments[0) | | {};const url = config.url;
        console.log('Ajax sent, url is:', url);

        return originRequest.apply(this.arguments); }});Copy the code

This time to achieve the interception function!

Monitor the abnormal

The applet registration function App has a global onError method. We can register one of these methods in the applet entry file app.js:

App({
    onError: function(err) {
        console.log('Report error! ');
        wx.request({
            url: 'http://monitor.com/monitor/error'.data: err
        })
    }
})

App({
    // Other logic
})
Copy the code

However, it is important to note that if a subsequent program overrides onError, it will invalidate the previously registered onError.

The solution could be: we monitor the SDK to expose an interface and let the access party itself call our interface in onError.

App({
  onError: function (err) {
    monitor.notifyError(err)
  }
})
Copy the code

Reported data

After collecting the required data, of course, it is necessary to report to the background. How to report it? Of course, wx.request is still used to send requests.

It’s easy to create an endless loop: if you use the wx. Request we wrapped earlier, then the Ajax request for the reported data will be considered a normal Ajax request, which will trigger the reported data, and so on, back and forth, sending the reported data endlessly.

There are various solutions, such as:

Plan 1

Wx. request can be wrapped to determine if the url to be sent is the reporting interface, then it will not be reported.

const originRequest = wx.request;
Object.defineProperty(wx, 'request', {
    configurable: true.enumerable: true.writable: true.value: function() {
        const config = arguments[0) | | {};const url = config.url;
        if (url.indexOf('http://monitor.com') > - 1) {
            // Send the request directly without reporting it
            return originRequest.apply(this.arguments);
        }

        console.log('Ajax data reporting! ');
        wx.request({
            url: 'http://monitor.com/monitor/ajax'.data: config.data
        })

        return originRequest.apply(this.arguments); }});Copy the code

Scheme 2

Before wrapping wx.request, keep a copy of the original Wx. request method. For all reported requests, use the original method instead of the wrapped method.

const myRequest = wx.request;

const wrapRequest = function () {
    const originRequest = wx.request;
    Object.defineProperty(wx, 'request', {
        configurable: true.enumerable: true.writable: true.value: function() {
            const config = arguments[0) | | {};const url = config.url;
       
            console.log('Report the data! ');
            // Use the original request method
            myRequest({
                url: 'http://monitor.com/monitor/ajax'.data: config.data
            })

            return originRequest.apply(this.arguments); }}); } wrapRequest();Copy the code

Other matters

Of course, there are more details in the actual development, such as the authentication of monitoring projects, the code structure of SDK, data collection and aggregation before reporting, etc., which will not be expanded in detail in this paper.