一步一步搭建前端监控系统:如何记录用户行为?
摘要: 通過記錄用戶行為,快速復現BUG場景。
- 作者:一步一個腳印一個坑
- 原文:搭建前端監控系統(備選)用戶行為統計和監控篇(如何快速定位線上問題)
Fundebug經授權轉載,版權歸原作者所有。
一步一步搭建前端監控系統系列博客:
- 一步一步搭建前端監控系統:JS錯誤監控篇
- 一步一步搭建前端監控系統:如何將網頁截圖上報?
- 一步一步搭建前端監控系統:接口請求異常監控篇
- 一步一步搭建前端監控系統:如何定位前端線上問題?
- 一步一步搭建前端監控系統:如何記錄用戶行為?
背景: 市面上的監控系統有很多,大多收費,對于小型前端項目來說,必然是痛點。另一點主要原因是,功能雖然通用,卻未必能夠滿足我們自己的需求, 所以我們自給自足也許是個不錯的辦法。
這是搭建前端監控系統的第二章,主要是介紹如何統計js報錯,跟著我一步步做,你也能搭建出一個屬于自己的前端監控系統。
目前已經在運行的線上Demo:前端監控系統
代碼和講解都放在這篇文章里:監控系統介紹及代碼
如果實在嫌部署麻煩,Demo系統可以提供 7天 的監控量,我會長期維護:一鍵部署
一直以來, 前端上線的項目,對于前端程序猿來說,完全是一個黑盒子。 項目一旦上線,我們完全不知道用戶在我們的項目里邊做了什么,跳轉到哪里,是不是報錯了。一旦線上用戶出現問題,而我們又無法復現的時候,才能體會到什么叫絕望。 不管多么艱難,問題總是會在哪里等著你。所以,如果我們可以把線上的項目變成一個白盒子,讓我們能夠知道用戶在線上干了什么,復現不再困難了,對前端程序員來說,是不是一件好事呢。
接下來我要寫的是一個重要的功能, 因為它極大的提高了我解決問題的能力, 也讓對我的工作產生了很大的影響。
截止到現在,來看看我已經完成了哪些功能:
PV/UV的統計上報,js錯誤的上報和分析, 接口的統計上報,頁面截屏的統計上報。 那么,再補上今天要寫的“用戶點擊行為的上報”, 我們基本上就能夠分析出一個用戶在頁面上干了什么。
一、如何記錄線上用戶的行為
線上用戶的基本行為包括: 訪問頁面, 點擊行為,請求接口行為, js報錯行為, 這幾點基本上能夠清楚的記錄下用戶在線上的所有行為。 當然還包括:資源加載行為,滾動頁面行為, 元素進入用戶視野等等行為,這些是更為細節的行為統計, 也許會在以后進行完善, 但是以上的四種行為已經可以完成我們的統計需求。
訪問頁面, js報錯行為我們已經有了,接下來看看如何統計點擊行為和請求接口的行為吧。
點擊行為
// 用戶行為日志,繼承于日志基類MonitorBaseInfofunction 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等等,我們需要足夠的的內容才能夠確定用戶點擊的是哪個按鈕。這種方式比較弱智,將會在以后寫留存分析功能的時候進行完善一下,但是目前足以滿足我們的要求了。
請求接口行為
// 接口請求日志,繼承于日志基類MonitorBaseInfofunction 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代碼, 這樣就可以生效了。好了,這部分并不是這篇幅的重點,我們就說到這里。
二、如何查詢線上用戶的行為
終于,我們把剩下的兩種行為記錄都成功上傳了,那么該如果把他們都查詢出來呢。我們先來看一下頁面上我查詢出來的結果。
因為屏幕太小,無法展示所有的記錄,記錄信息包含:行為名稱,行為發生時間, 行為發生頁面, 錯誤信息, 錯誤截圖, 以及用戶自定義上傳截圖的時機。
說到這里有幾個小問題需要注意。
- 因為是用Js做探針,記錄日志的時候很難保證每次記錄都可以把用戶的userId插入進去
- 所以我們給每個用戶都定義一個customerKey來做區分,如果用戶不卸載app和清理app的緩存, customerKey將保持不變
- 在查詢用戶的行為記錄的時候,需要先查詢出用戶所有的customerKey(可能有多個),再用customerKey進行查詢,便可以得到準確的結果。
三、如何分析線上用戶的行為
其實我們做了這么多,記錄了這么多,就是為了這個目的:分析行為,快速定位問題。
那么我們如何定位問題呢,我可以舉例說明一下:
- JS報錯阻斷行為,我們可以看到發生錯誤的前后行為,就能夠快速準確定位問題。
- 復雜的鏈接跳轉發生了錯誤。有些錯誤是前端頁面會經過復雜的跳轉,回退之后才發生的,就算測試人員也很難測試出這種問題,因為線上的用戶的任何行為都有可能出現。往往我們知道的只是他在最后停留的頁面發生了錯誤。 如此,經過我們排查行為日志, 就能夠復現出用戶的行為, 從而復現BUG
- 接口異常。 正常情況下,前端的接口都會設置超時時間的, 但是呢, 后臺接口排查發現正常, 而前端就是無法正常執行, 這種問題沒有顯示的錯誤現象,而線上的反饋并不能夠準確,前端只能背鍋了。 而日志記錄是可以把請求發出時間和返回時間記錄下來, 是否超時,看一眼就知道。
- 線上的用戶根本就不會反饋異常, 他們能做的只是把最后一眼能看到的東西告訴你。 天知道他們之前經歷了什么步驟。 最終的結果是,前端有問題,然后背鍋,哈哈。
總之, 我們知道用戶在頁面上干了什么, 便不再擔心問題出現, 遇見問題也不會再手忙腳亂了。
關于Fundebug
Fundebug專注于JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有陽光保險、核桃編程、荔枝FM、掌門1對1、微脈、青團社等眾多品牌企業。歡迎大家免費試用!
總結
以上是生活随笔為你收集整理的一步一步搭建前端监控系统:如何记录用户行为?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对 torch 中 dim 的总结和理解
- 下一篇: gae代码_GAE中的Java EE