前端监控笔记(一)

错误监听

1. 基于 onerror、unhandledrejection 事件错误监听

  • 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)

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

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.target 与 event.currentTarget。
  • setTimeout / setInterval / requestAnimationFrame:错误兜底与调度耗时分析。

总结

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

参考内容

相关推荐
涛涛ing1 小时前
2026年最值得关注的前端变化,藏在“默认值”里
前端
Go Work2 小时前
两个等值条件摆着,MySQL 偏要全表扫
经验分享·笔记
Oll Correct2 小时前
Adobe illustrator 案例十四:花元素标志草图绘制
笔记·ui·adobe·illustrator
亿元程序员3 小时前
太牛了!这AI做出来的游戏,居然还能变现!
前端
hellice3 小时前
闪传 FlashDrop 实战:我用 300 行 Node 代码做了个「4 位取件码」临时文件中转站
前端·产品
用户0829773260053 小时前
RTX 5500 MIG 踩坑记录:切分之后模型跑不起来,排错整整一下午
前端
Lank_M3 小时前
AI 放大后的图更清楚,但它不能当原图入库
前端
Swot3 小时前
SvelteKit 数据流研究:`+page.js` 和 `+page.server.js` 到底谁在跑,数据又去了哪儿
前端·后端
用户4147679304153 小时前
Windows 触摸屏上 Swiper 轮播滑不动?可能是内部滚动容器抢走了你的手势
前端
张明君644513 小时前
从一个 Referer 引发的 403,到 SSR 服务失稳:Astro 流式渲染与缓存失效分析
前端