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

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

但是,想在众多的监控系统挑出一个趁手的,还真不是一件容易的事。不过徒手撕一个前端监控系统,好像也绝非那么容易。今天我们就以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. 大数据框架Hive优化方法

    常规调优手段 Fetch抓取 某些情况查询不必用MapReduce计算,比如select*,可以直接读取文件 本地模式 有时数据量比较小,hive可以通过本地模式在单台机器上处理所有任务,对于小数据集 ...

  2. 痞子衡嵌入式:恩智浦i.MX RTxxx系列MCU特性那些事(1)- 概览

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是恩智浦i.MX RTxxx系列MCU的基本特性. 恩智浦半导体于2017年开始推出的i.MX RT系列重新定义了MCU,其第一款芯片i. ...

  3. 将ip加入到防火墙

    五分钟内访问次数最多的ip加入到防火墙 #!/bin/bash cat /etc/httpd/logs/access_log|grep `date -d '1-minute-ago' +%d/%b/% ...

  4. goroutine调度源码阅读笔记

    以下为本人阅读goroutine调度源码随手记的笔记,现在还是一个个知识点的形式,暂时还没整理,先发到这里,一点点更新:   1). runq [256]guintptr P 的runable队列最大 ...

  5. Django学习路37_request属性

      打印元信息,基本上都会打印出来 类字典结构的 key 键 允许重复   get 请求可以传参,但是长度有限制 最大不能超过 2K post 文件上传使用 get 参数 默认放在网址中 post 在 ...

  6. Python File seek() 方法

    概述 seek() 方法用于移动文件读取指针到指定位置.高佣联盟 www.cgewang.com 语法 seek() 方法语法如下: fileObject.seek(offset[, whence]) ...

  7. PHP prev() 函数

    实例 输出数组中的当前元素.下一个元素和上一个元素的值: <?php$people = array("Peter", "Joe", "Glenn ...

  8. Skill Virtuoso IC6.1.7 的所有View Type

    https://www.cnblogs.com/yeungchie/ 可以用deGetAllViewTypes()来获取. "graphic" "layout" ...

  9. EACCES: permission denied,mkdir … npm install 安装依赖问题解决

    强哥最近在用hugeGraph图库做二次开发的时候,在打包的时遇到前端项目打包失败的问题: cwebp-bin@4.0.0 postinstall /home/hugegraph/my-hugegra ...

  10. 问题记录,php webserver端跨子域setcookie后浏览器不存

    如题. path已设置成/,domain也已指定成父级域名,数据包response中可见Set-Cookie header为期望的cookie数据,但浏览器就是不接收.存储该cookie, 浏览器端也 ...