现在的前端开发已不再是刀耕火种的年代了,各种框架、编译工具层出不穷,前端监控系统也不甘其后,遍地开花。

前端正承受着越来越重的职责,前端的业务也变得越来越复杂,此时此刻我们就更需要一套完善的监控系统来为我们的线上应用保驾护航。

但是,想在众多的监控系统挑出一个趁手的,还真不是一件容易的事。不过徒手撕一个前端监控系统,好像也绝非那么容易。今天我们就以Webfunny前端监控为基础,来说一下前端监控最核心的部分,Js错误监控。

推广:(官网:www.webfunny.cn 、Git:webfunny_monitor  我们致力于解决前端开发痛点,为前端开发量身订做)

监控流程:监控并收集错误 -> 存储并上报错误 -> 分析并聚合错误 -> 发送错误报警-> 定位并解决JS错误

一、监控并收集Javascript错误

众所周知,我们是有办法去监听前端Js错误的,他们分别 window.onerror、window.onunhandledrejection、console.error方法。

通过这些方法能够为我们记录下线上的运行时错误,以及详细的堆栈信息。我将window.onerror(捕获异常),console.error(自定义异常)方法收集到的错误信息进行分析统计后的效果如下:

(1)重写 window.onerror 方法

// 重写 onerror 进行jsError的监听
window.onerror = function(errorMsg, url, lineNumber, columnNumber, errorObj) {
var errorStack = errorObj ? errorObj.stack : null;
siftAndMakeUpMessage("on_error", errorMsg, url, lineNumber, columnNumber, errorStack);
};

window.onerror 方法以及它的参数我就不一一介绍了,我相信大家也已经耳熟能详了;我们记录下错误发生时的行、列号,以及错误堆栈。

(2)重写 window.onunhandledrejection 方法

window.onunhandledrejection = function(e) {
var errorMsg = "";
var errorStack = "";
if (typeof e.reason === "object") {
errorMsg = e.reason.message;
errorStack = e.reason.stack;
} else {
errorMsg = e.reason;
errorStack = "";
}
// 分类解析
siftAndMakeUpMessage("on_error", errorMsg, WEB_LOCATION, 0, 0, "UncaughtInPromiseError: " + errorStack);
}

window.onunhandledrejection 能够捕获到Promise未处理的rejection异常,rejection异常并不会阻断页面运行,容易被很多小伙伴所遗忘,所以我们监控了此类型的错误。

(3)重写 console.error 方法

// 重写console.error, 可以捕获更全面的报错信息
var oldError = console.error;
console.error = function (tempErrorMsg) {
var errorMsg = (arguments[0] && arguments[0].message) || tempErrorMsg;
var lineNumber = 0;
var columnNumber = 0;
var errorObj = arguments[0] && arguments[0].stack;
if (!errorObj) {
if (typeof errorMsg == "object") {
try {
errorMsg = JSON.stringify(errorMsg)
} catch(e) {
errorMsg = "错误无法解析"
}
}
siftAndMakeUpMessage("console_error", errorMsg, WEB_LOCATION, lineNumber, columnNumber, "CustomizeError: " + errorMsg);
} else {
// 如果报错中包含错误堆栈,可以认为是JS报错,而非自定义报错
siftAndMakeUpMessage("on_error", errorMsg, WEB_LOCATION, lineNumber, columnNumber, errorObj);
}
return oldError.apply(console, arguments);
};

console.error 是用来打印警告日志,所以我将其归类为自定义异常。一般像前端框架、引入第三方的插件都会用 console.error 来打印较为严重的警告信息,而我在工作中也会将后台抛出的错误信息(非后台异常)用console.error打印出来,上报到监控系统里。这样对排查异常也是有很大作用的(这一点会在行为记录查询中有体现)。

二、存储并上报错误

Javascript错误产生后,应该存入浏览器的缓存中,然后定时上传,如果实时上传,将会对服务器造成压力。通过接口将Js错误信息上传到服务器,由后台server对数据进行清洗分类,然后再进行持久化存储。因为我用的是mysql来存储日志信息,所以需要以JS错误为一个model,明确定义Js错误的每个字段,定义如下:

  // 设置日志对象类的通用属性
