首页/文章/前端监控

前端错误收集方式

2025-05-29
14765 分钟
...

在讲错误收集方式之前,先理清一下前端编码时有那些常见错误

 

 

 

 


前端错误收集是保障用户体验、排查线上问题的关键手段。本文将从多个角度详解前端错误收集的各种方式,包括不同框架(如 React、Vue)、JavaScript 执行环境中的同步/异步/Promise 错误、资源加载错误(如图片、script 加载失败)等。以下是全面的错误捕获机制总结:


 

 

🌐 一、通用错误收集方式(原生 JavaScript)

1. try...catch 捕获同步代码错误

try {
  
  let result = a.b; 
} catch (err) {
  console.error('捕获同步错误:', err);
}

注意:

  • 只能捕获同步代码错误,不能捕获异步或事件回调内的错误。

2. window.onerror

window.onerror = function (message, source, lineno, colno, error) {
  console.error('window.onerror 捕获:', { message, source, lineno, colno, error });
};

特性:

  • 可捕获运行时错误,包括未捕获的异常。
  • 跨域脚本错误默认不会暴露具体信息,需设置 crossorigin 属性和响应头。

3. window.addEventListener('error') 捕获资源加载错误

window.addEventListener('error', function (event) {
  if (event.target instanceof HTMLScriptElement || event.target instanceof HTMLImageElement) {
    console.error('资源加载错误:', event.target.src || event.target.href);
  } else {
    console.error('运行时错误:', event.message);
  }
}, true); 

优点:

  • 能捕获 <img>, <script>, <link> 等静态资源加载错误。

4. unhandledrejection 捕获未处理的 Promise 异常

window.addEventListener('unhandledrejection', function (event) {
  console.error('未处理的 Promise 异常:', event.reason);
});

场景:

  • Promise 的 .catch() 未添加或写在不合适位置。

⚛️ 二、React 中的错误捕获

1. React 16+ 错误边界(Error Boundaries)

class ErrorBoundary extends React.Component {
  state = { hasError: false, error: null };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    console.error('React 错误捕获:', error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h2>出错了</h2>;
    }
    return this.props.children;
  }
}

