一個簡單的前端可視化監控系統


背景

首先我們為什么要做前端系統呢,先看下面這張表,可以很顯然的看出,前端的性能對於產品的價值提升還是蠻有幫助的,但是這些信息如果我們能實時的采集到,並且實施以監控,讓整個產品在產品線上一直保持高效的運作,這才是我們的目的。

性能
收益
Google 延遲 400ms 搜索量下降 0.59%
Bing 延遲 2s 收入下降 4.3%
Yahoo 延遲 400ms 流量下降 5-9%
Mozilla 頁面打開減少 2.2s 下載量提升 15.4%
Netflix 開啟 Gzip 性能提升 13.25% 帶寬減少50%

其次,也有利於我們發布的產品,能夠及時發現我們的錯誤。如果一個產品在新的迭代中,發生不可描述的錯誤。

開始

以上是我們需要做的一些事情。

要做監控系統,首先我們得有一個對象。我們監控的對象!對象!對象!對象。

我在我的系統寫了一個這樣的頁面,

<body> <div>2</div> <div>2</div> <div>2</div> <div>2</div> <div>2</div> <div>2</div> </body>

沒錯這就是我們要監控的頁面。這個.....真不是我懶。

然后接下來我一共設計了3塊數據

  • 頁面加載時間
  • 統計用戶使用設備
  • 錯誤量的統計

頁面加載時間

