前端监控笔记(一)

错误监听

1. 基于 onerrorunhandledrejection 事件错误监听

  • window.onerror 属于 DOM0 级别接口,主要捕获 JS 运行时错误。
  • addEventListener('error', ..., true) 可在捕获阶段监听,能额外捕获资源加载错误(图片、脚本、样式、字体等)。
  • 通过 event.target.src || event.target.href 可区分资源错误与普通运行时错误。
  • unhandledrejection 用于捕获未被处理的 Promise 拒绝错误。
javascript 复制代码
window.addEventListener(
  'error',
  (event) => {
    const target = event.target;
      
    // 判断错误类型
    const isResourceError =
      !!target &&
      ('src' in target || 'href' in target);

    if (isResourceError) {
      const url =
        target.src ||
        target.href ||
        'unknown';
      captureException({ type: 'resource_error', url });
      return;
    }

    captureException(event.error || new Error(event.message));
  },
  // 配置 true 支持捕获阶段监听
  true
);

window.addEventListener('unhandledrejection', (event) => {
  // 统一错误上报入口
  captureException({ type: 'unhandledrejection', reason: event.reason });
});

2. 跨域脚本错误捕获

跨域脚本默认可能只看到 Script error。要拿到完整堆栈,需要前后端同时配置:

  • 前端标签增加 crossorigin
  • 服务端返回对应 CORS 头。
html 复制代码
<script src="https://cdn.example.com/app.js" crossorigin="anonymous"></script>
http 复制代码
Access-Control-Allow-Origin: https://your-site.com
Access-Control-Allow-Credentials: true

注意 :若使用 crossorigin="use-credentials",服务端必须允许携带凭据,且 Access-Control-Allow-Origin 不能为 *

3. 异步任务错误捕获(包装全局 API)

setTimeoutsetIntervalrequestAnimationFrame 等异步回调可通过包装函数统一兜底,且不改变原有行为。

javascript 复制代码
function wrap(fn) {
  return function wrapped(...args) {
    try {
      // 保留 this 和参数,保持原函数行为一致
      return fn.apply(this, args);
    } catch (err) {
      // 捕获后继续抛出,避免吞错
      captureException(err);
      throw err;
    }
  };
}

const originalSetTimeout = window.setTimeout;
window.setTimeout = function (fn, delay, ...rest) {
  if (typeof fn === 'function') {
    return originalSetTimeout(wrap(fn), delay, ...rest);
  }
  return originalSetTimeout(fn, delay, ...rest);
};

4. 请求错误监听(XHR / Fetch)

XHR 拦截
javascript 复制代码
if (window.XMLHttpRequest) {
  const originalSend = XMLHttpRequest.prototype.send;

  function handleXhrEvent(event) {
    const xhr = event.currentTarget;
    if (xhr && xhr.status >= 400) {
      captureException({
        type: 'xhr_error',
        status: xhr.status,
        responseURL: xhr.responseURL,
      });
    }
  }

  XMLHttpRequest.prototype.send = function (...args) {
    // 统一监听请求失败与异常中断
    this.addEventListener('error', handleXhrEvent);
    this.addEventListener('abort', handleXhrEvent);
    this.addEventListener('load', handleXhrEvent);
    return originalSend.apply(this, args);
  };
}
Fetch 拦截
javascript 复制代码
if (window.fetch) {
  const originalFetch = window.fetch;

  window.fetch = function (...args) {
    return originalFetch
      .apply(this, args)
      .then((res) => {
        if (!res.ok) {
          captureException({
            type: 'fetch_error',
            status: res.status,
            url: res.url,
          });
        }
        return res;
      })
      .catch((error) => {
        captureException({ type: 'fetch_exception', error });
        throw error;
      });
  };
}

5. Vue 错误监听

Vue 提供组件级 errorCaptured 与应用级 app.config.errorHandler。通常建议:

  • errorCaptured:用于局部兜底和降级。
  • errorHandler:用于全局聚合与统一上报。
javascript 复制代码
app.config.errorHandler = (err, instance, info) => {
  const componentName =
    instance?.$options?.name ??
    instance?.$options?.__name ??
    'AnonymousComponent';

  captureException({
    type: "vue_error",
    error: err,
    component: componentName,
    hook: info,
  });

  // 如需保留默认日志输出,可手动补充
  console.error(err);
};

无侵入拦截(Monkey Patching)

监控 SDK、埋点库常使用 Monkey Patching 实现无侵入注入。

本质是保留原函数引用,在外层扩展逻辑后再调用原始行为。

  1. 保留 this 与参数上下文。
  2. 提供 unpatch,支持恢复原行为(便于测试和热更新)。
  3. 通过标记位避免重复 patch。
javascript 复制代码
function patchMethod(obj, name, replacement) {
  const original = obj[name]
  if (typeof current !== 'function') return () => {};
  if (current.__patched__) return () => {};

  obj[name] = function(...args) {
    // 把原函数 original 传进去,让 replacement 决定何时调用
    return replacement.call(this, original.bind(this), args)
  }
  // 保存引用方便回退使用
  obj[name].__original = original
  obj[name].__patched  = true

   // 返回 unpatch 函数
  return () => { 
  		obj[name] = original
  		delete obj[name].__original
  		delete obj[name].__patched
  	} 
}

// 使用示例:拦截 fetch,返回的unpatch 函数讲内容还原成默认值
const unpatch = patchMethod(window, 'fetch', (original, args) => {
  const [url, options] = args
  const start = Date.now()

	// 调用原始函数指向默认行为,然后修改返回内容
  return original(url, options).then(resp => {
    // 这里是记录请求示例
    recordRequest(url, resp.status, Date.now() - start)
    return resp
  })
})

常用场景

  • fetch / XMLHttpRequest:采集请求耗时、状态码、错误率,注入追踪头。
  • console.*:采集日志与面包屑,避免在 patch 内递归调用自身。
  • history.pushState / history.replaceState:路由变化监听,配合 popstate
  • addEventListener:采集交互行为,区分 event.targetevent.currentTarget
  • setTimeout / setInterval / requestAnimationFrame:错误兜底与调度耗时分析。

总结

  • 错误捕获 :通过 onerrorunhandledrejection、资源错误监听、请求拦截与 Vue 全局处理器,基本覆盖前端主要错误来源。
  • 无侵入增强:通过 Monkey Patching 在不改业务调用方式下扩展监控能力,保留原函数行为,同时实现可恢复 patch。

参考内容

相关推荐
夏暖冬凉2 小时前
前端大文件上传
前端
Highcharts.js2 小时前
Highcharts Grid Lite:企业免费表格数据的基本工具
前端·javascript·信息可视化·免费·highcharts·表格工具
老萬頭2 小时前
【技术深水区】抖音 WEB 端逆向:从零到一拿下 a_bogus 参数
前端·爬虫·python
程序员小李白2 小时前
Vue 组件通信 极简速记版
前端·javascript·vue.js
光影少年2 小时前
跨域问题如何解决?
前端·nginx·前端框架
C澒2 小时前
微前端容器标准化 —— 公共能力篇:通用监控能力
前端·架构
念念不忘 必有回响2 小时前
Next.js 14-16 全栈开发实战:从 App Router 核心原理到 Server Actions 深度剖析
前端·nextjs
英俊潇洒美少年2 小时前
React 16 → 17 → 18 → 19 完整区别
前端·javascript·react.js