前端APM系统主要用于监控JavaScript应用的运行状态,采集性能、错误、行为等多维度数据,通过可视化面板展示,帮助开发团队快速定位线上问题。搭建这类系统需要覆盖数据采集、传输、存储、展示四个核心环节,每个环节都有对应的技术实现方案。

APM系统核心模块划分
完整的JavaScript APM系统通常包含以下四个核心模块,各模块职责明确,相互配合完成监控全流程:
- 数据采集模块:运行在客户端,负责采集页面性能、JS错误、用户行为、接口请求等数据
- 数据上报模块:将采集到的数据经过处理后,发送到服务端存储
- 数据存储模块:接收上报的数据,进行持久化存储,支持后续查询分析
- 可视化展示模块:从存储中读取数据,通过图表、列表等形式展示监控结果
客户端数据采集实现
数据采集是APM系统的基础,需要在页面加载完成后自动采集关键指标,同时监听运行过程中的错误和请求。
性能数据采集
利用浏览器提供的Performance API可以获取页面加载各阶段的耗时,以下是采集核心性能指标的代码示例:
// 采集页面核心性能指标
function collectPerformance() {
// 等待页面加载完成后再获取性能数据
window.addEventListener('load', () => {
setTimeout(() => {
const performance = window.performance;
if (!performance) return;
const timing = performance.timing;
// 计算关键性能指标
const data = {
// DNS查询耗时
dnsTime: timing.domainLookupEnd - timing.domainLookupStart,
// TCP连接耗时
tcpTime: timing.connectEnd - timing.connectStart,
// 白屏时间
whiteScreenTime: timing.responseStart - timing.navigationStart,
// DOM渲染耗时
domReadyTime: timing.domContentLoadedEventEnd - timing.navigationStart,
// 页面完全加载耗时
loadTime: timing.loadEventEnd - timing.navigationStart
};
// 上报性能数据
reportData('performance', data);
}, 0);
});
}
JS错误采集
通过监听全局错误事件,可以捕获页面运行过程中的JavaScript错误,代码如下:
// 采集JS运行时错误
function collectJSError() {
// 监听普通JS错误
window.addEventListener('error', (event) => {
const errorData = {
type: 'js_error',
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error ? event.error.stack : '',
time: new Date().getTime()
};
reportData('error', errorData);
}, true);
// 监听Promise未捕获错误
window.addEventListener('unhandledrejection', (event) => {
const errorData = {
type: 'promise_error',
message: event.reason ? (event.reason.message || String(event.reason)) : '未知Promise错误',
stack: event.reason && event.reason.stack ? event.reason.stack : '',
time: new Date().getTime()
};
reportData('error', errorData);
});
}
接口请求采集
通过劫持XMLHttpRequest和fetch方法,可以采集所有接口请求的耗时、状态等信息:
// 劫持XMLHttpRequest采集接口数据
function hackXHR() {
const originalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new originalXHR();
const startTime = Date.now();
let url = '';
let method = '';
// 劫持open方法获取请求方法和地址
const originalOpen = xhr.open;
xhr.open = function(m, u) {
method = m;
url = u;
return originalOpen.apply(xhr, arguments);
};
// 劫持send方法监听请求完成
const originalSend = xhr.send;
xhr.send = function() {
xhr.addEventListener('loadend', () => {
const endTime = Date.now();
const requestData = {
type: 'xhr',
url: url,
method: method,
status: xhr.status,
duration: endTime - startTime,
time: startTime
};
reportData('request', requestData);
});
return originalSend.apply(xhr, arguments);
};
return xhr;
};
}
数据上报模块实现
采集到的数据需要可靠地上报到服务端,考虑到数据量可能较大,通常采用批量上报、失败重试的策略,同时避免影响主业务流程。
// 数据上报工具函数
const reportQueue = [];
let timer = null;
const REPORT_INTERVAL = 3000; // 每3秒上报一次
const MAX_QUEUE_SIZE = 10; // 队列最大长度,达到后立即上报
function reportData(type, data) {
// 给数据添加公共字段
const reportData = {
...data,
type: type,
pageUrl: window.location.href,
userAgent: navigator.userAgent,
userId: localStorage.getItem('user_id') || 'unknown'
};
reportQueue.push(reportData);
// 队列达到最大长度立即上报
if (reportQueue.length >= MAX_QUEUE_SIZE) {
sendReport();
}
}
// 定时批量上报
function startReportTimer() {
timer = setInterval(() => {
if (reportQueue.length > 0) {
sendReport();
}
}, REPORT_INTERVAL);
}
// 发送上报请求
function sendReport() {
if (reportQueue.length === 0) return;
const data = [...reportQueue];
reportQueue.length = 0;
// 使用sendBeacon保证页面关闭时也能上报成功
if (navigator.sendBeacon) {
const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
navigator.sendBeacon('https://ipipp.com/api/report', blob);
} else {
// 降级使用fetch上报
fetch('https://ipipp.com/api/report', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).catch(() => {
// 上报失败重新加入队列
reportQueue.push(...data);
});
}
}
// 初始化时启动上报定时器
startReportTimer();
服务端存储与可视化展示
服务端可以选择时序数据库存储监控数据,比如InfluxDB适合存储带时间戳的性能、请求类指标,MySQL适合存储错误、用户行为等结构化数据。可视化展示可以使用开源工具Grafana对接存储数据源,快速搭建监控面板,也可以自研前端可视化页面,使用ECharts等图表库渲染数据。
以下是自研可视化页面中,获取并展示接口平均耗时的简单示例:
// 可视化页面获取接口平均耗时数据
async function getRequestAvgDuration() {
try {
const res = await fetch('https://ipipp.com/api/monitor/request/avg_duration?time_range=1h');
const result = await res.json();
if (result.code === 0) {
const chartData = result.data.map(item => {
return { name: item.url, value: item.avg_duration };
});
// 使用ECharts渲染柱状图
const chart = echarts.init(document.getElementById('request-chart'));
chart.setOption({
title: { text: '近1小时接口平均耗时' },
xAxis: { type: 'category', data: chartData.map(d => d.name) },
yAxis: { type: 'value', name: '耗时(ms)' },
series: [{ type: 'bar', data: chartData.map(d => d.value) }]
});
}
} catch (e) {
console.error('获取监控数据失败', e);
}
}
搭建注意事项
在实际搭建过程中,需要注意以下几点:首先,采集脚本要轻量,避免影响页面自身性能;其次,上报数据要做采样处理,高流量场景下可以降低上报比例,减少服务端压力;最后,敏感数据比如用户隐私信息要做脱敏处理,避免合规风险。按照以上流程搭建完成后,就可以在可视化面板中实时查看JavaScript应用的运行状态,快速定位线上问题。
JavaScriptAPM系统可视化监控前端性能监控修改时间:2026-07-10 07:21:37