如何搭建可视化监控JavaScript的APM系统

来源:Nodejs社区作者:广州GEO公司头衔:草根站长
导读:本期聚焦于小伙伴创作的《如何搭建可视化监控JavaScript的APM系统》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何搭建可视化监控JavaScript的APM系统》有用,将其分享出去将是对创作者最好的鼓励。

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

如何搭建可视化监控JavaScript的APM系统

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);
  });
}

接口请求采集

通过劫持XMLHttpRequestfetch方法,可以采集所有接口请求的耗时、状态等信息:

// 劫持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

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。