Glittering's blog Glittering's blog
Home
  • 学习手册

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • 《Vite》
    • 《Vue3》
    • TypeScript
    • CSS
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)

Glitz Ma

前端开发工程师
Home
  • 学习手册

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • 《Vite》
    • 《Vue3》
    • TypeScript
    • CSS
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)
  • 技术文档

  • 算法

  • 工作总结

    • 时区校正
    • 上传下载文件方式总结
    • webpack优化实践
    • webpack基础应用知识总结
    • vue常见原理总结
    • vue基础知识总结
    • react高级特性
    • react基础知识总结
    • 微前端总结
    • 地图标绘--射线法来计算点在多边形内
    • web异常监控和分析
      • 1、前端监控概述与核心价值
      • 2. 监控的三个核心维度 (监控什么?)
        • 🛡️ 稳定性监控 (Stability / Error)
        • 🚀 性能监控 (Performance)
        • 👥 用户行为监控 (User Behavior)
      • 3. 关键性能指标 (Core Web Vitals)
      • 4. 工具选型方案
        • 💎 方案 A:SaaS 商业服务 (最省心,但贵)
        • 🛠️ 方案 B:自研 / 基于开源搭建 (成本可控,数据私有)
        • 📦 方案 C:简单埋点 (轻量级)
      • 5. 核心技术实现与难点
        • 5.1 白屏监控的实现方案
        • ✅ 一、什么是白屏?
        • ✅ 二、白屏检测的 3 种实战方案(按可靠性排序)
        • 方案 1:关键 DOM 探测(推荐)
        • ✔ 可直接用的代码
        • 方案 2:像素采样检测(更严格,但耗性能)
        • 方案 3:超时 + DOM 无变化(轻量 fallback)
        • ✅ 三、白屏上报的数据格式(建议)
        • ✅ 四、如何减少误报?
        • 1. 检查是否有 LCP
        • 2. 检查是否存在 skeleton(骨架屏)
        • 3. 检查是否用户主动切走 tab
        • 4. 分地域 + 设备聚合
        • ✅ 五、白屏监控仪表盘(指标建议)
        • 1. 白屏率(主指标)
        • 2. LCP = 0 的比例(辅助指标)
        • 3. 白屏分布
        • 5.2 错误捕获与上报
        • 跨域的js运行错误捕获
        • 错误上报
        • 5.3 性能数据采集
        • 5.4 实施难点与解决方案(SourceMap、采样、过滤)落地实施的难点与技巧
      • 6. 总结与实施路线
      • 7. 参考资料与扩展阅读
        • Web Vitals
    • axios 与 promise
    • 前端优化指南
    • http缓存机制
    • websocket聊天
    • 静态资源灰度发布
    • 小程序笔记
    • JS随机打乱数组
    • 非常实用的JavaScript一行代码
  • 实用技巧

  • 收藏夹

  • 技术
  • 工作总结
mamingjuan
2019-05-01
目录

web异常监控和分析

# 1、前端监控概述与核心价值

前端监控是保障产品质量的“眼睛”。它能帮助你在用户投诉之前发现 Bug,在老板质疑之前量化性能瓶颈。


# 2. 监控的三个核心维度 (监控什么?)

一个完善的前端监控系统通常包含以下三个方面:

# 🛡️ 稳定性监控 (Stability / Error)

  • JS 错误: 运行时报错(TypeError、ReferenceError 等)。
  • 资源加载错误: 图片、CSS、Script 加载失败(通常是 404)。
  • API 请求错误: 接口返回 4xx/5xx,或者业务逻辑上的“成功但报错”(如 {code: -1, msg: 'error'})。
  • 白屏检测: 页面关键 DOM 节点缺失,导致用户看到一片白,这是最高优先级的致命故障。

# 🚀 性能监控 (Performance)

  • 加载速度: 页面多久能刷出来?多久能交互?
  • 接口耗时: 关键业务接口的响应时间。
  • 静态资源: 主要是大图、大 JS 文件的加载耗时。

