还记得在我上一家公司中,某一大佬作了一个监控系统,牛逼哄哄,挺想研究他究竟是怎么搞出来的。固然咱们也不是拍拍脑壳干活的人,总不能人家咋干咱们就咋干。下面先就介绍下,这样的平台到底有啥好处。javascript
首先咱们为何要作前端系统呢,先看下面这张表,能够很显然的看出,前端的性能对于产品的价值提高仍是蛮有帮助的,可是这些信息若是咱们能实时的采集到,而且实施以监控,让整个产品在产品线上一直保持高效的运做,这才是咱们的目的。css
性能 | 收益 |
---|---|
Google 延迟 400ms | 搜索量降低 0.59% |
Bing 延迟 2s | 收入降低 4.3% |
Yahoo 延迟 400ms | 流量降低 5-9% |
Mozilla 页面打开减小 2.2s | 下载量提高 15.4% |
Netflix 开启 Gzip | 性能提高 13.25% 带宽减小50% |
其次,也有利于咱们发布的产品,可以及时发现咱们的错误。若是一个产品在新的迭代中,发生不可描述的错误。html
对!就是不可描述。咱们总不可能等待用户的反馈投诉,到那个时候黄花菜都凉了。前端
基于以上咱们就开始搭建一个前端监简易控平台。(虽然如今市面上有不少这样的系统好比ELK,可是仍是忍不住本身撸一个)java
只能是简易了。node
兄弟们原谅我,只能帮大家到这里了。android
接下来请看。ios
以上是咱们须要作的一些事情。git
要作监控系统,首先咱们得有一个对象。咱们监控的对象!对象!对象!对象。github
我在个人系统写了一个这样的页面,
<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把全部的请求处理并都记录下来,记录好后的数据是这样子的。
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的效果起见。通常我采用的是一分钟进行一次存储。
若有好的建议以及优化的方案,还请各位在Issues上提给我。
我利用这个平台对个人一个项目进行了监控。若是你只是纯粹玩的话,还请只阅读上面的原系统地址,能够忽视我这一段,毕竟我这个系统还不够完善。
线上demo:www.qiufengh.com/#/
监控demo:qiufengh.com:8080/
在这里我设置了每过1分钟记录一第二天志。
// 监控引入
app.use(performance({
time: 60, // 秒为单位
originalDir: './originalData', // 数据的目录
errorDir: './errorData' // 报错的目录
}))
复制代码
以及每隔10分钟进行一次解析。
function setPrase() {
setInterval(function(){
parseData();
}, 1000 * 60 * 10);
}
复制代码
2017-12-20
本文只是提供一个思路,若是想要专业的,可使用ELK之类专业的统计分析工具。