搭建前端監控系統(備選)用戶行爲統計和監控篇(如何快速定位線上問題)

  背景:市面上的監控系統有不少,大多收費,對於小型前端項目來講,必然是痛點。另外一點主要緣由是,功能雖然通用,卻未必可以知足咱們本身的需求,  因此咱們自給自足也許是個不錯的辦法。html

  這是搭建前端監控系統的第二章,主要是介紹如何統計js報錯,跟着我一步步作,你也能搭建出一個屬於本身的前端監控系統。前端

 

  目前已經在運行的線上Demo : 前端監控系統  git

  代碼和講解都放在這篇文章裏: 監控系統介紹及代碼程序員

  若是實在嫌部署麻煩,Demo系統能夠提供 7天 的監控量,我會長期維護:一鍵部署github

  

  一直以來, 前端上線的項目,對於前端程序猿來講,徹底是一個黑盒子。 項目一旦上線,咱們徹底不知道用戶在咱們的項目裏邊作了什麼,跳轉到哪裏,是否是報錯了。一旦線上用戶出現問題,而咱們又沒法復現的時候,才能體會到什麼叫絕望。 無論多麼艱難,問題老是會在哪裏等着你。因此,若是咱們能夠把線上的項目變成一個白盒子,讓咱們可以知道用戶在線上幹了什麼,復現再也不困難了,對前端程序員來講,是否是一件好事呢。web

  接下來我要寫的是一個重要的功能, 由於它極大的提升了我解決問題的能力, 也讓對個人工做產生了很大的影響。ajax

  截止到如今,來看看我已經完成了哪些功能:數據庫

  PVUV的統計上報,js錯誤的上報和分析, 接口的統計上報,頁面截屏的統計上報。 那麼,再補上今天要寫的「用戶點擊行爲的上報」, 咱們基本上就可以分析出一個用戶在頁面上幹了什麼。瀏覽器

 


 

  1、如何記錄線上用戶的行爲緩存

  線上用戶的基本行爲包括: 訪問頁面, 點擊行爲,請求接口行爲, js報錯行爲, 這幾點基本上可以清楚的記錄下用戶在線上的全部行爲。 固然還包括:資源加載行爲,滾動頁面行爲, 元素進入用戶視野等等行爲,這些是更爲細節的行爲統計, 也許會在之後進行完善, 可是以上的四種行爲已經能夠完成咱們的統計需求。

  訪問頁面, js報錯行爲咱們已經有了,接下來看看如何統計點擊行爲和請求接口的行爲吧。

  點擊行爲:

// 用戶行爲日誌,繼承於日誌基類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));
  }

/**
   * 用戶行爲記錄監控
   * @param project 項目詳情
   */
  function recordBehavior(project) {
    // 行爲記錄開關
    if (project && project.record && project.record == 1) {
      // 記錄行爲前,檢查一下url記錄是否變化
      checkUrlChange();
      // 記錄用戶點擊元素的行爲數據
      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 (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);
      }
    }
  };

 

  咱們先來看一下點擊行爲的代碼,其實很簡單,就是重寫一下document的onclick方法,而後把相應的元素的屬性,內容等等保存起來, 可是,咱們費了這麼大的力氣保存瞭如此多的日誌,就爲了簡單的記錄一下用戶的點擊行爲,實在太浪費了。 因此,這個點擊行爲統計會被添加到將來的留存分析當中去,到時候可以實現無埋點記錄日誌的功能,讓咱們的監控系統更加的強大和豐富。留存分析會參考GrowingIo, 有興趣能夠了解一下。

  咱們須要記錄下元素的className, tagName, innerText等等,咱們須要足夠的的內容纔可以肯定用戶點擊的是哪一個按鈕。這種方式比較弱智,將會在之後寫留存分析功能的時候進行完善一下,可是目前足以知足咱們的要求了。

  


 

  請求接口行爲:

