Recently in the research of front-end monitoring scheme, because of the work needs to study the badJS source code of Xiayi Factory, mainly read the front-end report, that is, BADJS-report. Badjs can be found in the official documentation

Front-end monitoring pain points

Before you get to know a framework or library, think about what problem it is trying to solve. The landing of front-end abnormal monitoring system This article summarizes the problems that need to be solved by front-end monitoring in detail, and summarizes the following:

  1. Error to intercept
  2. Report the error
  3. Offline error log storage
  4. Error path playback
  5. Visual log management background
  6. Compressed single – line file source location
  7. Email (SMS) notification

Badjs2 already implements all of the above functions except for four and six. Error interception, error reporting and offline error log storage are implemented by the front-end component BADJS-Report. Badjs-report code mainly has three entrances: init initialization, onError rewriting and reportOfflinelog reporting offline logs. Here’s how each of these three portals calls other functions and implements functions (the code below is truncated due to space limitations and can be understood in combination with the source code).

BJ_REPORT init initialization

Badjs-report inserts the BJ_REPORT object into the global object, which provides init() for initialization. The function method takes an object as a configuration parameter.

The first step is to overwrite the value of the private _config object with the value of the passed configuration parameter object.

init: function(config) {
	if(t.isobj (config)) {// traversal overwritefor (var key inconfig) { _config[key] = config[key]; }}}Copy the code

It then concatenates the reported URL and clears the error cache.

Var id = parseInt(_config.id, 10);if (id) {
    _config._reportUrl = (_config.url || "/badjs") +
        "? id=" + id +
        "&uin=" + _config.uin +
        "&"; } // Clear the list of errors. The _process_log function is covered belowif (_log_list.length) {
	_process_log();
}
Copy the code

Next, initialize the indexedDB database. Badjs stores offline logs in the indexedDB database and uploads offline logs by calling reportOfflineLog().

if(! Offline_DB._initing) { Offline_DB._initing =true;
    Offline_DB.ready(function(err, DB) {
        if (DB) {
            setTimeout(function() {// Clear the expiration log db.cleardb (_config.offlinelogexp);setTimeout(function() { _config.offlineLogAuto && _autoReportOffline(); }, 5000); }, 1000); }}); }Copy the code

The main job of offline_db.ready () is to open the database and set success and upgradenneeded to listen for events

Var request = window.indexeddb.open ("badjs", version); // Request. Onsuccess =function(e) { self.db = e.target.result; // Execute the callback after successfully openingsetTimeout(function() { callback(null, self); }, 500); }; // Version upgrade (upgradenrequired Labour page is triggered first before success is triggered after initialization) request.onupgradenrequired =function(e) {
   var db = e.target.result;
   if(! db.objectStoreNames.contains('logs')) {
       db.createObjectStore('logs', { autoIncrement: true}); }};Copy the code

Rewrite the onerror

After BJreport is initialized, we need to overwrite window.onError to catch errors in the program. The overwritten onError mainly formats the error information and pushes the error into an error queue, while the push() method also triggers _process_log().

var orgError = global.onerror;
global.onerror = function(msg, url, line, col, error) { var newMsg = msg; // Format error messageif (error && error.stack) {
        newMsg = T.processStackMsg(error);
    }
    if (T.isOBJByType(newMsg, "Event")) {
        newMsg += newMsg.type ?
            ("--" + newMsg.type + "--" + (newMsg.target ?
                (newMsg.target.tagName + "... "" + newMsg.target.src) : "")) : ""; Push ({MSG: newMsg, target: url, rowNum: line, colNum: col, _orgMsg: msg }); _process_log(); // Call the original global onError event orgError && orgError.apply(global, arguments); };Copy the code

Badjs report functions are mainly implemented by _process_log(), including random report, ignore report, offline log storage, and delay report. The error object is first pushed into the _log_list during push, and then _process_log() loops through the _log_list.

The random of Config is used to determine whether to ignore this report

Var randomIgnore = math.random () >= _config.random; var randomIgnore = math.random () >= _config.random;Copy the code

Each time the cycle determines whether the number exceeds the repeated count

// Report againif (T.isRepeat(report_log)) continue;
Copy the code

It then filters according to user-defined ignore rules

/ / formatlogVar log_str = _report_log_tostring(report_log, submit_log_list.length); // If the user defines the ignore rule, the filter is implemented according to the ruleif (T.isOBJByType(_config.ignore, "Array")) {
    for (var i = 0, l = _config.ignore.length; i < l; i++) {
        var rule = _config.ignore[i];
        if ((T.isOBJByType(rule, "RegExp") && rule.test(log_str[1])) ||
            (T.isOBJByType(rule, "Function") && rule(report_log, log_str[1]))) {
            isIgnore = true;
            break; }}}Copy the code

The offline logs are then stored in the database and the logs that need to be reported are pushed into submit_log_list

// Ignore rule passedif(! IsIgnore) {// if the offlineLog function is enabled, store the logs to the database _config.offlinelog && _save2Offline("badjs_"+ _config.id + _config.uin, report_log); // If the level is 20, the logs are pushed by the offlineLog method, and only offline logs are savedif(! randomIgnore && report_log.level ! Submit_log_list (log_str[0]) {submit_log_list (log_str[0]) {submit_log_list (log_str[0]); OnReport && (_config.onReport(_config.id, report_log)); }}Copy the code

At the end of the cycle, report or delay the report as required

if(isReportNow) { _submit_log(); // Report immediately}else if(! comboTimeout) { comboTimeout =setTimeout(_submit_log, _config.delay); // Delay reporting}Copy the code

In the _submit_log() method, a new img tag is used to report

var _submit_log = function() {// If the user defines the report method, the custom method is usedif (_config.submit) {
        _config.submit(url, submit_log_list);
    } elseVar _img = new Image(); _img.src = url; } submit_log_list = []; };Copy the code

Uploading Offline Logs

Badjs requires the user to actively call the bj_report.reportofflinelog () method to upload offline logs in the database.

The reportOfflineLog() method first opens the database by calling offline_db.ready, then gets the logs from the database through db.getlogs () in the callback, and finally uploads the data through the form submission.

reportOfflineLog: function() {
    Offline_DB.ready(function(err, DB) {// The Date must be startDate ~ endDate var startDate = new Date -0 -_config. offlineLogExp * 24 * 3600 * 1000; var endDate = new Date - 0; DB.getLogs({ start: startDate, end: endDate, id: _config.id, uin: _config.uin },function(err, result) {
            var iframe = document.createElement("iframe");
            iframe.name = "badjs_offline_" + (new Date - 0);
            iframe.frameborder = 0;
            iframe.height = 0;
            iframe.width = 0;
            iframe.src = "javascript:false;";

            iframe.onload = function() {
                var form = document.createElement("form");
                form.style.display = "none";
                form.target = iframe.name;
                form.method = "POST";
                form.action = _config.offline_url || _config.url.replace(/badjs$/, "offlineLog");
                form.enctype.method = 'multipart/form-data';

                var input = document.createElement("input");
                input.style.display = "none";
                input.type = "hidden";
                input.name = "offline_log"; input.value = JSON.stringify({ logs: result, userAgent: navigator.userAgent, startDate: startDate, endDate: endDate, id: _config.id, uin: _config.uin }); iframe.contentDocument.body.appendChild(form); form.appendChild(input); // Submit the offline log form.submit();setTimeout(function() {
                    document.body.removeChild(iframe);
                }, 10000);

                iframe.onload = null;
            };
            document.body.appendChild(iframe);
        });
    });
}
Copy the code

conclusion

In order to prevent the length is too long, I have made some cuts to the above source code, if you want to see the complete source code can see my own version with Chinese annotations https://github.com/Q-Zhan/badjs-report-annotated, any question can issue to me ~~