埋点监控平台开发全流程解密

205 阅读15分钟

一、先来解决一些知识障碍

1. 什么是 SDK

SDK 全称 Software Development Kit (软件开发工具包),就是一个可被引入、用来 “ 增强功能 ” 的工具包。

在前端埋点监控场景下,SDK 就是一个前端脚本(通常是一个 JS 文件),它可以被放到任何前端项目里,用来自动收集和上报运行数据。

 

2. 什么是埋点和监控

○ 埋点是一种通过在代码中插入监控代码(埋点代码)来收集用户在应用程序中的行为数据的一种数据采集技术,核心关注点是用户行为和业务数据

○ 监控是一种实时收集应用的性能表现、加载速度、错误日志等运行状态数据的技术

○ 埋点和监控可以「 获取用户行为以及跟踪产品在用户端的使用情况」,并以监控数据为基础,指明产品优化的方向,核心关注点是系统Bug、性能优化

 

3. 关于埋点监控平台的“通用性设计”与“需求来源”问题

埋点监控平台就相当于给一个已有的比如说电商管理系统做监控,那开发这个埋点监控平台需要事先有一个这种管理系统吗?如果不需要,那我们怎么知道这个埋点监控平台需要监控哪些东西呢?

不需要。

首先明确 : 埋点监控系统 ≠ 为某个系统定制,埋点监控平台本质上是一种 通用的数据采集上报平台

它的目标是: 提供一种可以 " 接入任何前端项目 "的能力,用来采集行为、性能、错误等数据

那我们怎么知道要采集什么??

关键在于: 明确监控目标和数据类型

我们可以把监控目标分为几大类:

监控类型采集内容示例目的
性能监控页面加载时间、白屏时间、资源加载情况优化前端性能体验
行为监控(埋点)点击按钮、浏览页面、表单提交,搜索操作分析用户行为路径
错误监控接口错误、资源加载错误、Promise拒绝提升系统稳定性
用户体验监控收集白屏、卡顿等影响用户体验的问题提高用户体验
环境信息浏览器、系统设备、网络类型维度分析辅助

我们在设计 SDK时,可以让它支持这些类型,等到将来接入某个系统的时候(比如电商管理系统)时,开发者只需要调用你提供的接口:

 monitor.trackEvent('add_to_cart', { productId: 'A123', price: 99 }); 

a. monitor 是这个 SDK 里定义的数据监控分析工具的实例对象

b. .trackEvent是该对象的方法,专门用于追踪用户事件,记录用户的具体操作行为

c. 第一个参数'add\_to\_cart'是事件类型,表示 “ 加入购物车 “动作

d. 第二个参数{ productId: 'A123', price: 99 }是事件属性,提供事件的详细信息

 productId: 'A123'是被加入购物车的商品ID

 price: 99是商品价格(单位通常是元)

实际应用起来的场景:

当用户在电商网站点击"加入购物车"按钮时,这段代码会执行,向数据分析服务器发送类似这样的信息:

 用户执行了"加入购物车"操作 商品ID: A123 商品价格: 99元 

 

4. 什么是架构

架构(Architecture)就是系统整体的结构设计,可以理解为“系统的骨架或蓝图”。它告诉你:

○ 系统有哪些模块(功能块)

○ 模块之间怎么协作(数据流、调用关系)

○ 数据在系统里怎么传递和处理

 

5. 埋点监控 SDK 怎么集成到前端项目里

a. 通过<script>标签引入

▪ 适合普通 HTML 页面或者快速试用SDK

▪ 做法:SDK打包成一个JS文件放到服务器上然后在页面引入

b. 通过包管理工具引入(现代前端框架)

▪ 做法:包管理工具安装,在项目里 import 并初始化

c. 动态加载 SDK

▪ 适合希望按需加载 SDK,减少首屏压力的场景,例如:

 const script = document.createElement('script'); 
 script.src = '<https://cdn.example.com/sdk.v1.0.0.js>'; 
 script.onload = () => {     SDK.init({ appId: '123' }); }; 
 document.head.appendChild(script); |

二、从 0 到 1 开发一个埋点监控平台的过程

image.png

1. 需求分析 & 功能规划

