Abstract: How to monitor HTTP request errors?

  • Author: One step at a time
  • Set up front-end monitoring system (4) interface request abnormal monitoring

FundebugReproduced with authorization, copyright belongs to the original author.

Background: There are a lot of monitoring systems on the market, most of which are charged, which is a pain point for small front-end projects. The other main reason is that although the features are universal, they may not be able to meet our own needs, so it might be a good idea for us to be self-sufficient.

This is the fourth chapter of building a front-end monitoring system, mainly introduces how to count static resource loading errors, follow me step by step, you can also build a front-end monitoring system of your own.

The previous chapter explained how to count static resource load errors, but today we will talk about interface request errors. One might think that interface errors should be monitored, counted, and fixed in the background. That’s true, and the back-end services have a lot of sophisticated statistical tools that can handle most exceptions, so why do you need the front end to monitor interface requests? The reason is very simple, because the front end is the first place to find bugs, how to quickly throw out before you help the background to carry the blame, at this time, we need to have an interface monitoring system, haha 🙂

  • We need to monitor the situation of interface errors and locate the causes of online problems in time
  • We need to analyze the performance of the interface to help us optimize the front-end application.

Okay, let’s get down to business:

How do you monitor front-end interface requests?

In general, front-end requests are ajax requests with jquery, and fetch requests are also used, as well as requests encapsulated by the front-end framework itself. However, they all encapsulate the browser object window.xmlHttprequest, so as long as we can listen for some events on this object, we can separate the requested information.

1. How do I listen for Ajax requests

If you use jquery, Zepto, or your own packaged Ajax methods, you can use the following methods to listen. We listen for two events loadStart and loadEnd on the XMLHttpRequest object. But the result of listening is not as easy to understand as we thought. Let’s first look at ajaxLoadStart, the callback method of ajaxLoadEnd.