# 👥 用户行为监控 (User Behavior)

  • PV/UV: 基础的访问量数据。
  • 用户路径: 用户点进了哪个页面,又跳转到了哪里。
  • 点击热力图: 用户最喜欢点哪里。
  • 场景回溯 (Session Replay): 这是排查 Bug 的神器。通过技术手段(如 rrweb)录制用户操作视频,还原 Bug 发生时的真实场景。

# 3. 关键性能指标 (Core Web Vitals)

Google 制定了一套行业标准,称为 Core Web Vitals (核心 Web 指标),这是目前性能监控的“圣经”。

指标 全称 中文含义 衡量什么? 标准 (好)
LCP Largest Contentful Paint 最大内容绘制 加载速度。视口内最大的图片或文本块渲染出来的时间。 < 2.5s
INP Interaction to Next Paint 下次绘制交互 交互响应度。用户点击按钮后,页面多久才有反应。(注:INP 已取代 FID 成为核心指标) < 200ms
CLS Cumulative Layout Shift 累积布局偏移 视觉稳定性。页面加载时元素是否乱跳(导致误触)。 < 0.1
FCP First Contentful Paint 首次内容绘制 白屏结束的时间点,用户看到第一个像素的时间。 < 1.8s

# 4. 工具选型方案

根据团队规模和预算,通常有三种选择路径:

# 💎 方案 A:SaaS 商业服务 (最省心,但贵)

适合中小团队或快速迭代期,花钱买时间。

  • Sentry: 全球最强。错误追踪能力极强,支持 SourceMap 还原代码,现在的性能监控也做得很好。
  • LogRocket: 专注于会话回放,能像看电影一样看用户怎么操作出错的,体验极佳。
  • 阿里云 ARMS / 腾讯云 RUM: 国内大厂方案,合规性好,网络接入快。

# 🛠️ 方案 B:自研 / 基于开源搭建 (成本可控,数据私有)

适合大厂或对数据隐私要求高的团队。

  • Web-Tracing: 国内开发者的开源项目,功能比较全。
  • Prometheus + Grafana: 后端常用的监控方案,前端通过埋点 SDK 上报数据,Grafana 做可视化面板。
  • Sentry (Self-hosted): Sentry 提供私有化部署版本,但维护成本(服务器资源、数据库)较高。

# 📦 方案 C:简单埋点 (轻量级)

  • Google Analytics (GA4): 免费,主要看流量和简单的性能数据,查 Bug 能力弱。
  • Lighthouse (本地): 不是实时监控,但适合开发阶段跑分优化。

# 5. 核心技术实现与难点

# 5.1 白屏监控的实现方案

# ✅ 一、什么是白屏?

白屏指页面加载后,用户看到几秒钟“什么都没有渲染”,常见原因:

  • JS 报错导致根节点没挂载
  • 资源加载失败(CSS/JS)
  • 后端返回空 HTML
  • 路由跳转异常(SPA)
  • 渲染被长任务阻塞
  • 第三方脚本卡住

白屏上报的核心: 发现用户看到“全白”的画面并记录上下文信息。


# ✅ 二、白屏检测的 3 种实战方案(按可靠性排序)

# 方案 1:关键 DOM 探测(推荐)

适合所有 SPA/SSR 项目,准确率最高。

原理: 判断首屏是否成功渲染关键内容(主容器、关键节点、LCP 元素等)