a. 明确要监控的内容:

▪ 性能指标:FCP、LCP、CLS、TTFB、资源加载时间

▪ 错误监控:JS 错误、Promise 未捕获异常、资源加载失败

▪ 用户行为:点击、PV、UV、停留时间、页面跳转

b. 确定数据优先级:

▪ 高优先级:错误、关键行为(影响功能)

▪ 中优先级:性能指标(影响体验)

▪ 低优先级:普通行为数据(影响业务决策)

c. 明确项目目标:

▪ 通用性强:SDK 可嵌入任何前端项目

▪ 低侵入、低损耗:不影响页面正常运行

▪ 后端可实时处理与可视化分析

d. 输出:

▪ 功能清单

▪ 数据采集指标列表

▪ 数据优先级表

2. 技术调研与架构设计

 a. SDK 架构设计

i. 采集模块:决定要捕获哪些数据(性能/错误/行为)

ii. 处理模块:如何处理采集到的数据(数据格式化、去重、队列缓存、优先级调度、重试机制)

iii. 上报模块:数据怎么、什么时候、以什么方式发送到后端(立即上报、批量上报、延迟上报、页面关闭上报)

b.上报策略

▪ 高优先级 → fetch/sendBeacon 立即上报

▪ 中优先级 → 页面加载完成或定时批量上报

▪ 低优先级 → 定时/批量上报

c. 后端初步设计

▪ 数据接收接口

▪ 数据存储(性能、错误、行为表)

▪ 聚合分析逻辑:对数据库里的数据做统计、汇总、计算指标,生成可用信息

d. 输出:

▪ SDK 模块图:把SDK内部的各功能模块的组成和关系可视化

▪ 上报流程设计:展示数据从采集到上报后端的完整流程

▪ 后端接口与数据表结构草图

3️. SDK 核心开发

a. 采集模块

○ 性能监控:Performance API + 页面 load/DOMContentLoaded

○ 错误监控

▪ window.onerror 捕获 JS 错误

▪ window.onunhandledrejection 捕获 Promise 异常

▪ 资源加载失败事件监听

 b. 行为监控

▪ 点击事件监听 (document.addEventListener)

▪ 路由跳转监听 History API (pushState, replaceState) 或路由变化

▪ 表单提交、页面停留时长等记录用户交互数据

浏览器原生支持的 API 比如 Performance API +  错误事件 +  行为事件 就支持绝大部分的基础监控数据的采集了,所以掌握已有的 API 是实现这部分的基础

c. 数据处理模块

○ 数据去重、格式化

○ 队列缓存,支持高/中/低优先级调度

○ 网络异常重试机制

数据处理模块就是要用 JS 实现一些复杂数据结构和算法来保证高效有序合理,技术其实就是前端开发基础的技术

d. 上报模块

○ 高优先级:立即 fetch/sendBeacon

○ 中优先级:页面 unload 或定时批量

○ 低优先级:定时批量上报(如 5 条或 5 秒)

○ 页面关闭时确保使用 navigator.sendBeacon 发送未上报的数据

本质上就是用 JS 里面的fetch/ XHR / navigator.sendBeacon这些网络请求工具把数据高效合理地发给后端

4️. 后端接口与数据存储开发

 a. 接口设计

▪ 定义好请求方法(POST)和数据格式(JSON)

▪ 支持高并发请求,保证数据不会丢失

▪ 对不同优先级数据可能做不同处理策略

b. 数据存储

▪ 数据分表存储,便于聚合统计和查询

▪ 可以设计索引和分区,保证高性能查询

 c. 数据处理

▪ 聚合统计(PV、点击量、错误次数)

▪ 异常告警触发(高优先级错误)

核心目标是可靠接收、存储、处理前端采集的数据,为后面可视化和分析提供基础

5️. SDK 与后端联调

○ 测试数据是否准确到达后端

○ 检查高优先级数据是否即时上报

○ 检查低优先级数据是否批量、延迟上报

○ 测试网络异常、页面关闭、SPA 路由切换等特殊场景下是否可靠

6️. 可视化与分析模块开发

 a. 展示内容

▪ 性能趋势图(FCP、LCP、CLS)