window.logInfo = {};   //統計頁面加載時間
window.logInfo.openTime = performance.timing.navigationStart;
window.logInfo.whiteScreenTime = + new  Date() - window.logInfo.openTime;
document.addEventListener( 'DOMContentLoaded' , function  (event) {
   window.logInfo.readyTime = + new  Date() - window.logInfo.openTime;
});
window.onload =  function  () {
   window.logInfo.allloadTime = + new  Date() - window.logInfo.openTime;
   window.logInfo.nowTime =  new  Date().getTime();
   var  timname = {
     whiteScreenTime:  '白屏時間' ,
     readyTime:  '用戶可操作時間' ,
     allloadTime:  '總下載時間' ,
     mobile:  '使用設備' ,
     nowTime:  '時間' ,
   };
   var  logStr =  '' ;
   for  ( var  in  timname) {
     console.warn(timname[i] +  ':'  + window.logInfo[i] +  'ms' );
     if  (i ===  'mobile' ) {
       logStr +=  '&'  + i +  '='  + window.logInfo[i];
     else  {
       logStr +=  '&'  + i +  '='  + window.logInfo[i];
     }
 
   }
   ( new  Image()).src =  '/action?'  + logStr;
};

統計用戶使用設備

window.logInfo.mobile = mobileType();
function  mobileType() {
   var  u = navigator.userAgent, app = navigator.appVersion;
   var  type =  { // 移動終端瀏覽器版本信息
     ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),  //ios終端
     iPad: u.indexOf( 'iPad' ) > -1,  //是否iPad
     android: u.indexOf( 'Android' ) > -1 || u.indexOf( 'Linux' ) > -1,  //android終端或者uc瀏覽器
     iPhone: u.indexOf( 'iPhone' ) > -1 || u.indexOf( 'Mac' ) > -1,  //是否為iPhone或者QQHD瀏覽器
     trident: u.indexOf( 'Trident' ) > -1,  //IE內核
     presto: u.indexOf( 'Presto' ) > -1,  //opera內核
     webKit: u.indexOf( 'AppleWebKit' ) > -1,  //蘋果、谷歌內核
     gecko: u.indexOf( 'Gecko' ) > -1 && u.indexOf( 'KHTML' ) == -1,  //火狐內核
     mobile: !!u.match(/AppleWebKit.*Mobile/i) || !!u.match(/MIDP|SymbianOS|NOKIA|SAMSUNG|LG|NEC|TCL|Alcatel|BIRD|DBTEL|Dopod|PHILIPS|HAIER|LENOVO|MOT-|Nokia|SonyEricsson|SIE-|Amoi|ZTE/),  //是否為移動終端
     webApp: u.indexOf( 'Safari' ) == -1  //是否web應該程序,沒有頭部與底部
   };
   var  lists = Object.keys(type);
   for ( var  i = 0; i < lists.length; i++) {
     if (type[lists[i]]) {
       return  lists[i];
     }
  
}

錯誤量的統計

window.onload =  function  () {
         window.logInfo.allloadTime = + new  Date() - window.logInfo.openTime;
         window.logInfo.nowTime =  new  Date().getTime();
         var  timname = {
             whiteScreenTime:  '白屏時間' ,
             readyTime:  '用戶可操作時間' ,
             allloadTime:  '總下載時間' ,
             mobile:  '使用設備' ,
             nowTime:  '時間' ,
         };
         var  logStr =  '' ;
         for  ( var  in  timname) {
             console.warn(timname[i] +  ':'  + window.logInfo[i] +  'ms' );
             if  (i ===  'mobile' ) {
                 logStr +=  '&'  + i +  '='  + window.logInfo[i];
             else  {
                 logStr +=  '&'  + i +  '='  + window.logInfo[i];
             }
             
         }
         ( new  Image()).src =  '/action?'  + logStr;
     };
       
     var  defaults = {
         msg: '' ,   // 錯誤的具體信息
         url: '' ,   // 錯誤所在的url
         line: '' // 錯誤所在的行
         col: '' ,   // 錯誤所在的列
         nowTime:  '' , // 時間
     };
     window.onerror =  function (msg,url,line,col,error) {
         col = col || (window.event && window.event.errorCharacter) || 0;
 
         defaults.url = url;
         defaults.line = line;
         defaults.col =  col;
         defaults.nowTime =  new  Date().getTime();
 
         if  (error && error.stack){
             // 如果瀏覽器有堆棧信息,直接使用
             defaults.msg = error.stack.toString();
 
         } else  if  (arguments.callee){
             // 嘗試通過callee拿堆棧信息
             var  ext = [];
             var  fn = arguments.callee.caller;
             var  floor = 3; 
             while  (fn && (--floor>0)) {
                 ext.push(fn.toString());
                 if  (fn  === fn.caller) {
                     break ;
                 }
                 fn = fn.caller;
             }
             ext = ext.join( "," );
             defaults.msg = error.stack.toString();
         }
         var  str =  ''
         for ( var  in  defaults) {
             // console.log(i,defaults[i]);
             if (defaults[i] ===  null  || defaults[i] === undefined) {
                 defaults[i] =  'null' ;
             }
             str +=  '&' + i +  '='  + defaults[i].toString();
         }
         srt = str.replace( '&' '' ).replace( '\n' , '' ).replace(/\s/g,  '' );
         ( new  Image()).src =  '/error?'  + srt;
     }

以上就是收集數據的全部,通過發送/action請求或者是/error請求,這些都是可以自定義的,我講的只是整個過程是如何實現的。

然后通過我的的一個后台express.js把所有的請求處理並都記錄下來,記錄好后的數據是這樣子的。

user_ip= 127.0 . 0.1 &whiteScreenTime= 185 &readyTime= 192 &allloadTime= 208 &mobile=webKit&nowTime= 1513071388941

數據處理

這里我是通過自己寫的一段腳本進行解析,parse.js,這里不具體講解,看源碼即可。我展現下解析好的數據。

我以cvs的數據格式儲存,因為后面圖表的需要,我也支持json格式方式導出,只不過后面就需要你自己來配置可視化的界面了。

數據是這樣的。

charts/csvData/2017-12-16time.csv

時間,白屏時間,用戶可操作時間,總下載時間
1513427051482 , 137 , 137 , 153
1513427065080 , 470 , 471 , 507
1513427080040 , 127 , 127 , 143
1513428714345 , 274 , 275 , 323
1513428733583 , 267 , 268 , 317
1513428743167 , 268 , 268 , 317
1513428754796 , 276 , 276 , 328

數據展示

這里我用的是highcharts.js

具體的配置我不進行講解,可以自己到官網進行查看。

下面是可視化的圖表,顯示的是每天各個時間段的信息。


環境

node >= 6.0.0

redis >= 2.6.0

在這里我說明下,因為如果這個部署在線上環境的時候,如果每次記錄都進行記錄的話,會消耗大量的內存,所以我架設了一層redis,為了防止大流量的沖擊,然后可以每隔一段時間進行存儲。

const express = require( 'express' );
const performance = require( './lib/performance.js' );
const app = express();
const router = express.Router();
router.get( '/' function  (req, res, next) {
   req.url =  './index.html' ;
   next();
});
app.use(router);
app.use(performance({
     time: 10,  // 秒為單位
     originalDir:  './originalData' // 數據的目錄
     errorDir:  './errorData'  // 報錯的目錄
}))
app.use(express.static( './' ));
const server = app.listen(3000)

這里可以設置默認的時間,我這里以10秒為單位,為了demo的效果起見。一般我采用的是一分鍾進行一次存儲。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM