成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

一步一步搭建前端監(jiān)控系統(tǒng):如何記錄用戶行為?

tolerious / 3157人閱讀

摘要:摘要通過記錄用戶行為,快速復(fù)現(xiàn)場景。這是搭建前端監(jiān)控系統(tǒng)的第二章,主要是介紹如何統(tǒng)計報錯,跟著我一步步做,你也能搭建出一個屬于自己的前端監(jiān)控系統(tǒng)。

摘要: 通過記錄用戶行為,快速復(fù)現(xiàn)BUG場景。

作者:一步一個腳印一個坑

原文:搭建前端監(jiān)控系統(tǒng)(備選)用戶行為統(tǒng)計和監(jiān)控篇(如何快速定位線上問題)

Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。

一步一步搭建前端監(jiān)控系統(tǒng)系列博客:

一步一步搭建前端監(jiān)控系統(tǒng):JS錯誤監(jiān)控篇

一步一步搭建前端監(jiān)控系統(tǒng):如何將網(wǎng)頁截圖上報?

一步一步搭建前端監(jiān)控系統(tǒng):接口請求異常監(jiān)控篇

一步一步搭建前端監(jiān)控系統(tǒng):如何定位前端線上問題?

一步一步搭建前端監(jiān)控系統(tǒng):如何記錄用戶行為?

背景:市面上的監(jiān)控系統(tǒng)有很多,大多收費,對于小型前端項目來說,必然是痛點。另一點主要原因是,功能雖然通用,卻未必能夠滿足我們自己的需求, 所以我們自給自足也許是個不錯的辦法。

這是搭建前端監(jiān)控系統(tǒng)的第二章,主要是介紹如何統(tǒng)計js報錯,跟著我一步步做,你也能搭建出一個屬于自己的前端監(jiān)控系統(tǒng)。

目前已經(jīng)在運行的線上Demo:前端監(jiān)控系統(tǒng)

代碼和講解都放在這篇文章里:監(jiān)控系統(tǒng)介紹及代碼

如果實在嫌部署麻煩,Demo系統(tǒng)可以提供 7天 的監(jiān)控量,我會長期維護:一鍵部署

一直以來, 前端上線的項目,對于前端程序猿來說,完全是一個黑盒子。 項目一旦上線,我們完全不知道用戶在我們的項目里邊做了什么,跳轉(zhuǎn)到哪里,是不是報錯了。一旦線上用戶出現(xiàn)問題,而我們又無法復(fù)現(xiàn)的時候,才能體會到什么叫絕望。 不管多么艱難,問題總是會在哪里等著你。所以,如果我們可以把線上的項目變成一個白盒子,讓我們能夠知道用戶在線上干了什么,復(fù)現(xiàn)不再困難了,對前端程序員來說,是不是一件好事呢。

接下來我要寫的是一個重要的功能, 因為它極大的提高了我解決問題的能力, 也讓對我的工作產(chǎn)生了很大的影響。

截止到現(xiàn)在,來看看我已經(jīng)完成了哪些功能:

PV/UV的統(tǒng)計上報,js錯誤的上報和分析, 接口的統(tǒng)計上報,頁面截屏的統(tǒng)計上報。 那么,再補上今天要寫的“用戶點擊行為的上報”, 我們基本上就能夠分析出一個用戶在頁面上干了什么。

一、如何記錄線上用戶的行為

線上用戶的基本行為包括: 訪問頁面, 點擊行為,請求接口行為, js報錯行為, 這幾點基本上能夠清楚的記錄下用戶在線上的所有行為。 當然還包括:資源加載行為,滾動頁面行為, 元素進入用戶視野等等行為,這些是更為細節(jié)的行為統(tǒng)計, 也許會在以后進行完善, 但是以上的四種行為已經(jīng)可以完成我們的統(tǒng)計需求。

訪問頁面, js報錯行為我們已經(jīng)有了,接下來看看如何統(tǒng)計點擊行為和請求接口的行為吧。

點擊行為
// 用戶行為日志,繼承于日志基類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));
  }

/**
   * 用戶行為記錄監(jiān)控
   * @param project 項目詳情
   */
  function recordBehavior(project) {
    // 行為記錄開關(guān)
    if (project && project.record && project.record == 1) {
      // 記錄行為前,檢查一下url記錄是否變化
      checkUrlChange();
      // 記錄用戶點擊元素的行為數(shù)據(jù)
      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, "");
          // 如果點擊的內(nèi)容過長,就截取上傳
          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方法,然后把相應(yīng)的元素的屬性,內(nèi)容等等保存起來, 但是,我們費了這么大的力氣保存了如此多的日志,就為了簡單的記錄一下用戶的點擊行為,實在太浪費了。 所以,這個點擊行為統(tǒng)計會被添加到未來的留存分析當中去,到時候能夠?qū)崿F(xiàn)無埋點記錄日志的功能,讓我們的監(jiān)控系統(tǒng)更加的強大和豐富。留存分析會參考GrowingIo, 有興趣可以了解一下。