▪ 错误分布图(类型、频次、页面)

▪ 用户行为统计(PV、UV、点击热区)

b. 数据刷新

▪ 实时数据 → WebSocket 或定时拉取

▪ 历史数据 → 后端聚合接口

▪ 可选:告警系统(如错误次数超过阈值触发邮件或钉钉)

用 Echarts 这种前端图表库把得到的数据直观地展示出来,辅之用 Websocket 实现实时传输数据 ,用 AJAX/fetch 来定时请求数据 ,后端还可以用 REST API 把数据进行聚合来展示数据的趋势

7️. 测试与性能优化

a.功能测试:

▪ 数据采集是否完整

▪ 数据上报是否正确

b. 性能测试:

▪ SDK 体积小、对页面渲染无影响

▪ 高并发行为上报不会丢数据

c. 优化:

▪ 队列调度优化

▪ 数据压缩

▪ 上报策略调整

基本上就是用抓包工具,浏览器开发者工具这些获取一些模拟的数据,用 JS和浏览器提供的api实现数据结构和算法来对这个系统上线后的结果进行模拟和预测

8️. 上线与迭代

○ 先小范围上线,收集真实数据

○ 调整指标采集和上报策略

○ 迭代优化 SDK 性能和功能

这部分就是用 CDN 内容分发网络实现版本化,实现一些数据结构和算法,利用 JS 的网络请求工具实现根据实际情况调整采集和上报策略,然后用一些打包工具来减小SDK的体积等实现优化

三、监控价值与学习目标

1. 为什么做这个埋点监控平台 ?

角度目的
业务侧分析用户行为,优化产品转化路径
技术侧定位前端 Bug、性能瓶颈、白屏等问题
团队侧搭建统一的监控体系,减少埋点重复工作
学习侧理解浏览器运行机制、事件系统、数据传输、日志采集与分析等全链路知识

2. 做完了要掌握哪些知识 ?

学习方向涉及知识点
前端采集层浏览器事件机制、错误捕获、性能指标采集(Performance API)
SDK 设计模块化封装、插件机制、数据缓存与上报策略
网络层Fetch / Beacon / XHR 上报方案、跨域与可靠性
后端接收层日志存储结构设计、接口处理、数据分析管道
数据展示层可视化平台(如 ECharts、Grafana)
工程化能力TypeScript、打包(Rollup / Vite)、npm 发布、监控 SDK 接入方式

四、原生 JS 实现最简单的监控

JS 有一些原生 API 可以实现对一些事件的监听和跟踪,下面是原生JS实现的对一些事件和数据的监控

1. 跟踪用户事件

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>跟踪用户事件</title>
    <style>
        body {
            height: 2000px;
            background-color: antiquewhite;
        }
    </style>
</head>

<body>
    <!-- 1.捕获按钮的点击 -->
    <!-- 2.捕获页面的滚动行为 -->
    <div>
        <button id="myButton">捕获按钮的点击行为</button>
    </div>
    <script>
        //定义通用的跟踪函数
        function trackEvent(eventType, details) {
            console.log(`Event:${eventType}`, details)
            // 上报
            //fetch('/测试接口的地址',{
            //  method:'POST',
            // body: JSON.stringfy({eventType,details})
            // })
        }
        //获取按钮对象
        const button = document.getElementById('myButton')
        //给按钮添加监听,传入参数
        button.addEventListener('click', function () {
            trackEvent('button_click', {
                buttonId: 'myButton',
                timeStamp: Date.now()
            })
        })
        window.addEventListener('scroll', function () {
            trackEvent('page_scroll', {
                scrollY: window.scrollY,
                timeStamp: Date.now()
            })
        })
    </script>

</body>

</html>

a. 监控按钮点击

image.png

b. 监控页面滚动

image.png

2. 完成性能监控指标

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>性能监控指标</title>
    <style>

    </style>
</head>