限制:

  • 只能捕获子组件生命周期、render、构造函数中的错误。

  • 不能捕获:

    • 异步代码(如 setTimeout
    • 事件处理函数错误
    • SSR 期间的错误

🐸 三、Vue 中的错误捕获

1. Vue 2.x errorHandler

Vue.config.errorHandler = function (err, vm, info) {
  console.error('Vue 错误:', err, info);
};

2. Vue 3.x app.config.errorHandler

const app = Vue.createApp(App);

app.config.errorHandler = (err, instance, info) => {
  console.error('Vue3 错误:', err, info);
};

3. 异步函数中的错误

异步函数内的错误需要使用 try/catch 或监听 unhandledrejection


🧠 四、异步任务中的错误处理方式

1. setTimeoutsetInterval

setTimeout(() => {
  try {
    throw new Error('异步定时错误');
  } catch (err) {
    console.error('捕获异步错误:', err);
  }
}, 1000);

2. Promise 中的异常

new Promise((resolve, reject) => {
  throw new Error('Promise 内部异常'); 
});

3. async/await 异常处理

async function test() {
  try {
    await fetch('...');
    throw new Error('异步错误');
  } catch (e) {
    console.error('async/await 错误:', e);
  }
}

🖼️ 五、资源加载错误(图片、脚本等)

图片加载失败

<img src="wrong.png" onerror="console.error('图片加载失败')" />

脚本加载失败

<script src="wrong.js" onerror="console.error('JS 加载失败')"></script>

全局监听

window.addEventListener('error', function (e) {
  if (e.target.tagName === 'IMG' || e.target.tagName === 'SCRIPT') {
    console.error('资源加载失败:', e.target.src || e.target.href);
  }
}, true);

 

 


📦 六、前端错误上报实践建议

  1. 统一封装错误上报工具

    function reportError(errorInfo) {
      
      fetch('/log', {
        method: 'POST',
        body: JSON.stringify(errorInfo),
      });
    }
    
  2. 错误去重、限频

    • 防止相同错误反复上报。
  3. 结合 Source Map

    • 错误行号定位需要 source map 还原。
  4. 配合监控平台(如 Sentry、Fundebug、LogRocket)

    • 可视化查看错误堆栈、用户行为路径等。

✅ 总结

类型捕获方式说明
同步代码异常try...catch推荐搭配日志上报使用
全局 JS 错误window.onerror支持堆栈、来源信息
静态资源加载失败window.addEventListener('error', ..., true)捕获 <script>/<img>/<link> 加载失败
Promise 未处理异常window.unhandledrejection捕获 Promise 中未被 .catch 处理的异常
React错误边界组件捕获渲染和生命周期异常
Vueconfig.errorHandler支持全局组件异常处理
async/await 异常try/catch手动捕获为主

如需我帮你生成一个完整的错误收集 SDK、结合你项目的架构,欢迎告诉我。


下面是一个通用的前端错误收集 SDK,适用于纯 JavaScript 应用,也兼容 React、Vue 等框架。具备如下能力:

  • 捕获同步错误(try/catch)
  • 捕获未处理的 Promise 错误
  • 捕获资源加载失败(如图片、脚本)
  • 可扩展支持自定义上报方式
  • 可用于构建自己的埋点/监控平台

✅ 错误收集 SDK 示例(error-tracker.js

class ErrorTracker {
  constructor(options = {}) {
    this.reportUrl = options.reportUrl || '/report-error';
    this.extraContext = options.context || {};
    this.init();
  }

  init() {
    this.bindGlobalError();
    this.bindPromiseRejection();
    this.bindResourceError();
  }

  bindGlobalError() {
    window.onerror = (message, source, lineno, colno, error) => {
      const errorInfo = {
        type: 'js-error',
        message,
        source,
        lineno,
        colno,
        stack: error?.stack,
        ...this.extraContext,
      };
      this.report(errorInfo);
    };
  }

  bindPromiseRejection() {
    window.addEventListener('unhandledrejection', (event) => {
      const errorInfo = {
        type: 'unhandledrejection',
        reason: event.reason?.message || String(event.reason),
        stack: event.reason?.stack || null,
        ...this.extraContext,
      };
      this.report(errorInfo);
    });
  }

  bindResourceError() {
    window.addEventListener(
      'error',
      (event) => {
        if (event.target && (event.target.src || event.target.href)) {
          const errorInfo = {
            type: 'resource-error',
            tagName: event.target.tagName,
            url: event.target.src || event.target.href,
            outerHTML: event.target.outerHTML,
            ...this.extraContext,
          };
          this.report(errorInfo);
        }
      },
      true 
    );
  }

  report(errorData) {
    try {
      navigator.sendBeacon
        ? navigator.sendBeacon(this.reportUrl, JSON.stringify(errorData))
        : fetch(this.reportUrl, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(errorData),
          });
    } catch (err) {
      console.warn('错误上报失败:', err);
    }
  }

  
  captureException(error, context = {}) {
    const errorInfo = {
      type: 'manual-capture',
      message: error.message,
      stack: error.stack,
      ...this.extraContext,
      ...context,
    };
    this.report(errorInfo);
  }
}

export default ErrorTracker;

🧪 使用方法

1. 初始化

import ErrorTracker from './error-tracker';

const tracker = new ErrorTracker({
  reportUrl: 'https://your-domain.com/log-collector',
  context: {
    appVersion: '1.0.0',
    userId: '123456',
  },
});

2. 手动捕获异常(适用于 try/catch 或自定义埋点)

try {
  throw new Error('业务异常');
} catch (e) {
  tracker.captureException(e, { module: 'UserProfile' });
}

🛠️ 后端上报接口示例(Node.js)

app.post('/report-error', express.json(), (req, res) => {
  console.log('错误上报:', req.body);
  
  res.status(200).send('ok');
});

💡 可扩展建议

  • 加入节流、去重机制(防止重复错误刷爆接口)
  • 支持用户行为轨迹记录(如点击、页面跳转)
  • 上报前过滤第三方脚本错误
  • 使用 Source Map 解码堆栈信息

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
前端监控2024-10-12
前端小玩具~错误捕获定位工具
正文 我们在平时的工作中,开发功能的同时不可能把场景考虑的面面俱到,而生产环境往往情况是非常复杂的,用户录入进去的数据总是千奇百怪,那如果遇到问题的话,我们又要如何进行排查呢?总不可能让用户录个屏吧哈哈 所以我们就出现了 前端埋点 的操作,不过埋点的方向以及文章都挺多的,也都挺复杂的,这篇文章我们就讲一个比较有趣的 错误捕获思路 。 我们平时在使用框架开发 遇到bug 时,比如 Vue ,如果是在本地环境,我们在控制台可以很容易的找到出...
学习
前端监控2024-09-19
🍻 前端服务监控原理与手写开源监控框架SDK前端服务监控概述
前端监控的目的很明确,无非就是让我们的产品更完善,更符合我们和用户的需求。运营与产品团队需要关注用户在产品内的行为记录,通过用户的行为记录来优化产品,研发与测试团队则需要关注产品的性能以及异常,确保产品的性能体验以及安全迭代。 而一个完整的前端监控平台至少需要包括三个部分: 数据采集与上报、数据整理和存储、数据展示 。算上需要监控的项目的话,也就是说,至少需要4个项目才能完整的记录前端监控的内容。 下图是一个完整的前端监控平台需要处理和...
学习
前端监控2024-08-19
前端数据监控平台
比如客户说:你们的页面咋白屏了? 开发:具体可以给我说一下,白屏之前你做了什么吗? 客户:我就点了一下按钮 开发:你能告诉我是哪个页面的哪个按钮吗? 最后,客户极其不耐烦的地丢过来一张没有缺少url的白屏给你看,试图证明他说的是真的,真白屏了。 最后不再理睬你! 开发人员试图找到解决方案,想快速解决线上bug,但是客户却以为你在质疑他。 真的有点秀才遇上兵有理说不清,其实真的没有谁对谁错,只是双方立场不同,作为客户,你的系统bug了,我...
学习
前端监控2024-02-20
前端实现埋点&监控
实现埋点功能的意义主要体现在以下几个方面: 0. 数据采集:埋点是数据采集领域(尤其是用户行为数据采集领域)的术语,它针对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。通过埋点,可以收集到用户在应用中的所有行为数据,例如页面浏览、按钮点击、表单提交等。 1. 数据分析:采集的数据可以帮助业务人员分析网站或者App的使用情况、用户行为习惯等,是后续建立用户画像、用户行为路径等数据产品的基础。通过数据分析,企业可以更好地了解...
学习
前端监控2023-12-19
前端埋点重要环节:前端如何录制用户的操作过程
前言 大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心。 背景 公司有很多的项目,但是并不是每一个项目都很重要,其实重要的项目就那么几个,上面也是很重视这几个项目,尤其是对一些生产问题的关注度很高。 这几天上面交代下来了,需要对这些项目做一些用户行为的记录,主要是为了更好地还原用户在某一个时间点的操作过程 注意点 想要完成这个需求,仔细想了一下,需要注意几个点: 跨框架使用: 这些项目有vue、...
学习
前端监控2023-11-23
Web性能监测的利器Performance Observer
前言 前段时间在研究前端异常监控平台,在思考性能监控时,想到了浏览器自带的观察者以及页面生命周期API 。于是在翻查资料时发现了, API。正好趁着这个机会给大家好好讲讲Performance Observer API Performance Observer(性能观察者) 定义 是一种JavaScript API,用于监测页面性能指标,如资源加载时间、页面渲染时间等。它可以触发回调函数,以便你收集和分析页面性能数据。 优点 1. 性能...
学习

评论

请登录后发表评论

去登录
加载评论中...

目录