我們需要記錄下元素的className, tagName, innerText等等,我們需要足夠的的內(nèi)容才能夠確定用戶點擊的是哪個按鈕。這種方式比較弱智,將會在以后寫留存分析功能的時候進行完善一下,但是目前足以滿足我們的要求了。

請求接口行為
// 接口請求日志,繼承于日志基類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;
  }

/**
   * 頁面接口請求監(jiān)控
   */
  function recordHttpLog() {
    // 監(jiān)聽ajax的狀態(tài)
    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, "發(fā)起請求", 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);
    });

  }

讓我們來看看接口行為統(tǒng)計的代碼先,本來這個我想多帶帶拿出來說一說的,但是現(xiàn)在么有那么多時間把它相關(guān)的功能開發(fā)出來,所以只寫了一個簡版的。

接口行為的統(tǒng)計包括: 發(fā)起請求,接收請求,接收狀態(tài),請求時長, 通過前端對接口的統(tǒng)計和分析,我們是可以觀察出線上接口的質(zhì)量,同時也能夠?qū)η岸说倪壿嬜龀鱿鄳?yīng)的調(diào)整,已達到頁面加載的最佳效果。 數(shù)據(jù)庫字段定義都在分析后臺的項目里, 可以直接去看。

首先,我們要監(jiān)聽頁面的ajax請求, 如上所示,寫了一段監(jiān)聽ajax請求的代碼(我是在網(wǎng)上扒下來的 thanks), 可以監(jiān)聽到頁面上所有的ajax請求,對整個ajax請求過程進行了原子性分析,我們可以監(jiān)聽到請求過程中任何一個時段的事件,非常好用。 但是,有一點非常重要, 如果你的項目里邊用的是fetch請求數(shù)據(jù)的話, 那么這些監(jiān)聽就無效了。 因為fetch代碼是瀏覽器注入的, 肯定先用監(jiān)控代碼執(zhí)行,然后你再監(jiān)聽ajax就一點用都沒有了。 所以你需要在寫好ajax監(jiān)聽之后,重寫fetch代碼, 這樣就可以生效了。好了,這部分并不是這篇幅的重點,我們就說到這里。

二、如何查詢線上用戶的行為

終于,我們把剩下的兩種行為記錄都成功上傳了,那么該如果把他們都查詢出來呢。我們先來看一下頁面上我查詢出來的結(jié)果。

因為屏幕太小,無法展示所有的記錄,記錄信息包含:行為名稱,行為發(fā)生時間, 行為發(fā)生頁面, 錯誤信息, 錯誤截圖, 以及用戶自定義上傳截圖的時機。

說到這里有幾個小問題需要注意。

因為是用Js做探針,記錄日志的時候很難保證每次記錄都可以把用戶的userId插入進去

所以我們給每個用戶都定義一個customerKey來做區(qū)分,如果用戶不卸載app和清理app的緩存, customerKey將保持不變

在查詢用戶的行為記錄的時候,需要先查詢出用戶所有的customerKey(可能有多個),再用customerKey進行查詢,便可以得到準確的結(jié)果。

三、如何分析線上用戶的行為

其實我們做了這么多,記錄了這么多,就是為了這個目的:分析行為,快速定位問題。

那么我們?nèi)绾味ㄎ粏栴}呢,我可以舉例說明一下:

JS報錯阻斷行為,我們可以看到發(fā)生錯誤的前后行為,就能夠快速準確定位問題。

復(fù)雜的鏈接跳轉(zhuǎn)發(fā)生了錯誤。有些錯誤是前端頁面會經(jīng)過復(fù)雜的跳轉(zhuǎn),回退之后才發(fā)生的,就算測試人員也很難測試出這種問題,因為線上的用戶的任何行為都有可能出現(xiàn)。往往我們知道的只是他在最后停留的頁面發(fā)生了錯誤。 如此,經(jīng)過我們排查行為日志, 就能夠復(fù)現(xiàn)出用戶的行為, 從而復(fù)現(xiàn)BUG

接口異常。 正常情況下,前端的接口都會設(shè)置超時時間的, 但是呢, 后臺接口排查發(fā)現(xiàn)正常, 而前端就是無法正常執(zhí)行, 這種問題沒有顯示的錯誤現(xiàn)象,而線上的反饋并不能夠準確,前端只能背鍋了。 而日志記錄是可以把請求發(fā)出時間和返回時間記錄下來, 是否超時,看一眼就知道。