<body>
    <!-- 1.捕获按钮的点击 -->
    <!-- 2.捕获页面的滚动行为 -->
    <div>页面加载时间的监控</div>
    <div>API调用耗时监控</div>
    <script>
        //定义通用的跟踪函数
        function trackEvent(eventType, details) {
            console.log(`Event:${eventType}`, details)
            // 上报
            //fetch('/测试接口的地址',{
            //  method:'POST',
            // body: JSON.stringfy({eventType,details})
            // })
        }
        //API 调用耗时
        //1.请求发送前
        //2.请求接收后
        function apiPerformance() {
            const start = performance.now()
            fetch('https://api.imooc-web.lgdsunday.club//api/data-lazy/01')
                .then((res) => res.json())
                .then((data) => {
                    const duration = performance.now() - start
                    trackEvent('apicall', {
                        duration,
                        endpoint: 'https://api.imooc-web.lgdsunday.club//api/data-lazy/01'
                    }
                    )
                })
        }
        apiPerformance()
    </script>

</body>

</html>

a. 页面加载时间的监控

image.png

b. API调用耗时监控

image.png

3. 进行错误追踪监听

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>性能监控指标</title>
    <style>

    </style>
</head>

<body>
    <!-- 1.捕获按钮的点击 -->
    <!-- 2.捕获页面的滚动行为 -->
    <div>进行错误追踪监听</div>
    <script>
        //定义通用的跟踪函数
        function trackEvent(eventType, details) {
            console.log(`Event:${eventType}`, details)
            // 上报
            //fetch('/测试接口的地址',{
            //  method:'POST',
            // body: JSON.stringfy({eventType,details})
            // })
        }
        window.onerror = function (message, source, lineno, colno, error) {
            trackEvent('js_error', {
                message,
                source,
                lineno,
                colno,
                error
            })
            return true
        }
        ''.push('123')
    </script>

</body>

</html>

image.png

4. 自定义的埋点上报

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <h1>用户注册表单</h1>
    <form id="register">
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" required>
        </br>
        </br>
        <label for="email">邮箱:</label>
        <input type="email" id="email" name="email" required>
        </br>
        </br>
        <label for="password">密码:</label>
        <input type="password" id="password" name="password" required>
        </br>
        </br>
        <button type="button" id="submitButton">注册</button>
    </form>
    <script>
        function trackEvent(eventType, details) {
            console.log(`Event:${eventType}`, details)
            // 上报
            //fetch('/测试接口的地址',{
            // method:'POST',
            // body: JSON.stringfy({eventType,details})
            // })
        }
        //1.监控页面浏览量
        window.addEventListener('load', function () {
            trackEvent('page_view', {
                url: window.location.href,
                timestamp: Date.now()
            })
        })
        //2.监控字段的聚焦事件
        const inputFields = document.querySelectorAll('#register')
        inputFields.forEach(field => {
            field.addEventListener('focus', function () {
                trackEvent('input_focus', {
                    fieldName: field.name,
                    timestamp: Date.now()
                })
            })
        })
        //监控提交按钮点击
        submitButton.addEventListener('click', () => {
            trackEvent('button_click', {
                buttonId: 'submitButton',
                timestamp: Date.now()
            })
            handleSubmit()
        })
        //4.监控表单的提交事件
        function handleSubmit() {
            if (register.checkValidity()) {
                trackEvent('form_submit', {
                    formId: 'register',
                    formData: {
                        username: rigister.username.value,
                        email: rigister.email.value,
                        password: register.password.value
                    },
                    timestamp: Date.now()
                })
            } else {
                alert('请填写完整表单')
            }
        }
    </script>
    <style>
        #sectionId {
            width: 200px;
            height: 200px;
            background-color: antiquewhite
        }
    </style>
</body>

</html>

image.png

5. 一个简易表单的事件监控

image.png

JS实现监控方案总结: 核心就是获取到对应的数据,然后在特定的地点或者特定的回调时间把获取的数据上报给服务端, 想要完成监控,关键就是要更加深入地去了解各种关键的事件节点

五、现有埋点监控平台和上报方案对比

1. 一些现有的「前端埋点监控 SDK / 平台类开源项目」的对比

