如何搭建一套前端监控系统
日志采集
信息分类
日常手机的日志信息大概分如下几个类别:
- 错误
错误类别 | 捕获方法 | 表现形式 | 发生概率 |
---|---|---|---|
运行错误 | 全局监听error | ||
类型错误 | 全局监听error | ||
引用错误 | 全局监听error | const a = null; a.b.c = 123; | 频繁 |
接口错误 | 设置ajax和fetch的全量代理 | 根据不同的公司的业务制定 | 频繁 |
网络错误 | 设置ajax和fetch的全量代理 | 捕获http status 4xx、 5xx | 经常 |
资源加载错误 | 全局监听error | http 404 500 timeout | 较少 |
RangeError | 全局监听error | ||
语法错误 | 全局监听error | 语法错误:cont a = "123"; | 较少 |
TypeError | |||
URIError | |||
AggregateError | |||
EvalError |
- 性能
window.performance
中的各个参数,计算加载的速度,同时也可以通过performanceOberser
中获取FP FCP 指标从而评估首屏加载的速度。而对于运行中的性能问题,我们能做的较少。一般采用嵌入式埋点,通过对比两个时间的差值来判断某段js造成的性能问题。同时之前performanceOberserver
能够实时监控长任务的执行频率,因此也是总要的工具之一。最后,我们也可以通过mutationOberser
来判断界面元素的变化来判断界面的性能问题。- 行为
document
,然后在标签上做标记的处理。最后将用户的行为发送到服务端。自定义
- 敏感
- 环境
- 身份
- 业务
上报方式 我们一般用一个像素的透明gif来做数据上报,因为这种方式能最大的较少数据量的发送。而且采用图片资源做传输不需要考虑跨域的问题。很多公司的上报功能就是采用此种方式进行的数据上报的,百度,友盟,阿里等。不过有些场景这种方式无法适应,在统计之后发送了定向跳转行为,因为界面被打断了。为了兼容此种情况,我们可以考虑用navigator.sendBeacon的方式进行上报。
sendbeaon
方式可以在用户甚至关闭界面的后还可以继续发送请求。采用的应该是serviceworker
来进行进行底层操作的。但是因为要考虑兼容性问题,我们可以做一个数据回退。将发送方法进行降级处理。上报频率 如果每个行为或者错误都进行上报的话会对服务器和数据库造成极大的压力。我们可以通过制定一些规则来控制上报的频率。根据优先级的不同从而制定不同的发送频率。当然,这就涉及到客户端的存储,既然选择了不同的瓶绿,势必有些数据需要停留在客户端一段时间,等待发送的时机。下面这张图作为你发送数据的依据作为参考:
数据分类 | 重要性 | 体积 | 效时比 | 存储方式 | 上报频率 |
---|---|---|---|---|---|
错误信息 | 非常重要 | 少 | 10 | 无 | 及时发送 |
性能信息 | 重要 | 少 | 3 | indexdb | 按照版本发布的频率 |
用户信息 | 一般 | 多 | 3 | indexdb | 根据业务需求自定义即可 |
自定义 | 一般 | 较多 | 2 | indexdb | 根据业务有限级别 |
身份信息 | 重要 | 少 | 10 | cookie | 发送一次 |
会话信息 | 重要 | 少 | 10 | session | 以天为单位发送 |
业务信息 | 一般 | 较少 | 8 | indexdb | 根据业务需求自定 |
数据关联 信息收集的基本类型需要符合一个大原则即:谁在某什么时候做了什么,以及这么的原因。总结就是3w1h(who what when how);通过一条信息去判断用户的行为无疑是以管窥豹。数据发生既有历史现在,我们可以预测未来。场景复现一般都是线上报错后手忙脚乱的程序员最先需要找到的。有些错误信息并不是避险,所以需要模拟常出现的场景才能定位问题。这种工作问客服或者反馈的用户是非常低效和不明治的。我们需要自己建立数据关联性并且分析历史错误问题,同时也能帮助产品运营同时预测用户的行为。
- 建立
traceid
,每次用户进来都需要一个本次表示,以标示用户的进程如过程的一个总流程。一般用sessionstoreage
来存储,以用户同源的页面操作为分区。建立traceid能够帮助我们在用户纬度继续细分操作场景。 timerid
这个相当于给用户的的行为打上一个时间点,告诉信息的阅读着这个信息在本次行为中的先后顺序,当需要定位红一个时,我们可以推倒出上下问。
traceid
和timerid
我们就能准确的了解一个行为的上下文,这对于错误定位来说非常合适。我们上文提到了用户的操作会被客户端存储,这就会造成错误无法被即使还原。这可以通过错误发生机制,一旦某次操作发生了错误,我们强制把缓存中的对应信息连同错误信息一并发出。避免错误上下文被缓存的问题。
日志存储
存储类别 日志存储的架构设计可以分多种,一种是服务端的,一种是客户端的。
服务端主宰的是日志表系统,通过
nginx
网关把所有的http
记录下来,然后定期的通过消息系统去日志标中读取相应的日志信息。这种方式是做到快速即时,不会因为客户端的某些清除缓存操作而丢失信息。不足是前端开发人员需要做更多的提取和存储操作。而且每条信息都发送给服务器的话势必操作服务器的压力。在性能不高的服务架构上去做要比较困难。客户端主宰的方式如上文提到的,我们利用每台客户机器做日志的存储。相当于是把服务器的压力分个用户的机器上。客户端的好处显而易见,就是减少服务器眼里,根据不同的数据优先级定时定量的进行http发送。不过它的弊端就是无法作答永久存储很多缓存可能会被用户无意的清除调。而且也会存在数据不及时的问题,还有就是你并不会知道,下次用户的这些信息会被发送上来(说不动这个用户只会用一次你的应用也说不准)
你需要根据你自己的业务来决定哪种业务更适合你或者你的公司。或者你可以两者结合使用。来满足你对数据的实施性
数据库 统计信息分类复杂,数量众多。根据这些维度,你可以选择不同数据库进行存储。在存储的时候需要考虑建设合理的表结构以及索引,以提高数据的使用的效率。另外,并非所有的数据需要进行存储,一类数据可以存储在redis中,例如已经大多数的错误都会被解决,这些问题就无需被存储到数据库中占用资源。解决之后可以将其从redis中删除。在处理多种复杂数据的时候最好按照一定的分类程度存储到标中,例如,按年/月/日存储数据可以有效分类,或者省市区都可以有效的归纳和搜索信息。
统计分析
数据搜索 在海量的数据中查找相关的数据需要合理的分布。前面已经讲过通过建立索引,分表等操作减少数据查找的时间,提高响应速度。一般小数量的规模通过sql直接查询不会有太大的问题。大规模数据可应用应用在监控领域比较有代表性的有 时序数据库 OpenTSDB 和 全文检索搜索引擎 Elasticsearch 。
平台建设 搭建统一的监控平台,采用
nodejs
作为这种平台最为合适。一时前端开发人员较容易上手,其次是node搭建这种平台又快速的优势。利用express
或者koa
等web框架快速进行搭建。对于大数据来说,可视化无疑更符合人的直觉。因此,采用JavaScript 3d动画库或者利用python
的类库生成大数据屏幕是非常棒的选择。数据搜索分析是一项浩大的工程,需要深入去研究,此处一笔带过。
监控告警
错误一旦发生,是要立即同时相关人员去定位的。我们在设计监控系统中的一个重大原因就是需要监控并且解决错误。因此,我们需要做一些规则来设定推送告警。
- 导致页面无法正常运行的错误
- 重要的数据达到设定的阈值
- 在一定时间内的某个条件的触发频率较高
- 信息中包含某个设定的关键词时
快速定位
有了错误信息,我们就能即时知道线上出现的问题。但我们上传的脚本都是经过编译压缩的,而且大多数适合为了提升性能,我们一般不会将map文件放到线上去,如何定位问题就成了关键所在。以下是线上代码出现问题时在控制台或者日志中记录的错误:
如果单只看错误信息,是无法定位到错误的。这时候我们需要用到sourcemap来还原错误发生的位置。在node的第三方模块中,有一个
source-map
, 它提供为我们还原源代码提供了。 我们把这个包下载下来。npm install source-map
然后通过错误信息中的行号和列号对信息进行还原,我把还原的功能封装成了一个小模块,这个函数接受三个参数:string 原map文件,int 错误行号,int 错误列号。
const sourceMap = require('source-map');
const readline = require('readline');
const fs = require('fs');
sourceMap.SourceMapConsumer.initialize(
{"lib/mappings.wasm": "https://unpkg.com/source-map@0.7.3/lib/mappings.wasm"}
);
module.exports = function(rawSourceMap, line, column) {
return new Promise((reslove, reject) => {
let errorContent = [];
sourceMap.SourceMapConsumer.with(rawSourceMap, null, consumer => {
let originalPosition = consumer.originalPositionFor({
source: "./",
line: +line,
column: +column
});
const sourceContent =consumer.sourceContentFor(originalPosition.source);
const all = sourceContent.split(/\r\n/g);
originalPosition.content = all[originalPosition.line - 1];
reslove(originalPosition)
});
})}
通过这个模块还原出来的就是你的原代码。我们实际上只需要看到出问题的前后几行,因此,我在函数中使用了文本截断的方式,对外只输出发生错误的上下若干行的信息。你可以这样引用此函数:
const buffer = fs.readFileSync(files[0]);
const mp = sourceMap(String(buffer), result.lineNo, result.columnNo);
第一行去读取的文件就是sourcemap原文件,你每次打包后就需要把这些后缀为.sourcemap的文件传入到服务器上去,这样才能在出错的适合找到对于的源文件,进行快速还原。
为了帮助你快速实现sourcemap文件上传,我写了一个file-ziper-and-uploader的webpack插件,实现自动动上传文件到服务器去。相关文档请在到此,我们就可以把一个线上错误快速还原定位。从报错到定位源码,不会超过一分钟,可以说是非常的迅速。npm
上查看。
监控框架
【如何搭建一套前端监控系统】很多事情不必重复造轮子,业界又很多优秀的开源框架提供给我们使用。如果你不是在大厂,我不建议你从零开始搭建前端监控系统。我自己也是在有了一次手动造轮子的经历之后,最终采用sentry搭建的前端监控系统。结果是真香。
sentry
是开源的第三方框架,功能丰富,部署简单,最重要的是免费。betterjs
腾讯的前端监控方案fundebug
第三方监控框架,收费fee
贝壳网的前度监控系统,已经开源。免费- 岳鹰监控 阿里前端监控,收费
推荐阅读
- 考研英语阅读终极解决方案——阅读理解如何巧拿高分
- 如何寻找情感问答App的分析切入点
- mybatisplus如何在xml的连表查询中使用queryWrapper
- MybatisPlus使用queryWrapper如何实现复杂查询
- 六步搭建ES6语法环境
- 定制一套英文学习方案
- 如何在Mac中的文件选择框中打开系统隐藏文件夹
- 漫画初学者如何学习漫画背景的透视画法(这篇教程请收藏好了!)
- java中如何实现重建二叉树
- Linux下面如何查看tomcat已经使用多少线程