function setCommonProperty() {
this.wmVersion = WM_VERSION; // 探针版本号
this.happenTime = new Date().getTime(); // 日志发生时间
this.webMonitorId = WEB_MONITOR_ID; // 用于区分应用的唯一标识(一个项目对应一个)
this.simpleUrl = window.location.href.split('?')[0].replace('#', ''); // 页面的url
this.completeUrl = utils.b64EncodeUnicode(encodeURIComponent(window.location.href)); // 页面的完整url
this.customerKey = utils.getCustomerKey(); // 用于区分用户,所对应唯一的标识,清理本地数据后失效,
// 用户自定义信息, 由开发者主动传入, 便于对线上问题进行准确定位
var wmUserInfo = lsg.wmUserInfo ? JSON.parse(lsg.wmUserInfo) : {};
this.userId = wmUserInfo.userId;
this.firstUserParam = utils.b64EncodeUnicode(wmUserInfo.firstUserParam || "");
this.secondUserParam = utils.b64EncodeUnicode(wmUserInfo.secondUserParam || "");
}
// JS错误日志,继承于日志基类MonitorBaseInfo
function JavaScriptErrorInfo(uploadType, infoType, errorMsg, errorStack) {
setCommonProperty.apply(this);
this.uploadType = uploadType;
this.infoType = infoType;
this.pageKey = utils.getPageKey(); // 用于区分页面,所对应唯一的标识,每个新页面对应一个值
this.deviceName = DEVICE_INFO.deviceName;
this.os = DEVICE_INFO.os + (DEVICE_INFO.osVersion ? " " + DEVICE_INFO.osVersion : "");
this.browserName = DEVICE_INFO.browserName;
this.browserVersion = DEVICE_INFO.browserVersion;
// TODO 位置信息, 待处理
this.monitorIp = utils.getCookie("webfunny_ip"); // 用户的IP地址
this.country = "china"; // 用户所在国家
this.province = ""; // 用户所在省份
this.city = ""; // 用户所在城市
this.errorMessage = utils.b64EncodeUnicode(errorMsg)
this.errorStack = utils.b64EncodeUnicode(errorStack);
this.browserInfo = "";
}

Js错误信息需要包含系统版本号、应用版本号、平台信息、页面Url、错误信息、错误堆栈、发生时间等等,这样才能帮助我们准确定位,至于数据库的字段如何定义,我就不赘述了,可以访问我的git项目查看。

三、分析并聚合错误

如果每天都去盯着前端的报错数据,真的很耗费精力,而且很难看出是今天发生的,还是一直存在的报错。

其实前端项目每天都会有些报错,比如:script error 。我们既不能控制,也不会影响我们的业务,但它会一直存在。

只要每天的错误量没有波动太大,报错数据比较平稳,就可以认为线上应用是健康的。所以我选择跟一周前的数据进行比较,如果出现大幅上升,那么就需要对这个项目进行关注了,而不是每天查看具体的报错数据。

本文上部的健康状况看板图片就是为了表达这种想法,截图上正是前端发了严重的异常,而出现的曲线图。

那我们来看看如何对这些错误进行聚合,且看下Webfunny错误聚合的效果:

首先,我们对捕获的异常类型进行了分类(TypeError、ReferenceError、UncaughtInPromiseError),这样错误类型可以一目了然。

同时我们对发生错误的操作系统(Android、ios、Pc)进行了分类统计,比如截图中的第一个错误,就只会在苹果手机上发生,排查范围也就缩小了很多。

另外,我们把错误影响的人数也统计出来,就可以知道这个错误影响了多少用户,从而确定修复的优先级。

四、发送错误报警

这一步属于监控的附加功能,主要包括邮箱、钉钉、短信等消息通知,和本次讲得知识点无关,我就不细说了。

五、如何定位并解决JS错误

针对某一个错误,我们需要分析它发生的平台,影响的人数,系统版本,网络环境等等,同时也需要分析最为重要的一步,就是代码的位置。

如图所示

首先,我们分析了错误发生的具体时间、发生次数、影响人数、IP地址、浏览器版本、操作系统等环境因素

其次,我们还需统计这个报错发生的时间曲线,如果是大量报错,我们可以很容易定位到错误发生的起始点,针对那个时间点,对报错的原因进行定位

还有一个重要的点,就是对代码代码堆栈的分析。我们默认会根据错误提示的行、列号截取错误位置附近的一段代码,正常情况下,我们已经可以通过这段代码来定位出出错的具体位置了。

但是有些小伙伴说,我就是看不出来怎么办,没关系,我们还提供了利用Js的SourceMap文件反向定位源码的功能,让你准确定位到Js源码的位置。

PS:由于SourceMap文件反向定位源码的功能较为复杂,我将放到下一个知识分享中进行讲解。

最后,我们将Js错误结合到用户的行为记录中,这样我们就能够知道用户在发生错误的前后都做了什么,更进一步的了解错误发生原因,错误详情页提供了查看行为轨迹的按钮,我们来看看结果。

好了,说了这么多方法,对Js错误的监控和解决方法已经不再是什么难事了。

