# aegis-web-sdk Aegis 是腾讯云监控团队提供的前端监控 SDK,涵盖了错误监控,资源测速(img, script, css),接口测速,页面性能(首屏时间)。无需侵入代码,只需引入 SDK 即可自动完成所有监控上报。 在使用 aegis 时无需在业务代码中打点或者做任何其他操作,可以做到与业务代码充分解耦。aegis 将会自动监控前端错误,在错误发生时上报错误的具体情况,帮助您快速定位问题。当您开启资源测速时,aegis 将会自动监听页面资源加载情况(耗费时长、成功率等),并在不影响前端性能的前提下收集前端的性能数据,帮助您快速定位性能短板,提升用户体验。 使用本 SDK 需要配合使用腾讯云前端性能监控 [RUM 平台](https://console.cloud.tencent.com/rum)。 ## Usage 1. 前往腾讯云前端性能监控 [RUM 平台](https://console.cloud.tencent.com/rum) 2. 申请项目,申请完成后得到`上报 id`,id 在 sdk 初始化的时候会使用。 Aegis SDK 在上报所有数据时都会带上`上报 id`,后端服务将根据`上报 id`辨别数据来自哪一个项目,因此,Aegis 建议为每一个项目都单独申请一个 id,如果一个项目下有多个页面,还可以为每一个页面都申请一个项目 id,方便单独查看每一个页面的 PV、错误率、请求错误率等数据。 aegis-sdk 默认使用 `https://aegis.qq.com` 作为上报域名,您也可以选择修改 hostUrl 参数使用 `https://rumt-zh.com` 作为上报域名。如果是腾讯内网用户,有特殊需求也可以使用 `https://aegis-report.woa.com` 作为上报域名。 ## 使用SDK ### 安装 SDK 针对各种情况, SDK 提供了三种引入方式,选择适合业务中的一种即可。无论哪种使用方法,请务必保证 sdk 在 `
` 内,最先声明。这样能保证拿到各类数据监控。 1. cdn 引入 资源地址如下: 最新版本:https://tam.cdn-go.cn/aegis-sdk/latest/aegis.min.js 特定版本(注意链接中的版本号):https://tam.cdn-go.cn/aegis-sdk/{version}/aegis.min.js > 版本说明:为了保证CDN的稳定性,“latest” 版本将会比“npm”版本稍滞后一些; 安全身份识别版本:https://tam.cdn-go.cn/aegis-sdk/latest/aegis.f.min.js > 功能说明:引入浏览器指纹算法,提升uv准确率;增强安全审计功能。 将会在 window 上挂载 `Aegis` 构造函数。 该 cdn 使用 “h3-Q050” 协议,默认 cache-control 为 max-age=666,如果需要修改 cache-control,可以添加参数 max_age,如 ```html ``` 2. npm 引入 ```sh $ npm install aegis-web-sdk ``` 2. 内联引入 如果想要把 SDK 代码直接内联到 html 中的话,可以选择直接 copy 代码的方式,或者使用您熟悉打包工具的内联代码的工具 **推荐使用 CDN 的方式使用 aegis,可以享用更新更全的功能,更及时修复 bug,并且体验无感升级的快感。** 我们也会尽全力保证 CDN 版本的稳定,请您方便使用。 ### SDK 实例化 引入 SDK 后,需实例化: ```javascript // 如果使用 npm 可以直接 import import Aegis from 'aegis-web-sdk'; // 如果使用 cdn 的话,Aegis 会自动绑定在 window 对象上 const aegis = new Aegis({ id: 'pGUVFTCZyewxxxxx', // 项目上报id uin: 'xxx', // 用户唯一标识(可选) reportApiSpeed: true, // 接口测速 reportAssetSpeed: true, // 静态资源测速 spa: true, // spa 页面需要开启,页面切换的时候上报pv }); ``` ::: warning 注意 ⚠️ 为了不遗漏数据,须尽早进行初始化; 初始化之后,可以打开控制台查看上报接口是否正常,network 中搜索上报域名(默认 aegis.qq.com)查看上报数据情况。 如果上报接口返回 403 可以查看 [技术排查相关问题](https://cloud.tencent.com/document/product/1464/58608)。 ::: ::: tip 当您做了以上接入工作之后,您已经开始享受 Aegis 提供的以下功能: 1、错误监控:JS执行错误、Promise错误、Ajax请求异常、资源加载失败、返回码异常、pv上报、白名单检测等; 2、测速:页面性能测速、接口测速、静态资源测速; 3、数据统计和分析:可在 [RUM 平台](https://console.cloud.tencent.com/rum) 上查看各个维度的数据分析; ::: ## 日志 Aegis SDK 会主动收集用户的一些性能和错误日志,开发者可以根据不同的参数来配置哪些日志需要上报,以及上报的日志具体信息。 ### 日志类型 全部日志类型如下: ```javascript { logType: 'custom', name: '自定义测速' } { logType: 'event', name: '自定义事件' } { logType: 'log', name: '日志' } { logType: 'performance', name: '页面测速' } { logType: 'pv', name: '页面PV' } { logType: 'speed', name: '接口和静态资源测速' } { logType: 'vitals', name: 'web vitals' } ``` ### 日志等级 全部日志等级如下: ```javascript { level: 1, name: '白名单日志' }, { level: 2, name: '一般日志' }, { level: 4, name: '错误日志' }, { level: 8, name: 'Promise 错误' }, { level: 16, name: 'Ajax 请求异常' }, { level: 32, name: 'JS 加载异常' }, { level: 64, name: '图片加载异常' }, { level: 128, name: 'css 加载异常' }, { level: 256, name: 'console.error' }, { level: 512, name: '音视频资源异常' } { level: 1024, name: 'retcode 异常' } { level: 2048, name: 'aegis report' } { level: 4096, name: 'PV' } { level: 8192, name: '自定义事件' } { level: 16384, name: '小程序 页面不存在' } { level: 32768, name: 'websocket错误' } { level: 65536, name: 'js bridge错误' } ``` ## aid Aegis SDK 为每个用户设备分配的唯一标识,会存储在浏览器的 localStorage 里面,用来区分用户,计算 uv 等。aid 只有用户清理浏览器缓存才会更新。 对于一些项目,使用自己构造的 aid 作为上报规则,后端对 aid 的校验规则如下:`/^[@=.0-9a-zA-Z_-]{4,36}$/` ## 实例方法 Aegis 实例暴露接口简单实用,目前 Aegis 实例有以下方法供您使用: `setConfig` 、 `info` 、 `infoAll` 、 `report` 、 `error` 、 `reportEvent` 、 `reportTime` 、 `time` 、 `timeEnd`、 `destroy` ### setConfig 该方法用来修改实例配置,比如下面场景: 在实例化 Aegis 时需要传入配置对象 ```javascript const aegis = new Aegis({ id: 'pGUVFTCZyewxxxxx', uin: '777' }) ``` 很多情况下,并不能一开始就获取到用户的 `uin`,而等获取到用户的 `uin` 才开始实例化 Aegis 就晚了,这期间发生的错误 Aegis 将监听不到。`uin` 的设置可以在获取到用户的时候: ```javascript const aegis = new Aegis({ id: 'pGUVFTCZyewxxxxx' }) // 拿到uin之后... aegis.setConfig({ uin: '6666' }) ``` ### info、infoAll、report、error 这三个方法是 Aegis 提供的主要上报手段。 ```javascript aegis.info('上报一条白名单日志,这两种情况这条日志才会报到后台:1、打开页面的用户在名单中;2、对应的页面发生了错误🤨'); aegis.infoAll('上报了一条日志,该上报与info唯一的不同就在于,所有用户都会上报'); aegis.error(new Error('主动上报一个错误')); // report 默认是 aegis.report 的日志类型,但是现在你可以传入任何日志类型了 aegis.report({ msg: '这是一个ajax错误日志', level: Aegis.logType.AJAX_ERROR, ext1: 'ext1', ext2: 'ext2', ext3: 'ext3', trace: 'trace', }); ``` info vs infoAll 1. 使用 “infoAll ” 所有用户都上报,方便排查问题。但是也会带来一定的上报和存储成本。 2. 使用 “info” 白名单上报。出了问题,可能会缺少关键路径日志。需要添加白名单,重新操作收集日志(类似于染色系统操作)。 ### reportEvent 该方法可用来上报自定义事件,平台将会自动统计上报事件的各项指标,诸如:PV、平台分布等... reportEvent 可以支持两种类型上报参数类型,一种是字符串类型 ```javascript aegis.reportEvent('XXX请求成功'); ``` 一种是对象类型,ext1 ext2 ext3 默认使用 new Aegis 的时候传入的参数,自定义事件上报的时候,可以覆盖默认值。 ```javascript aegis.reportEvent({ name: 'XXX请求成功', // 必填 ext1: '额外参数1', ext2: '额外参数2', ext3: '额外参数3', }) ``` 注意,额外参数的三个 key 是固定的,目前只支持 ext1 ext2 ext3。 ### reportTime 该方法可用来上报自定义测速,例如: ```javascript // 假如‘onload’的时间是1s aegis.reportTime('onload', 1000); ``` 或者如果需要使用额外参数,可以传入对象类型参数,ext1,ext2,ext3 会覆盖默认值: ```javascript aegis.reportTime({ name: 'onload', // 自定义测速名称 duration: 1000, // 自定义测速耗时(0 - 60000) ext1: 'test1', ext2: 'test2', ext3: 'test3', }); ``` > `onload` 可以修改为其他的命名。 ### time、timeEnd 该方法同样可用来上报自定义测速,适用于两个时间点之间时长的计算并上报,例如: ```javascript aegis.time('complexOperation'); /** * . * . * 做了很久的复杂操作之后。。。 * . * . */ aegis.timeEnd('complexOperation'); /** 此时日志已经报上去了😄**/ ``` > `complexOperation` 同样可以修改为其他的命名。 > 自定义测速是用户上报任意值,服务端对其进行统计和计算,因为服务端不能做脏数据处理,因此建议用户在上报端进行统计值限制,防止脏数据对整体产生影响。 > 目前 Aegis 只支持 0-60000 的数值计算,如果大于该值,建议进行合理改造。 > 高频率的自定义测速上报尽量使用 reportTime。time 和 timeEnd 上报会存在上报值覆盖的问题。比如 aegis.time(aaa), 在调用 aegis.timeEnd(aaa) 之前,又调用了一次 aegis.time(aaa), 则上报的时间为 timeEnd 时间 - 第二次 time 的时间。 ### destroy 该方法用于销毁 sdk 实例,销毁后,不再进行数据上报 ```javascript aegis.destroy(); ``` ## 白名单 白名单功能是适用于开发者希望对某些特定的用户上报更多的日志,但是又不希望太多上报来影响到全部日志数据,并且减少用户的接口请求次数,因此 TAM 设定了白名单的逻辑。 1. 白名单用户会上报全部的 API 请求信息,包括接口请求和请求结果。 2. 白名单用户可以使用 info 接口上报数据。 3. info vs infoAll:在开发者实际体验过程中,白名单用户可以添加更多的日志,并且使用 info 进行上报。infoAll 会对所有用户无差别进行上报,因此可能导致日志量上报巨大。 4. 通过接口 whitelist 来判断当前用户是否是白名单用户,白名单用户的返回结果会绑定在 aegis 实例上 (aegis.isWhiteList) 用来给开发者使用。 5. 用了减少开发者使用负担,白名单用户是团队有效,可以在 [应用管理-白名单管理](https://console.cloud.tencent.com/rum/web/group-whitelist-manage) 内创建白名单,则团队下全部项目都生效。 ## 钩子函数 ### beforeRequest 该钩子将会在日志上报前执行,用于对上报数据的拦截和修改,通过返回不同类型的值: * 拦截:返回false * 修改:修改入参的值并返回 ```javascript const aegis = new Aegis({ id: "pGUVFTCZyewxxxxx", beforeRequest: function(data) { // 入参 data 的数据结构:{logs: {…}, logType: "log"} if (data.logType === 'log' && data.logs.msg.indexOf('otheve.beacon.qq.com') > -1) { // 拦截:日志类型为 log,且内容包含 otheve.beacon.qq.com 的请求 return false; } // 入参 data 数据结构:{logs: {}, logType: "speed"} if (data.logType === 'speed' && data.logs.url.indexOf('otheve.beacon.qq.com') > -1) { // 拦截:日志类型为 speed,并且接口 url 包含 otheve.beacon.qq.com 的请求 return false; } if (data.logType === 'performance') { // 修改:将性能数据的首屏渲染时间改为2s data.logs.firstScreenTiming = 2000; } return data; } }); ``` 其中,`msg` 将会有以下几个字段: > 1.`logs`: 上报的日志内容; > 2.`logType`: 日志类型 logType等于`custom`时,logs的值如下: ```sh {name: "白屏时间", duration: 3015.7000000178814, ext1: '', ext2: '', ext3: '', from: ''} ``` logType等于`event`时,logs的值如下: ```sh {name: "ios", ext1: "", ext2: "", ext3: ""} ``` logType等于`log`时,logs的值如下: ```sh { level: '1', msg: '接口请求日志(白名单日志)' } // 具体level信息参考日志等级 ``` logType等于`performance`时,logs的值如下: ```sh {contentDownload: 2, dnsLookup: 0, domParse: 501, firstScreenTiming: 2315, resourceDownload: 2660, ssl: 4, tcp: 4, ttfb: 5} ``` logType等于`speed`时,logs的值如下: ```sh // 静态资源 {connectTime: 0, domainLookup: 0, duration: 508.2, nextHopProtocol: "", isHttps: true, method: "get", status: 200, type: "static", url: "https://puui.qpic.cn/xxx", urlQuery: "max_age=1296000"} // API {duration: 26, isErr: 0, isHttps: true, method: "GET", nextHopProtocol: "", ret: "0", status: 200, type: "fetch", url: "https://xx.com/cgi-bin/whoami"} ``` logType等于`vitals`时,logs的值如下: ```sh {CLS: 3.365504747991234, FCP: 139.39999997615814, FID: -1, LCP: 127.899} ``` ### modifyRequest 该钩子函数会在所有请求发出前调用,参数中会传入请求的所有内容,必须返回待发送内容。 ```javascript function changeURLArg(url,arg,arg_val) { var pattern=arg+' = ([^&]*)'; var replaceText = arg+'='+arg_val; if (url.match(pattern)) { var tmp = '/('+ arg+'=)([^&]*)/gi'; tmp = url.replace(eval(tmp),replaceText); return tmp; } return url; } const aegis = new Aegis({ id: 'pGUVFTCZyewxxxxx', modifyRequest(options) { if (options.type === 'performance') { // 页面测速,此时可以修改options内容,如修改页面测速platform options.url = changeURLArg(options.url, 'platform', type) } return options } }); ``` ### afterRequest 该勾子将会在数据上报后被执行,例如: ```javascript const aegis = new Aegis({ id: "pGUVFTCZyewxxxxx", reportApiSpeed: true, reportAssetSpeed: true, afterRequest: function(msg) { // {isErr: false, result: Array(1), logType: "log", logs: Array(4)} console.log(msg); } }); ``` 其中,`msg` 将会有以下几个字段: > 1.`isErr`: 请求上报接口是否错误; > 2.`result`: 上报接口的返回结果; > 3.`logs`: 上报的日志内容; > 4.`logType`: 日志类型,有以下值speed:接口和静态资源测速,performance:页面测速,vitals:web vitals,event:自定义事件,custom:自定义测速; ## 错误监控 ::: warning Aegis 的实例在初始化之后会自动进行以下监控 1. JS执行错误 2. Promise执行错误 3. 资源加载失败 开启 `reportApiSpeed` 参数后,会自动监听以下异常 1. Ajax(Fetch)请求异常 2. retcode异常 开启 `websocketHack` 参数后,会自动监听 websocket 执行异常 注意!是 Aegis 实例会进行监控,当您只是引入了 SDK 而没有将其实例化时,Aegis 将什么都不会做。 ::: ### JS执行错误 Aegis 通过监听 `window` 对象上的 `onerror` 事件来获取项目中的报错,并且通过解析错误和分析堆栈,将错误信息自动上报到后台服务中。该上报的上报等级为 error ,所以,当自动上报的错误达到阈值时,Aegis 将会自动告警,帮助您尽早发现异常。由于上报等级为 error ,自动上报也将影响项目的评分。 > 如果页面上引入了跨域的JS脚本,需要给对应的 `script` 标签添加 `crossorigin` 属性,否则 Aegis 将无法获取详细的错误信息,参考 [这篇文章](http://km.oa.com/group/11800/articles/show/386426) 注意如果用户使用的是 vue 框架,请务必自己获取错误并且主动上报 ```javascript Vue.config.errorHandler = function(err, vm, info) { console.log(`Error: ${err.toString()}\nStack: ${err.stack}\nInfo: ${info}`); aegis.error(`Error: ${err.toString()}\nStack: ${err.stack}\nInfo: ${info}`); }; ``` ### Promise执行错误 通过监听 `unhandledrejection` 事件,捕获到未被 `catch` 的Promise错误,为了页面的健壮,建议您 `catch` 住所有的Promise错误哟。 ### 资源加载失败 页面元素发出的请求如果失败,将会被 `window.onerror` 事件捕获到(捕获阶段),Aegis 正是通过这个特性监听的资源加载失败。Aegis监听了以下资源: 1. `` 标签请求的css、font等; 2. ` ``` npm 引入: ```sh $ npm install aegis-web-sdk ``` ```javascript import Aegis from '@tencent/aegis-web-sdk/lib/aegis.f.min'; ``` > 备注:version > 1.38.56 2. 查看uv  3. 更多应用场景 与此同时,配合业务场景进行技术赋能,可以扩展出更多能力。如安全部门基于指纹SDK扩展出安全审计功能等。 4. 指纹算法简介 人的指纹千变万化,具有唯一性,可以作为人的身份标识。类似的,浏览器指纹是通过获取浏览器具有辨识度的信息,进行一些计算得出一个值,那么这个值就是浏览器指纹。 浏览器指纹是由许多浏览器的特征信息综合起来的,其中特征值的信息熵也不尽相同。因此,指纹也分为基本指纹和高级指纹。**基本指纹**是任何浏览器都具有的特征标识,比如硬件类型(Apple)、操作系统(Mac OS)、用户代理(User agent)、系统字体、语言、屏幕分辨率、浏览器插件 (Flash, Silverlight, Java, etc)等众多信息,这些指纹信息“类似”人类的身高、年龄等,有很大的冲突概率,只能作为辅助识别。普通指纹是不够区分独特的个人,这时就需要**高级指纹**,将范围进一步缩小,甚至生成一个独一无二的浏览器身份。常见的有:Canvas指纹、Webgl指纹、audio指纹等。 在指纹版Aegis SDK中,也引入了多种业界成熟的基础/高级指纹,设备识别准确度的提升显著。我们以Canvas为例,来展开介绍下指纹算法的实现思路。 由于系统的差别,Canvas底层的图形渲染引擎不同,对抗锯齿、次像素渲染等算法也不同;我们通过在画布上渲染一些文字,再用 toDataURL 转换输出一段字符串,这就生成了代表设备唯一标识的指纹信息。 ```javascript function getCanvasFingerprint () { var canvas = document.createElement('canvas'); var context = canvas.getContext("2d"); context.font = "18pt Arial"; context.textBaseline = "top"; ctx.fillText('aegis,sdk