Abstract: By recording user behavior, the BUG scene can be quickly reproduced.

  • Author: One step at a time
  • Build front-end monitoring system (Optional) User behavior statistics and monitoring (how to quickly locate online problems)

FundebugReproduced with authorization, copyright belongs to the original author.

Step by step build front-end monitoring system series blog:

  • Step by step to build a front-end monitoring system: JS error monitoring
  • Build a front-end monitoring system step by step: How to report web page screenshots?
  • Step by step to build a front-end monitoring system: interface request exception monitoring
  • Step by step, build a front-end monitoring system: How to locate front-end problems?
  • Step by step front-end monitoring system: How to record user behavior?

** Background: ** There are many monitoring systems on the market, most of which are charged. For small front-end projects, it is bound to be a pain point. 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 second chapter to build a front-end monitoring system, mainly introduces how to statistics JS error, follow me step by step to do, you can also build a belongs to their own front-end monitoring system.

Currently running online Demo:Front-end monitoring system

The code and explanation are in this article:Monitoring system introduction and code

If it is really troublesome to deploy, the Demo system can provide 7 days of monitoring, and I will maintain it for a long time:A key deployment

Front-end projects have always been a black box for front-end programmers. Once the project is online, we have no idea what users are doing in our project, where to jump to, or whether there is an error. Despair comes to us when something goes wrong with our online users and we can’t replicate it. No matter how hard it is, there is always a problem waiting for you. So, wouldn’t it be a good thing for the front-end programmer if we could turn an online project into a white box that allows us to see what the user is doing online and make it easier to reproduce?

The next feature I’m going to write about is an important one, because it has greatly improved my problem-solving skills and made a big impact on my work.

Here’s what I’ve done so far:

PV/UV statistics report, JS error report and analysis, interface statistics report, page screenshot statistics report. So, in addition to today’s article on “reporting user clicks,” we can basically analyze what a user is doing on a page.

1. How to record online users’ behaviors

The basic behaviors of online users include: page visit, click behavior, interface request behavior, JS error behavior, which can basically clearly record all online behaviors of users. Of course, it also includes: resource loading behavior, page scrolling behavior, element entering user view and so on. These are more detailed behavior statistics, which may be improved in the future, but the above four behaviors can meet our statistical requirements.

Now that we have access to the page and js error behavior, let’s look at how to count click behavior and request interface behavior.

Click on the behavior

// User behavior logs, inherited from MonitorBaseInfo
  function BehaviorInfo(uploadType, behaviorType, className, placeholder, inputValue, tagName, innerText) {
    setCommonProperty.apply(this);
    this.uploadType = uploadType;
    this.behaviorType = behaviorType;
    this.className = utils.b64EncodeUnicode(className);
    this.placeholder = utils.b64EncodeUnicode(placeholder);
    this.inputValue = utils.b64EncodeUnicode(inputValue);
    this.tagName = tagName;
    this.innerText = utils.b64EncodeUnicode(encodeURIComponent(innerText));
  }

/** * user behavior record monitoring * @param project project details */
  function recordBehavior(project) {
    // Behavior record switch
    if (project && project.record && project.record == 1) {
      // Check if the URL record has changed before recording the behavior
      checkUrlChange();
      // Records the behavior of the user clicking on the element
      document.onclick = function (e) {
        var className = "";
        var placeholder = "";
        var inputValue = "";
        var tagName = e.target.tagName;
        var innerText = "";
        if(e.target.tagName ! ="svg"&& e.target.tagName ! ="use") {
          className = e.target.className;
          placeholder = e.target.placeholder || "";
          inputValue = e.target.value || "";
          innerText = e.target.innerText.replace(/\s*/g."");
          // If the click content is too long, it will be captured and uploaded
          if (innerText.length > 200) innerText = innerText.substring(0.100) + "... ..." + innerText.substring(innerText.length - 99, innerText.length - 1);
          innerText = innerText.replace(/\s/g.' ');
        }
        var behaviorInfo = new BehaviorInfo(ELE_BEHAVIOR, "click", className, placeholder, inputValue, tagName, innerText); behaviorInfo.handleLogInfo(ELE_BEHAVIOR, behaviorInfo); }}};Copy the code

Let’s take a look at the code for the click behavior. It’s actually very simple, which is to rewrite the document onclick method, and then save the corresponding element properties, content, etc. ** But ** we’ve gone to a lot of trouble to save so many logs, just to record the click behavior of the user, What a waste. Therefore, this click behavior statistics will be added to the future retention analysis to achieve the function of unburied logging, making our monitoring system more powerful and rich. Retention analysis will refer to GrowingIo for more information.

We need to record the element’s className, tagName, innerText, and so on, and we need enough information to know which button the user clicked. This approach is a bit silly and will be refined later when writing retention analytics, but it’s good enough for now.

Request interface behavior

// The interface requests logs, inherited from MonitorBaseInfo
  function HttpLogInfo(uploadType, url, status, statusText, statusResult, currentTime) {
    setCommonProperty.apply(this);
    this.uploadType = uploadType;
    this.httpUrl = utils.b64EncodeUnicode(url);
    this.status = status;
    this.statusText = statusText;
    this.statusResult = statusResult;
    this.happenTime = currentTime;
  }