Webfunny知识分享:JS错误监控的更多相关文章

  1. 搭建前端监控系统(二)JS错误监控篇

    ===================================================================== 前端性能监控系统: DEMO地址    GIT代码仓库地址 ...

  2. 前端监控系列2 |聊聊 JS 错误监控那些事儿

    作者:彭莉,火山引擎 APM 研发工程师.2020年加入字节,负责前端监控 SDK 的开发维护.平台数据消费的探索和落地. 有必要针对 JS 错误做监控吗? 我们可以先假设不对 JS 错误做监控,试想 ...

  3. Fundebug上线Node.js错误监控啦

    作为全栈JavaScript错误实时监测平台,Fundebug的Node.js实时错误监测服务上线啦,我们能够帮助开发者及时,高效地发现并且解决Node.js错误,从而提高开发效率,并提升用户体验. ...

  4. Webfunny知识分享:webpack sourceMap解析源码

    前端的业务越来越庞大,导致我们需要引入的js等静态资源文件的体积也越来越大,不得不使用压缩js文件的方式来提高加载的效率. 编译工具的诞生,极大地方便了我们处理js文件的这一过程,但压缩后的js文件极 ...

  5. 前端监控系统(二)JS错误日志收集篇

    前端监控系统 目前已经上线,欢迎使用! 服务器搭建好了,可以着手开发了. 其实前端需要分析的数据有很多,包括,PVUV, 接口请求统计,耗时统计,JS错误统计,用户使用设备统计,用户地域分布,页面用户 ...

  6. Vue.js@2.6.10更新内置错误处机制,Fundebug同步支持相应错误监控

    摘要: Fundebug 的 JavaScript 错误监控插件同步支持 Vue.js 异步错误监控. Vue.js 从诞生至今已经 5 年,尤大在今年 2 月份发布了重大更新,即Vue 2.6.更新 ...

  7. 腾讯AlloyTeam发布AlloyLever - 开发调试发布错误监控上报用户问题定位尽在1kb代码

    AlloyLever [官网][Giuhub] 1kb(gzip)代码搞定开发调试发布,错误监控上报,用户问题定位. 支持错误监控和上报 支持 vConsole错误展示 支持开发阶段使用 vConso ...

  8. 读书笔记(04) - 错误监控 - JavaScript高级程序设计

    错误类型 即时运行错误 (代码错误) 资源加载错误 常见的错误 1. 类型转换错误 建议使用全等===操作符 2.数据类型错误 建议加强类型判断 // 数组倒序 function reverseSor ...

  9. 前端分享----JS异步编程+ES6箭头函数

    前端分享----JS异步编程+ES6箭头函数 ##概述Javascript语言的执行环境是"单线程"(single thread).所谓"单线程",就是指一次只 ...

随机推荐

  1. Centos7安装Docker1.9.1

    1.先检查是否安装旧版本docker [root@registry ~]# rpm -qa|grep docker 我这里没有安装,如果安装,请rpm -e 卸载2.编辑docker.repo文件,写 ...

  2. HTML自动刷新页面

    <meta http-equiv="refresh"content="5"/> 英文""

  3. acwing 173. 矩阵距离(bfs)

    给定一个N行M列的01矩阵A,A[i][j] 与 A[k][l] 之间的曼哈顿距离定义为: dist(A[i][j],A[k][l])=|i−k|+|j−l|dist(A[i][j],A[k][l]) ...

  4. 记一次针对静态页面的DDOS基本防护

    可以说是我试图进入安全口的天才第一步了,能走多远鬼知道呢 背景 去年年前接到的一个外包项目,是一个base在日本的中国人留学机构做的静态页面.出于锻炼自己的目的,选择为他们按次结薪做长期服务维护.20 ...

  5. cmd 安装第三方库问题

    pip install 包名 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com 一定要指定 信任豆瓣源,不然就算换了源 ...

  6. Django学习路19_is_delete属性,重写类方法,显性隐性属性

    如果在 创建数据表时,使用了 objects = models.Model() 使隐形属性变为了 显性属性 则 必须要自己定义一个 继承了 models.Model 类的类,实现 管理功能 如果一个属 ...

  7. Seaborn基础2

    import matplotlib.pyplot as plt import seaborn as sns import numpy as np def sinplot(flip = 1): x = ...

  8. luogu P3761 [TJOI2017]城市 树的直径 bfs

    LINK:城市 谢邀,学弟说的一道毒瘤题. 没有真正的省选题目毒瘤 或者说 写O(n)的做法确实毒瘤. 这里给一个花20min就写完的非常好写的暴力. 容易想到枚举哪条边删掉 删掉之后考虑在哪两个点上 ...

  9. luogu P4726 【模板】多项式指数函数 多项式 exp 牛顿迭代 泰勒展开

    LINK:多项式 exp 做多项式的题 简直在嗑药. 前置只是 泰勒展开 这个东西用于 对于一个函数f(x) 我们不好得到 其在x处的取值. 所以另外设一个函数g(x) 来在x点处无限逼近f(x). ...

  10. 如何在 Android 安卓手机上运行 Node.js 应用 | 如何在安卓手机配置node开发环境

    最近在学习js,由于没法随身携带笔记本电脑,在手机翻阅<JavaScript高级程序设计>时,时常想调试一下书中的代码.于是就有了,能否在手机上配置一个js开发环境.考虑到手机上的Chro ...