/** **
function recordHttpLog() {
    // Listen for ajax status
    function ajaxEventTrigger(event) {
        var ajaxEvent = new CustomEvent(event, {
            detail: this
        });
        window.dispatchEvent(ajaxEvent);
    }
    var oldXHR = window.XMLHttpRequest;
    function newXHR() {
        var realXHR = new oldXHR();
        realXHR.addEventListener(
            "loadstart".function() {
                ajaxEventTrigger.call(this."ajaxLoadStart");
            },
            false
        );
        realXHR.addEventListener(
            "loadend".function() {
                ajaxEventTrigger.call(this."ajaxLoadEnd");
            },
            false
        );
        // If the log reporting interface is abnormal, it will cause an infinite loop.
        // realXHR.onerror = function () {
        // siftAndMakeUpMessage("Uncaught FetchError: Failed to ajax", WEB_LOCATION, 0, 0, {});
        // }
        return realXHR;
    }
    var timeRecordArray = [];
    window.XMLHttpRequest = newXHR;
    window.addEventListener("ajaxLoadStart".function(e) {
        var tempObj = {
            timeStamp: new Date().getTime(),
            event: e
        };
        timeRecordArray.push(tempObj);
    });
    window.addEventListener("ajaxLoadEnd".function() {
        for (var i = 0; i < timeRecordArray.length; i++) {
            if (timeRecordArray[i].event.detail.status > 0) {
                var currentTime = new Date().getTime();
                var url = timeRecordArray[i].event.detail.responseURL;
                var status = timeRecordArray[i].event.detail.status;
                var statusText = timeRecordArray[i].event.detail.statusText;
                var loadTime = currentTime - timeRecordArray[i].timeStamp;
                if(! url || url.indexOf(HTTP_UPLOAD_LOG_API) ! =- 1) return;
                var httpLogInfoStart = new HttpLogInfo(
                    HTTP_LOG,
                    url,
                    status,
                    statusText,
                    "Make a request",
                    timeRecordArray[i].timeStamp,
                    0
                );
                httpLogInfoStart.handleLogInfo(HTTP_LOG, httpLogInfoStart);
                var httpLogInfoEnd = new HttpLogInfo(
                    HTTP_LOG,
                    url,
                    status,
                    statusText,
                    "Request return",
                    currentTime,
                    loadTime
                );
                httpLogInfoEnd.handleLogInfo(HTTP_LOG, httpLogInfoEnd);
                // The current request is removed from the array after success
                timeRecordArray.splice(i, 1); }}}); }Copy the code

There are many requests on a page, and when a page makes multiple requests, the ajaxLoadStart event is heard, but it is impossible to tell which request was sent, and returns a very large event object with almost identical contents. When the ajaxLoadEnd event is listened on, it also returns an overloaded time object that contains all the information requested by the interface. Fortunately, the two objects are the same reference, which means that the ajaxLoadStart and ajaxLoadEnd events are caught using the same object. Then we have a way to figure it out.

When an ajaxLoadStart event occurs, we put all of the event objects in the callback method into the array timeRecordArray. When an ajaxLoadEnd event occurs, we iterate through the data and find an event object that returns the result, indicating that the interface request has completed. And removes the event object from the array. This allows us to analyze the contents of the outgoing interface request one by one.

How do I listen for fetch requests

With the first approach, you can already listen to most Ajax requests. However, more and more people are using FETCH requests because the chained calls of FETCH can get us out of the nested hell of Ajax and into the favor of more people. Strangely, I cannot listen to the fetch request event using the first method. Why is this?

return new Promise(function(resolve, reject) {
    var request = new Request(input, init);
    var xhr = new XMLHttpRequest();

    xhr.onload = function() {
        var options = {
            status: xhr.status,
            statusText: xhr.statusText,
            headers: parseHeaders(xhr.getAllResponseHeaders() || "")}; options.url ="responseURL" in xhr
                ? xhr.responseURL
                : options.headers.get("X-Request-URL");
        var body = "response" in xhr ? xhr.response : xhr.responseText;
        resolve(new Response(body, options));
    };
    / /...
    xhr.send(
        typeof request._bodyInit === "undefined" ? null : request._bodyInit
    );
});
Copy the code

This is the source code for fetch. As you can see, it creates a Promise and a newXMLHttpRequest object var XHR =newXMLHttpRequest(). Since the fetch code is built into the browser, it must be executed with monitor code first, so when we add listening events, we cannot listen to the XMLHttpRequest object in the FETCH. What do we do? We need to rewrite the fetch code. As long as we override fetch after monitoring code execution, we can listen for requests sent using FETCH normally. Simple as that 🙂

Look at the fields you want to listen on:

// Sets the generic properties of the log object class
function setCommonProperty() {
    this.happenTime = new Date().getTime(); // Log occurrence time
    this.webMonitorId = WEB_MONITOR_ID; // Unique identifier used to distinguish applications (one for each project)
    this.simpleUrl = window.location.href.split("?") [0].replace("#".""); // The url of the page
    this.completeUrl = utils.b64EncodeUnicode(
        encodeURIComponent(window.location.href)
    ); // The full URL of the page
    this.customerKey = utils.getCustomerKey(); // The unique identifier is used to distinguish users. The unique identifier becomes invalid after local data is cleaned.
    // User - defined information, which is actively imported by the developer, facilitates accurate location of online problems
    var wmUserInfo = localStorage.wmUserInfo
        ? JSON.parse(localStorage.wmUserInfo)
        : "";
    this.userId = utils.b64EncodeUnicode(wmUserInfo.userId || "");
    this.firstUserParam = utils.b64EncodeUnicode(
        wmUserInfo.firstUserParam || ""
    );
    this.secondUserParam = utils.b64EncodeUnicode(
        wmUserInfo.secondUserParam || ""
    );
}
// The interface requests logs, inherited from MonitorBaseInfo
function HttpLogInfo(uploadType, url, status, statusText, statusResult, currentTime, loadTime) {
    setCommonProperty.apply(this);
    this.uploadType = uploadType; // Upload type
    this.httpUrl = utils.b64EncodeUnicode(encodeURIComponent(url)); // Request an address
    this.status = status; // Interface status
    this.statusText = statusText; // State description
    this.statusResult = statusResult; // Distinguish between initiated and returned states
    this.happenTime = currentTime; // Client send time
    this.loadTime = loadTime; // The interface request takes time
}
Copy the code

All the work is ready, if the collected logs are presented from different dimensions, I won’t go into detail, straight to the picture above. In this way, you can have a clear understanding of the front-end interface error situation and quickly find the online problems.

reference

  • Step by step to build a front-end monitoring system: JS error monitoring
  • Log network request exceptions with the Fundebug plug-in

About 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, Walnut Programming, Lychee FM, Zhangmen 1-to-1, Weimai, Qingtuanshe and many other brand enterprises. Welcome to try it for free!