/** **
  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('abort'.function () { ajaxEventTrigger.call(this.'ajaxAbort'); }, false);
      realXHR.addEventListener('error'.function () { ajaxEventTrigger.call(this.'ajaxError'); }, false);
      realXHR.addEventListener('load'.function () { ajaxEventTrigger.call(this.'ajaxLoad'); }, false);
      realXHR.addEventListener('loadstart'.function () { ajaxEventTrigger.call(this.'ajaxLoadStart'); }, false);
      realXHR.addEventListener('progress'.function () { ajaxEventTrigger.call(this.'ajaxProgress'); }, false);
      realXHR.addEventListener('timeout'.function () { ajaxEventTrigger.call(this.'ajaxTimeout'); }, false);
      realXHR.addEventListener('loadend'.function () { ajaxEventTrigger.call(this.'ajaxLoadEnd'); }, false);
      realXHR.addEventListener('readystatechange'.function() { ajaxEventTrigger.call(this.'ajaxReadyStateChange'); }, false);
      return realXHR;
    }

    window.XMLHttpRequest = newXHR;
    window.addEventListener('ajaxLoadStart'.function(e) {
      var currentTime = new Date().getTime()
      setTimeout(function () {
        var url = e.detail.responseURL;
        var status = e.detail.status;
        var statusText = e.detail.statusText;
        if(! url || url.indexOf(HTTP_UPLOAD_LOG_API) ! =- 1) return;
        var httpLogInfo = new HttpLogInfo(HTTP_LOG, url, status, statusText, "Make a request", currentTime);
        httpLogInfo.handleLogInfo(HTTP_LOG, httpLogInfo);
      }, 2000)});window.addEventListener('ajaxLoadEnd'.function(e) {
      var currentTime = new Date().getTime()
      var url = e.detail.responseURL;
      var status = e.detail.status;
      var statusText = e.detail.statusText;
      if(! url || url.indexOf(HTTP_UPLOAD_LOG_API) ! =- 1) return;
      var httpLogInfo = new HttpLogInfo(HTTP_LOG, url, status, statusText, "Request return", currentTime);
      httpLogInfo.handleLogInfo(HTTP_LOG, httpLogInfo);
    });

  }
Copy the code

Let’s take a look at the interface behavior statistics code first, originally THIS I want to separate out to talk about, but now do not have enough time to develop its related functions, so I only wrote a simple version.

The statistics of interface behavior include: initiating request, receiving request, receiving state, and request duration. Through the statistics and analysis of the front-end interface, we can observe the quality of the outbound interface and adjust the front-end logic accordingly, so as to achieve the best effect of page loading. Database field definitions are in the analysis background project, you can directly look at.

First, we need to listen for ajax requests on the page, as shown above, write a piece of code to listen for Ajax requests (I am on the Internet thanks), can listen for all Ajax requests on the page, atomic analysis of the entire Ajax request process, we can listen for any time during the request process events. Very useful. ** However, it is important to note that these listeners are invalid if your project uses a fetch for data. Because the FETCH code is browser-injected, it must be executed with monitor code first, and then you can listen for Ajax and it doesn’t help at all. So you need to rewrite the FETCH code after you write the Ajax listener so that it will work. Well, that’s not the point of the story, so let’s stop there.

How to query online user behavior

Finally, we successfully uploaded the remaining two behavior records, so how to query them out. Let’s take a look at what I found on the page.

Because the screen is too small to display all the records, the record information includes: behavior name, behavior occurred time, behavior occurred page, error message, error screenshot, and user – defined time to upload the screenshot.

Speaking of which, there are a few minor issues to note.

  • Since Js is used as a probe, it is difficult to ensure that the user’s userId is inserted into the log every time
  • So we define a customerKey for each user to differentiate. If the user does not uninstall the APP and clean the app cache, the customerKey will remain unchanged
  • When querying user behavior records, you need to query all the customerKeys of the user (there may be multiple customerKeys) and then use the customerKey to obtain accurate results.

Third, how to analyze online user behavior

This is exactly what we’ve been doing and documenting: analyzing behavior and quickly locating problems.

So how do we locate the problem? I can give you an example:

  • JS error blocking behavior, we can see the behavior before and after the occurrence of errors, can quickly and accurately locate the problem.

  • A complex link jump failed. Some of these errors occur after the front end of the page goes through a complex jump and falls back, which is difficult for even a tester to detect because any behavior of the online user can occur. Often all we know is that he made an error on the last page he stopped on. This way, after we sift through the behavior log, we can reproduce the user’s behavior and thus the BUG
  • The interface is abnormal. Under normal circumstances, the front-end interface will set the timeout time, but it is found that the background interface is normal, and the front-end is not normal execution, this problem does not display error phenomenon, and the online feedback is not accurate, the front-end can only take the blame. Logging is to record the request time and return time, whether timeout, at a glance to know.
  • Online users don’t report anything unusual, they just tell you the last thing they see. God knows what steps they went through. The end result is that there is a problem on the front end and then the blame is on the back, haha.

In short, we know what the user is doing on the page, so we don’t have to worry about problems, and we don’t have to worry about problems.

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!