線上的用戶根本就不會反饋異常, 他們能做的只是把最后一眼能看到的東西告訴你。 天知道他們之前經(jīng)歷了什么步驟。 最終的結(jié)果是,前端有問題,然后背鍋,哈哈。

總之, 我們知道用戶在頁面上干了什么, 便不再擔心問題出現(xiàn), 遇見問題也不會再手忙腳亂了。

關(guān)于Fundebug

Fundebug專注于JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java線上應(yīng)用實時BUG監(jiān)控。 自從2016年雙十一正式上線,F(xiàn)undebug累計處理了10億+錯誤事件,付費客戶有陽光保險、核桃編程、荔枝FM、掌門1對1、微脈、青團社等眾多品牌企業(yè)。歡迎大家免費試用!

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/106358.html

相關(guān)文章

  • 步一搭建前端監(jiān)控系統(tǒng)如何定位前端線上問題?

    摘要:一直以來,前端的線上問題很難定位,因為它發(fā)生于用戶的一系列操作之后。當然,這些問題并非不能克服,讓我們來一起看看如何去定位線上的問題吧。地址參考一步一步搭建前端監(jiān)控系統(tǒng)錯誤監(jiān)控篇一步一步搭建前端監(jiān)控系統(tǒng)接口請求異常監(jiān)控篇 摘要: 記錄用戶行為,排查線上BUG。 作者:一步一個腳印一個坑 原文:如何定位前端線上問題(如何排查前端生產(chǎn)問題) Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所...

    aaron 評論0 收藏0
  • 步一搭建前端監(jiān)控系統(tǒng)如何將網(wǎng)頁截圖上報?

    摘要:目前已經(jīng)在運行的線上前端監(jiān)控系統(tǒng)代碼和講解都放在這篇文章里監(jiān)控系統(tǒng)介紹及代碼用戶對前端程序員來說,就是一個黑匣子。 摘要: 通過錄屏或者截圖,快速復(fù)現(xiàn)BUG場景。 作者:一步一個腳印一個坑 原文:搭建前端監(jiān)控系統(tǒng)(備選)Js截圖上報篇 Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。 PS:本文關(guān)于Fundebug錄屏功能的內(nèi)容有些不準確的地方,比如錄屏并非通過截圖實現(xiàn)的,錄屏插件...

    Martin91 評論0 收藏0
  • 步一搭建前端監(jiān)控系統(tǒng):JS錯誤監(jiān)控

    摘要:摘要徒手寫錯誤監(jiān)控。為什么用定時器呢,因為在單頁應(yīng)用中,路由的切換和地址欄的變化是無法被監(jiān)控的,我確實沒有想到特別好的辦法來監(jiān)控,所以用了這種方式,如果有人有更好的辦法,請給我留言,謝謝。 摘要: 徒手寫JS錯誤監(jiān)控。 作者:一步一個腳印一個坑 原文:搭建前端監(jiān)控系統(tǒng)(二)JS錯誤監(jiān)控篇 Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。 背景:市面上的監(jiān)控系統(tǒng)有很多,大多收費,對于...

    EdwardUp 評論0 收藏0
  • 步一搭建前端監(jiān)控系統(tǒng):接口請求異常監(jiān)控

    摘要:參考一步一步搭建前端監(jiān)控系統(tǒng)錯誤監(jiān)控篇用插件記錄網(wǎng)絡(luò)請求異常關(guān)于專注于微信小程序微信小游戲支付寶小程序和線上應(yīng)用實時監(jiān)控。 摘要: 如何監(jiān)控HTTP請求錯誤? 作者:一步一個腳印一個坑 原文:搭建前端監(jiān)控系統(tǒng)(四)接口請求異常監(jiān)控篇 Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。 背景:市面上的監(jiān)控系統(tǒng)有很多,大多收費,對于小型前端項目來說,必然是痛點。另一點主要原因是,功能雖然...

    dreamtecher 評論0 收藏0
  • 前端之從零開始系列

    摘要:只有動手,你才能真的理解作者的構(gòu)思的巧妙只有動手,你才能真正掌握一門技術(shù)持續(xù)更新中項目地址求求求源碼系列跟一起學(xué)如何寫函數(shù)庫中高級前端面試手寫代碼無敵秘籍如何用不到行代碼寫一款屬于自己的類庫原理講解實現(xiàn)一個對象遵循規(guī)范實戰(zhàn)手摸手,帶你用擼 Do it yourself!!! 只有動手,你才能真的理解作者的構(gòu)思的巧妙 只有動手,你才能真正掌握一門技術(shù) 持續(xù)更新中…… 項目地址 https...

    Youngdze 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<