// 接口請求日誌,繼承於日誌基類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() {
    // 監聽ajax的狀態
    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, "發起請求", 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, "請求返回", currentTime);
      httpLogInfo.handleLogInfo(HTTP_LOG, httpLogInfo);
    });

  }

  讓咱們來看看接口行爲統計的代碼先,原本這個我想單獨拿出來講一說的,可是如今麼有那麼多時間把它相關的功能開發出來,因此只寫了一個簡版的。

  接口行爲的統計包括: 發起請求,接收請求,接收狀態,請求時長, 經過前端對接口的統計和分析,咱們是能夠觀察出線上接口的質量,同時也可以對前端的邏輯作出相應的調整,已達到頁面加載的最佳效果。 數據庫字段定義都在分析後臺的項目裏, 能夠直接去看。

  首先,咱們要監聽頁面的ajax請求, 如上所示,寫了一段監聽ajax請求的代碼(我是在網上扒下來的 thanks), 能夠監聽到頁面上全部的ajax請求,對整個ajax請求過程進行了原子性分析,咱們能夠監聽到請求過程當中任何一個時段的事件,很是好用。 可是,有一點很是重要, 若是你的項目裏邊用的是fetch請求數據的話, 那麼這些監聽就無效了。 由於fetch代碼是瀏覽器注入的, 確定先用監控代碼執行,而後你再監聽ajax就一點用都沒有了。 因此你須要在寫好ajax監聽以後,重寫fetch代碼, 這樣就能夠生效了。好了,這部分並非這篇幅的重點,咱們就說到這裏。

  2、如何查詢線上用戶的行爲

  終於,咱們把剩下的兩種行爲記錄都成功上傳了,那麼該若是把他們都查詢出來呢。咱們先來看一下頁面上我查詢出來的結果。

      

    由於屏幕過小,沒法展現全部的記錄,記錄信息包含:行爲名稱,行爲發生時間, 行爲發生頁面, 錯誤信息, 錯誤截圖, 以及用戶自定義上傳截圖的時機。

  說到這裏有幾個小問題須要注意。

  1. 由於是用Js作探針,記錄日誌的時候很難保證每次記錄均可以把用戶的userId插入進去

  2. 因此咱們給每一個用戶都定義一個customerKey來作區分,若是用戶不卸載app和清理app的緩存, customerKey將保持不變

  3. 在查詢用戶的行爲記錄的時候,須要先查詢出用戶全部的customerKey(可能有多個),再用customerKey進行查詢,即可以獲得準確的結果。

  3、如何分析線上用戶的行爲

  其實咱們作了這麼多,記錄了這麼多,就是爲了這個目的:分析行爲,快速定位問題。

  那麼咱們如何定位問題呢,我能夠舉例說明一下:

  1. JS報錯阻斷行爲,咱們能夠看到發生錯誤的先後行爲,就可以快速準肯定位問題。

  

  2. 複雜的連接跳轉發生了錯誤。有些錯誤是前端頁面會通過複雜的跳轉,回退以後才發生的,就算測試人員也很難測試出這種問題,由於線上的用戶的任何行爲都有可能出現。每每咱們知道的只是他在最後停留的頁面發生了錯誤。 如此,通過咱們排查行爲日誌, 就可以復現出用戶的行爲, 從而復現BUG

  3. 接口異常。 正常狀況下,前端的接口都會設置超時時間的, 可是呢, 後臺接口排查發現正常, 而前端就是沒法正常執行, 這種問題沒有顯示的錯誤現象,而線上的反饋並不可以準確,前端只能背鍋了。 而日誌記錄是能夠把請求發出時間和返回時間記錄下來, 是否超時,看一眼就知道。

  4. 線上的用戶根本就不會反饋異常, 他們能作的只是把最後一眼能看到的東西告訴你。 天知道他們以前經歷了什麼步驟。 最終的結果是,前端有問題,而後背鍋,哈哈。

  總之, 咱們知道用戶在頁面上幹了什麼, 便再也不擔憂問題出現, 碰見問題也不會再手忙腳亂了。

相關文章
相關標籤/搜索