Performance — 前端性能監控利器 使用示例
轉載 https://blog.csdn.net/blueblueskyhua/article/details/78830692
Performance 方法詳解 可點選此處檢視
還記得在我上一家公司中,某一大佬做了一個監控系統,牛逼哄哄,挺想研究他到底是怎麼搞出來的。當然我們也不是拍拍腦袋幹活的人,總不能人家咋幹我們就咋幹。下面先就介紹下,這樣的平台到底有啥好處。
背景
首先我們為什麼要做前端系統呢,先看下面這張表,可以很顯然的看出,前端的性能對于産品的價值提升還是蠻有幫助的,但是這些資訊如果我們能實時的采集到,并且實施以監控,讓整個産品在産品線上一直保持高效的運作,這才是我們的目的。
性能 | 收益 |
---|---|
Google 延遲 400ms | 搜尋量下降 0.59% |
Bing 延遲 2s | 收入下降 4.3% |
Yahoo 延遲 400ms | 流量下降 5-9% |
Mozilla 頁面打開減少 2.2s | 下載下傳量提升 15.4% |
Netflix 開啟 Gzip | 性能提升 13.25% 帶寬減少50% |
其次,也有利于我們釋出的産品,能夠及時發現我們的錯誤。如果一個産品在新的疊代中,發生不可描述的錯誤。
對!就是不可描述。我們總不可能等待使用者的回報投訴,到那個時候黃花菜都涼了。
開始
基于以上我們就開始搭建一個前端監簡易控平台。(雖然現在市面上有很多這樣的系統比如ELK,但是還是忍不住自己撸一個)
隻能是簡易了。
兄弟們原諒我,隻能幫你們到這裡了。
接下來請看。
以上是我們需要做的一些事情。
收集資訊
要做監控系統,首先我們得有一個對象。我們監控的對象!對象!對象!對象。
我在我的系統寫了一個這樣的頁面,
<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 i 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 i 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 i 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把所有的請求處理并都記錄下來,記錄好後的資料是這樣子的。
1
資料處理
這裡我是通過自己寫的一段腳本進行解析,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
具體的配置我不進行講解,可以自己到官網進行檢視。
下面是可視化的圖表,顯示的是每天各個時間段的資訊。
https://user-gold-cdn.xitu.io/2017/12/17/16062ce35649b7e7?w=861&h=1595&f=png&s=83479
界面可能不是特别美觀,還請見諒。
https://user-gold-cdn.xitu.io/2017/12/17/16062ce3539eb419?w=237&h=234&f=gif&s=561618
環境
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的效果起見。一般我采用的是一分鐘進行一次存儲。
github位址:https://github.com/hua1995116/mcharts
如有好的建議以及優化的方案,還請各位在Issues上提給我,喜歡的話可以點個star。
進階(一個利用監控平台的實戰栗子)
我利用這個平台對我的一個項目進行了監控。如果你隻是純粹玩的話,還請隻閱讀上面的原系統位址,可以忽視我這一段,畢竟我這個系統還不夠完善。
項目線上位址:http://www.qiufengh.com/#/
項目監控位址:http://qiufengh.com:8080/
此項目代碼位址:https://github.com/hua1995116/webchat/tree/monitoring
在這裡我設定了每過1分鐘記錄一次日志。
// 監控引入
app.use(performance({
time: 60, // 秒為機關
originalDir: './originalData', // 資料的目錄
errorDir: './errorData' // 報錯的目錄
}))
以及每隔10分鐘進行一次解析。
function setPrase() {
setInterval(function(){
parseData();
}, 1000 * 60 * 10);
}
原文連結: http://yifenghua.win/html/2017-12-16.html (更完美的閱讀體驗哦)