无论是初创的小公司,还是互联网大厂。只要是能赚钱的业务,那它的前端项目必定是需要监控的。你们的项目直接果奔?不需要任何监控?那没事了。但是,有木有一种可能,你们一路果奔的前端项目,在不久的将来,出现了本不该出现的 bug,关键这个 bug 还不是你们内部自己发现的,而是客户发现的。这个时候你就需要一款强大的前端监控了。
本文主要介绍的就是我的开源项目,前端监控 sdk:heimdallr-sdk。篇幅有限,因此本篇文章仅仅是对 sdk 主要模块的简单介绍,后续考虑出个系列,欢迎关注;同时,项目也已开源,欢迎 star ⭐
无论是初创的小公司,还是互联网大厂。只要是能赚钱的业务,那它的前端项目必定是需要监控的。
什么?你们的项目直接果奔?不需要任何监控? 那没事了。
根据上述描述,假定你们的前端项目出现了不应该出现的bug。更糟糕的是,这个bug不是你们内部发现的,而是客户发现的。这导致线上bug的数量增加了一个。在你花费大量时间排查问题时,你还需要不断向客户追问:“您是如何操作的?我能远程查看吗?(微笑)”。有些客户愿意告诉你,有些客户则只是礼貌地向你问候或表达关切。
不出意外的话,你今天、明天、大后天,甚至下周、下下周,都会陷入深深的自我怀疑中
“为什么,明明没问题啊,怎么回事”
这个时候你就需要一款强大的前端监控了
前端监控已经不是个新鲜玩意儿了,市面上也已经有成熟的监控系统了,强大如 Sentry,但是,它们有一个共同点,那就是
贵
当然贵是相对小项目而言,对于大项目更关注的是安全性以及更多的定制化
同时作为使用方,一旦监控系统出了问题,就会显得比较被动
因此,不如自己动手撸一个前端监控
直接上菜 heimdallr-sdk
一款轻量级、插件化的前端监控 sdk
为了实现功能的按需引入与可扩展性,整体采用插件化架构
如上图,不同端继承自 Core,每个端各自有多种功能的插件,根据需要引入即可
为了能统一工作流,降低项目基建成本,提高团队协作性;项目采用目前主流的 monorepo 方式进行代码管理,即把多个 packages 放在同一仓库中,插件也将作为独立的子包放在 packages 下,统一编译、调试、发布
monorepo
整体架构如下图所示
大致分为:应用接入层、数据存储层、数据服务层、监控平台层
应用接入层
数据存储层
数据服务层
监控平台层
应用接入层即 sdk 的核心部分,负责收集应用信息并上报
数据存储使用的是 Mysql,为了方便操作数据库,额外引入了个 ORM 库
数据服务层、监控平台层后文细说
这里我实现了两种模式的服务
该模式下日志的上报、写入,与监控后台日志的读取在同一 node 服务中,如下图
node 服务既负责接收日志,也负责读写数据库
该模式拆分了“消费服务”与“生产服务”,同时使用了 RabbitMQ 达到削峰填谷的效果,如下图所示
producer 即生产者,负责接收客户端上报的日志,并推入消息队列。
作为一个SEO运营专家,我的职责之一是处理来自消息队列的消息,以及从数据库中检索相应信息并进行处理。这包括消费者从消息队列中读取消息,并将其拼接成日志信息,最后写入数据库。另外,我还需要处理监控后台发来的请求,在数据库中寻找相应的信息,进行处理后再返回给监控后台。
作为SDK的核心抽象类,Core 主要完成一些基础的初始化工作,并提供与平台无关的代码,同时规范了各个客户端的属性和方法。
Core 主要做了以下事情
Client 即客户端,也就是在不同平台使用的 sdk 基座
Browser 即浏览器端的监控基座,以浏览器为载体的应用都可以使用该基座
继承自 Core 抽象类,实现了 Core 中的抽象方法:
Browser 基座同时内置了错误监控 sdk,以内置插件的方式集成在基座中,可以监听到以下三种类型的错误:
此外还监听了页面的加载与卸载,作为一次访问会话上报,以页面加载作为会话开始、页面卸载视为会话结束
Browser 基座支持 CDN 与 NPM 两种引入方式,这也就意味着绝大多数技术栈的前端应用都可以使用该基座
CDN 方式引入如下
<script> window.__HEIMDALLR_OPTIONS__ = { dsn: { host: 'localhost:8888', init: '/project/init', upload: '/log/upload' }, app: { name: 'playgroundAPP', leader: 'test', desc: 'test proj' }, userIdentify: { name: '__state__.a.0.user.id', // window.__state__ = { a: [{ user: { id:'123' } }] } position: 'global' } }; </script> <script async src="/browser-dist/browser.iife.js"></script>
NPM 引入
import heimdallr from "@heimdallr-sdk/browser"; heimdallr({ dsn: { host: 'localhost:8888', init: '/project/init', upload: '/log/upload' }, app: { name: 'playgroundAPP', leader: 'test', desc: 'test proj' }, userIdentify: { name: '__state__.a.0.user.id', // window.__state__ = { a: [{ user: { id:'123' } }] } position: 'global' } });
Node 即 nodejs 服务端的监控基座
同样继承自 Core 抽象类,实现了应用初始化、上报数据最后的转换、数据上报三个方法
这里的上报方式使用了第三方库来实现,node-fetch
node-fetch
Node 基座同样默认集成了错误监听的能力,监听了 uncaughtException 的错误并上报
uncaughtException
Node 服务端一般不以“会话”为监控维度,更关注接口与服务器性能,因此没有 Browser 中的“会话”的概念
该基座可以通过 NPM 方式引入,与 Browser 引入方式类似
Wx 即微信小程序的监控基座
老规矩,继承自 Core 抽象类,实现初始化、转换、上报三个方法
同样的,Wx 基座也集成了基础的错误监控,本质上就是重写了 APP.onError,捕获到错误并上报
APP.onError
与 Browser (浏览器)最大的不同之处在于如何监听完整的会话。我们人为规定,以 onShow(显示)作为会话的开始,以 onHide(隐藏)作为会话的结束。同时,我们提供了两种方式来监听会话。
trace
heimdallrPage
通过 NPM 方式引入,引入方式参考微信官方文档啦
当前仅有 Browser 基座与 Wx 基座的插件
篇幅有限,只能罗列一下了,没法一个个单独讲
Browser 基座的所有插件均提供 CDN 与 NPM 两种引入方式
window.HEIMDALLR_REPORT(type: string, data: any)
@heimdallr-sdk/page-crash-worker
虽然小程序基座的插件数量有限,但实际上也没有太大的需要。毕竟小程序本身已经具备了一套完整的性能和错误监控机制。因此,我只选择了一些常用但小程序没有提供的监控插件来使用。
插件本质上就是一个个 Plugin 类型对象
基础的 Plugin 类型如下:
export interface BasePluginType { name: string; monitor: (notify: (collectedData: any) => void) => void; transform?: (collectedData: any) => ReportDataType<any>; }
所以,只需创建并返回一个符合 BasePluginType 的对象,就能将其作为插件接入 heimdallr-sdk 的基座中。
服务端作为私有子包,不发布,可通过 @heimdallr-sdk/cli 脚手架快速部署
服务端使用 express 作为 Node 服务端框架,ORM 库使用 Prisma,数据库则使用的是 MySQL
正如前面说的,这里我提供了两种服务端,我把它称为“单服务”与“多服务”
"单服务"使用传统的 MVC 架构,但默认的 View 不直接调用 API,而是作为接口文档,方便浏览。同时也可以通过修改路由将其指向不同的页面。
实现的主要功能如下:
“单服务”既负责接收,也负责提供接口给监控后台(Manager)使用,能直接读写数据库
“多服务”将服务端一分为二,分为“消费服务”与“生产服务”
使用 RabbitMQ 完成对流量的削峰填谷
“生产服务” 也就是图中的 producer,即生产者,面向监控 SDK,从 SDK 接收上报数据
主要功能如下:
“消费服务” 也就是上图的 consumer,也即消费者,面向监控后台,提供读取接口给监控后台调用。
Manager 即监控服务的管理后台,私有包,不发布,同样可以通过 @heimdallr-sdk/cli 脚手架工具快速部署
使用了自己写的 Vue3 脚手架 vva-cli 快速开发的,技术栈是 Vue3 + Typescript + Element-Plus,使用 Vite 打包编译
有以下四个模块:
详情不单开页面,在列表页右侧增加抽屉式弹层展示
(回放功能需引入 @heimdallr-sdk/record 插件)
@heimdallr-sdk/record
同样的,日志详情也不单开页面,在列表页右侧增加抽屉式弹层展示
heimdallr-sdk 的脚手架工具
主要作用就是为了能够快速部署“监控服务端”与“监控管理后台”
全局安装脚手架
npm i @heimdallr-sdk/cli
安装完成后输入 heimdallr-create 命令,即可开始选择相应的模板
heimdallr-create
提供监控后台管理台和监控服务以及带消息队列的监控服务 三类模板
监控后台管理台
监控服务
带消息队列的监控服务
依次完成配置(作答)后,在当前目录下将自动创建项目文件夹
三个模板前文已经介绍了,这里就不再赘述了
这个插件名为"doge",主要功能是自动上传在使用webpack构建工具的项目中生成的sourcemap文件。
它将在 webpack 构建完成后,将产出的 sourcemap 文件自动上传到指定服务器
用法也很简单,指定一下初始化 sdk 时使用的应用名称,以及文件上传的接口地址即可
import UploadSourceMapPlugin from "@heimdallr-sdk/webpack-plugin-sourcemap-upload"; const config = { plugins: [ new UploadSourceMapPlugin({ appname: "playground", url: `http://localhost:8001/sourcemap/upload`, }), ], // TODO-- };
该插件的功能与前一个插件相同,但不同的是:前一个插件适用于使用 webpack 作为构建工具的项目,而该插件适用于使用 vite 作为构建工具的项目。
同样是在 vite 构建工作完成后,将产出的 sourcemap 文件自动上传到指定服务器
因为 vite 底层其实是使用 rollup 构建,因此,该插件监听的是 writeBundle 和 closeBundle 两个阶段的 hook
writeBundle
closeBundle
用法如下
import sourceMapUpload from "@heimdallr-sdk/vite-plugin-sourcemap-upload"; export default defineConfig({ plugins: [ vue(), sourceMapUpload({ appname: "playground", url: `http://localhost:8001/sourcemap/upload`, }), ], build: { sourcemap: true, }, // TODO-- });
使用时需要注意的是,@heimdallr-sdk/webpack-plugin-sourcemap-upload 对外暴露的是一个类,而 @heimdallr-sdk/vite-plugin-sourcemap-upload 对外暴露的则是一个函数
@heimdallr-sdk/webpack-plugin-sourcemap-upload
@heimdallr-sdk/vite-plugin-sourcemap-upload
后续考虑出个系列,再详细写一下实现。刚开源不久,可能还有 bug ,欢迎多多提 issue
前端监控对于任何能赚钱的业务都是必不可少的。当项目出现bug时,使用自己开发的前端监控系统能够更早地发现和排查问题。基于插件化架构的监控系统具有更高的安全性和定制化的能力。对于大项目而言,贵重着重于安全性和定制化。自己开发前端监控系统能够更主动地解决问题。
参考链接:https://zhuanlan.zhihu.com/p/614352204
Webfunny专注于前端监控系统,前端埋点系统的研发。 致力于帮助开发者快速定位问题,帮助企业用数据驱动业务,实现业务数据的快速增长。支持H5/Web/PC前端、微信小程序、支付宝小程序、UniApp和Taro等跨平台框架。实时监控前端网页、前端数据分析、错误统计分析监控和BUG预警,第一时间报警,快速修复BUG!支持私有化部署,Docker容器化部署,可支持千万级PV的日活量!
让数据触手可及
前端/小程序/UniApp/Taro
©2020 上海快快查信息科技有限公司 沪ICP备2021030569号 版权与免责申明 版权申述