# ✔ 可直接用的代码
(function() {
  const TIMEOUT = 5000; // 5s 内不出现关键节点 → 白屏

  function isWhiteScreen() {
    // 关键 DOM 节点
    const root = document.getElementById('app') || document.getElementById('root');
    if (!root) return true;

    // 内容为空、或只有 loading skeleton
    const text = root.innerText.trim();
    const hasMeaningfulContent = text.length > 0;

    // 检测是否有 LCP 元素
    const hasLCP = performance.getEntriesByType('largest-contentful-paint').length > 0;

    return !(hasMeaningfulContent || hasLCP);
  }

  setTimeout(() => {
    if (isWhiteScreen()) {
      reportWhiteScreen();
    }
  }, TIMEOUT);

  function reportWhiteScreen() {
    const data = {
      type: 'white-screen',
      url: location.href,
      ua: navigator.userAgent,
      timestamp: Date.now(),
      html: document.documentElement.outerHTML.slice(0, 500), // 采样
      performance: {
        fcp: performance.getEntriesByName('first-contentful-paint')[0]?.startTime,
        lcp: performance.getEntriesByType('largest-contentful-paint')[0]?.startTime,
      }
    };
    navigator.sendBeacon('/monitor/white-screen', JSON.stringify(data));
    console.warn('White Screen Detected', data);
  }
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

这个版本可直接用于生产环境。


# 方案 2:像素采样检测(更严格,但耗性能)

通过 canvas 截取可视区域像素,检测是否纯白/纯灰。

function detectScreenPixels() {
  const canvas = document.createElement('canvas');
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  const ctx = canvas.getContext('2d');
  ctx.drawImage(document.body, 0, 0);

  const data = ctx.getImageData(0, 0, 10, 10).data;
  let sum = 0;
  for (let i = 0; i < data.length; i += 4) {
    sum += data[i] + data[i + 1] + data[i + 2];
  }
  const avg = sum / (data.length / 4);

  return avg > 230; // 过于亮 = 白屏
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

⚠ 不推荐默认启用(成本高、部分浏览器不支持 body 截图)。


# 方案 3:超时 + DOM 无变化(轻量 fallback)

5s 内 DOM 没有变化,可能白屏:

let lastDOMCount = 0;

setTimeout(() => {
  const currentCount = document.getElementsByTagName('*').length;
  if (currentCount === lastDOMCount || currentCount < 10) {
    reportWhiteScreen();
  }
}, 5000);

document.addEventListener('DOMContentLoaded', () => {
  lastDOMCount = document.getElementsByTagName('*').length;
});
1
2
3
4
5
6
7
8
9
10
11
12

适合 老项目、轻监控。


# ✅ 三、白屏上报的数据格式(建议)

统一结构便于分析:

{
  "type": "white-screen",
  "url": "https://xxx.com/page",
  "timestamp": 1710000000000,
  "ua": "Mozilla/5.0 ...",
  "domCount": 4,
  "fcp": 2200,
  "lcp": 0,
  "stack": "Error stack if available",
  "network": {
    "rtt": 50,
    "downlink": 4
  },
  "htmlSample": "<html>..."
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# ✅ 四、如何减少误报?

# 1. 检查是否有 LCP

有 LCP ⇒ 不算白屏 (Chrome 官方建议)

# 2. 检查是否存在 skeleton(骨架屏)

如果项目会先渲染 skeleton,要加入白名单:

if (document.querySelector('.skeleton')) return;
1
# 3. 检查是否用户主动切走 tab

若页面被后台化,白屏检测应跳过:

document.visibilityState === "visible"
1
# 4. 分地域 + 设备聚合

例如低端安卓设备白屏率高,不是 bug,是性能限制。


# ✅ 五、白屏监控仪表盘(指标建议)

你可以在 Grafana/Sentry/Datadog 中做 3 个核心指标:

# 1. 白屏率(主指标)
白屏次数 / PV
1
# 2. LCP = 0 的比例(辅助指标)
# 3. 白屏分布
  • 终端:安卓 / iOS / 桌面
  • 地域:省份 / 国家
  • 网络:4G / WiFi
  • 页面:URL pattern

这可以快速定位到具体场景。


白屏诊断流程

  1. 看 RUM 数据:FCP/LCP/INP 及地域/设备分布,确定是否普遍或个别设备/地区问题。
  2. 回放/filmstrip:用 Lighthouse lab filmstrip 或 RUM 视频/回放找出“最大元素什么时候出现”。(若 LCP 总是某张大图 → 优化该图)([web.dev][3])
  3. 查看网络面板:是否资源被阻塞(CSS/JS/Fonts)或第三方慢。
  4. 查看主线程:是否被长任务阻塞(导致渲染被延后)。
  5. 施策(最快路径):保证首屏 HTML 直接可渲染 + preload 关键资源 + 异步非关键 JS。

工具推荐(监控 + 本地调试)

  • RUM/监控:Sentry / Datadog RUM / NewRelic Browser / 自建收集 + Grafana
  • 本地/CI 测试:Lighthouse(或 Lighthouse CI) + PageSpeed Insights + WebPageTest(filmstrip)
  • 诊断:Chrome DevTools(Performance 面板 + Lighthouse) + web-vitals(上报)([Chrome for Developers][2])

# 5.2 错误捕获与上报

# 跨域的js运行错误捕获

只能捕获错误但不能拿到具体信息

  1. 在script标签增加crossorigin属性(在客户端做)
  2. 设置js资源响应头Access-Control-Allow-Origin: * (这里可以是*也可以是域名。需要在服务端做)
# 错误上报
  1. 采用ajax通信方式上报(一般不用这种方式)
  2. 利用Image对象上报(如google的gaa,国内的cnzz都是image方式上报)
  3. 监听请求进行上报时可以重写xmlHttpRequest的方式,重写send函数,执行完上报,在用老的send函数call执行发送请求
export function injectXHR() {
    let XMLHttpRequest = window.XMLHttpRequest;
    let oldOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function (method, url, async, username, password) {
        if (!url.match(/logstores/) && !url.match(/sockjs/)) {
            this.logData = {
                method, url, async, username, password
            }
        }
        return oldOpen.apply(this, arguments);
    }
    let oldSend = XMLHttpRequest.prototype.send;
    let start;
    XMLHttpRequest.prototype.send = function (body) {
        if (this.logData) {
            start = Date.now();
            let handler = (type) => (event) => {
                let duration = Date.now() - start;
                let status = this.status;
                let statusText = this.statusText;
                tracker.send({//未捕获的promise错误
                    kind: 'stability',//稳定性指标
                    type: 'xhr',//xhr
                    eventType: type,//load error abort
                    pathname: this.logData.url,//接口的url地址
                    status: status + "-" + statusText,
                    duration: "" + duration,//接口耗时
                    response: this.response ? JSON.stringify(this.response) : "",
                    params: body || ''
                })
            }
            this.addEventListener('load', handler('load'), false);
            this.addEventListener('error', handler('error'), false);
            this.addEventListener('abort', handler('abort'), false);
        }
        oldSend.apply(this, arguments);
    };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

// 利用image方式更简单,一行就可以轻松实现上报

(new Image()).src="http://xxx.com/abc?r=yyy"
1

# 5.3 性能数据采集

  1. 监测与回归防护* 在 CI/PR 中加入 Lighthouse 或 PSI 检查(关注 LCP/FCP/INP 回归),并在发布后用 RUM 数据验证。
  2. PerformanceTiming (opens new window)对象包含延迟相关的性能信息
  3. PerformanceObserver.observe (opens new window)方法用于观察传入的参数中指定的性能条目类型的集合。当记录一个指定类型的性能条目时,性能监测对象的回调函数将会被调用
  • DOMContentLoaded含义 (opens new window)
  • FMP(First Meaningful paint)(首次有意义绘制) (opens new window)
// 安装:npm i web-vitals
import {getLCP, getFCP, getCLS, getINP} from 'web-vitals';

function sendMetric(name, value, extra = {}) {
  navigator.sendBeacon('/rum/metric', JSON.stringify({name, value, extra, url: location.href, ts: Date.now()}));
}

getLCP(metric => sendMetric('LCP', metric.value));
getFCP(metric => sendMetric('FCP', metric.value));
getCLS(metric => sendMetric('CLS', metric.value));
getINP(metric => sendMetric('INP', metric.value));
1
2
3
4
5
6
7
8
9
10
11

web-vitals 会把浏览器 API 的差异封装好,便于把 LCP/FCP/INP/CLS 可靠地上报到后端进行聚合(CrUX 风格的 75th 百分位判断)。([web.dev]) 原生测 LCP(简单示例)

new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // entry.startTime 是 LCP 的时间(ms)
    console.log('LCP candidate', entry.startTime, entry);
    navigator.sendBeacon('/rum/metric', JSON.stringify({name:'LCP', value: entry.startTime}));
  }
}).observe({type: 'largest-contentful-paint', buffered: true});
1
2
3
4
5
6
7

(注意:要结合页面 visibility 和最终候选项来确定最终 LCP,实际库会处理这些细节。)([web.dev])

# 5.4 实施难点与解决方案(SourceMap、采样、过滤)落地实施的难点与技巧

如果你准备开始做监控,注意以下几个“坑”:

  1. SourceMap 安全问题:

    • 线上报错通常是压缩后的代码(如 a.js:1:503),看不懂。
    • 解决: 需要在构建流程(CI/CD)中把 .map 文件上传到监控平台(如 Sentry),但绝对不要部署到公网服务器上,否则源码会泄露。
  2. 海量日志削峰:

    • 如果某个 Bug 导致无限循环报错,瞬间上万条日志会打爆服务器或耗尽 SaaS 配额。
    • 解决: 前端 SDK 必须做采样 (Sampling) 和 防抖 (Throttle)。例如:同一个错误 1 秒内只报 1 次,或者只采集 10% 的性能数据。
  3. 无意义的报错:

    • Script error. (跨域脚本错误) 或 浏览器插件注入的错误。
    • 解决: 配置过滤规则(Ignore List),忽略非业务域名的脚本错误和常见的插件错误。

# 6. 总结与实施路线

💡 建议的起步路线:

  1. 第一阶段 (止血): 接入 Sentry (免费版或私有部署),重点监控 JS 报错 和 白屏。先保证程序不崩。
  2. 第二阶段 (优化): 引入 web-vitals 库,上报 LCP 和 INP 数据,结合性能面板优化首屏速度。
  3. 第三阶段 (洞察): 结合 rrweb 实现用户操作回放,解决那些“我本地复现不出来”的诡异 Bug。

# 7. 参考资料与扩展阅读

# Web Vitals

  • TTFB(time to first byte)(首字节时间) 是指浏览器发起第一个请求到数据返回第一个字节所消耗的时间,这个时间包含了网络请求时间、后端处理时间
  • FP(First Paint)(首次绘制) 首次绘制包括了任何用户自定义的背景绘制,它是将第一个像素点绘制到屏幕的时刻
  • FCP(First Content Paint)(首次内容绘制) 首次内容绘制是浏览器将第一个DOM渲染到屏幕的时间,可以是任何文本、图像、SVG等的时间
  • FMP(First Meaningful paint)(首次有意义绘制) (opens new window) 首次有意义绘制是页面可用性的量度标准
  • FID(First Input Delay)(首次输入延迟) 用户首次和页面交互到页面响应交互的时间
  • LCP (Largest Contentful Paint)(最大内容渲染) (opens new window) 代表在viewport中最大的页面元素加载的时间。2.5s以内是好的,2.5s到4s需要优化。
  • DCL (DomContentLoaded)(DOM加载完成) 当 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,无需等待样式表、图像和子框架的完成加载
  • L (onLoad) 当依赖的资源全部加载完毕之后才会触发
  • TTI (Time to Interactive) 可交互时间 用于标记应用已进行视觉渲染并能可靠响应用户输入的时间点
  • FID First Input Delay(首次输入延迟) 用户首次和页面交互(单击链接,点击按钮等)到页面响应交互的时间。100ms以内是好的,100ms到300ms需要改善。
  • TBT(Total blocking time)主线程累计阻塞时间
  • CLS(Cummulative layout time)累计布局偏移,小于0.1是好的,0.1s到0.25s需要改善。
  • 卡顿 超过50ms的长任务。响应用户交互的响应时间如果大于100ms,用户就会感觉卡顿
  • paint timing (opens new window)
上次更新: 2025/12/09, 03:15:37
地图标绘--射线法来计算点在多边形内
axios 与 promise

← 地图标绘--射线法来计算点在多边形内 axios 与 promise→

Copyright © 2015-2025 Glitz Ma
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式