类别对比方面WebSeeMonitorJSFunny-Monitor
功能点错误监控JS 异常、Promise、资源加载、接口错误JS 异常、Promise、资源加载(window.onerror 等实现)JS 异常、Promise、接口错误
性能监控FP / FCP / LCP / CLS、接口耗时没有现成的页面加载性能、资源性能
行为监控点击、路由跳转、页面停留用户路径记录(出错前后的操作上下文)点击、路由跳转
环境信息采集浏览器、设备、网络类型浏览器 UA、URL、时间戳不支持
数据上报Beacon / FetchXMLHttpRequestFetch / XHR
架构/扩展能力插件化架构设计不具备前端 SDK + 后端 + 可视化平台
亮点 ✅错误监控错误捕获覆盖全面,支持多类型错误错误捕获逻辑清晰直观错误捕获完整,便于学习整体流程
性能监控支持 FP/FCP/LCP/CLS,接口耗时上报没有现成的支持页面加载性能和资源性能分析
行为监控覆盖点击、路由跳转、页面停留用户行为路径记录覆盖点击、路由跳转
环境信息采集浏览器、设备、网络类型信息完整浏览器 UA、URL、时间戳
数据上报支持 Beacon / Fetch,多种上报方式XMLHttpRequest支持 Fetch / XHR
架构/扩展能力插件机制完善,可自由扩展,TypeScript 编写,代码结构清晰轻量、实现简单、易于嵌入架构完整(前端 SDK + 后端 + 可视化)
不足 ⚠️错误监控仅支持错误监控,无性能/行为数据文档不完善,部署复杂
性能监控后端能力较弱,难支撑大规模日志
行为监控自动化埋点能力一般
环境信息采集
数据上报依赖后端数据接收服务无插件机制与扩展能力无可视化展示层更新不活跃
架构/扩展能力SDK 较重,学习曲线陡峭代码更新少、维护较慢
总结推荐
项目名称适合人群学习价值
WebSee想深入了解前端监控 SDK 原理、设计架构的人⭐⭐⭐⭐(进阶级)
MonitorJS想快速理解错误监控机制、入门级学习者⭐⭐(入门级)
Funny-Monitor想学习从 SDK 到后端数据展示全流程的开发者⭐⭐⭐(综合实践型)

2. 上报方案对比

一个埋点监控系统已经包含了上报方案,我们如果用已有的埋点监控系统,选定了系统,上报方案一般改不了,那为什么还要拿出来说??因为对于开发者来说,要设计系统 ,就需要选择最佳上报方案,因此需要理解原理 ,得知道各种方案的优劣

上报方式工作原理优点缺点适用场景
Beacon利用浏览器提供的异步、可靠的数据上报接口,将少量数据在页面卸载时发送到服务器,不阻塞页面卸载过程。✅ 异步非阻塞,页面关闭时也能成功上报 ✅ 不影响性能,不需手动管理连接 ✅ 浏览器原生支持较好(现代浏览器)⚠️ 仅支持 POST 请求 ⚠️ 数据量有限(~64KB) ⚠️ 旧浏览器(IE)不兼容页面关闭、跳转、卸载等临时事件上报;前端埋点 SDK 推荐使用的默认方式
XHR传统 AJAX 接口,通过构造异步请求向服务器上报数据,可灵活设置请求头、同步/异步模式。✅ 兼容性好,几乎所有浏览器支持 ✅ 可精确控制请求(header、method等) ✅ 可与后端交互逻辑复用⚠️ 页面卸载时容易被中断(数据丢失) ⚠️ 若同步请求会阻塞页面卸载 ⚠️ 需自行封装上报逻辑页面加载阶段、周期性上报、错误日志上报
Fetch API基于 Promise 的现代异步请求接口,语义更简洁,可用于发送 JSON、FormData 等类型数据。✅ 语法简洁、支持 async/await ✅ 支持跨域、灵活配置请求头 ✅ 支持 streaming 等特性⚠️ 同样存在卸载中断问题(除非配合 Keepalive) ⚠️ 旧浏览器需 polyfill常规埋点、性能数据上报、接口日志上传
Image Beacon通过创建 new Image(),将数据拼接在 URL 中发送 GET 请求。✅ 实现极其简单、兼容性极好 ✅ 不受跨域限制⚠️ 数据长度受 URL 限制(约 2KB) ⚠️ 无法获取响应结果 ⚠️ 不适合复杂数据结构兜底兼容方案