Skip to content

做前端错误监控时,有什么办法能将报错的源码地址,包括代码行数也进行上报吗?

可以结合 Source Map 实现。

一、基础错误捕获

1. 全局错误监听

javascript
window.addEventListener('error', (event) => {
  const { filename, lineno, colno, error } = event;
  reportError({
    file: filename,  // 报错文件URL
    line: lineno,    // 行号
    column: colno,   // 列号
    stack: error?.stack // 错误堆栈
  });
});

2. Promise 异常捕获

javascript
window.addEventListener('unhandledrejection', (event) => {
  const stack = event.reason?.stack;
  extractSourceLocation(stack); // 从堆栈解析源码位置
});

二、关键方法

1. Source Map 解析

适用场景:生产环境压缩代码定位

javascript
// 示例堆栈解析
function parseStack(stack) {
  // 错误堆栈示例:
  // "at MyComponent (https://example.com/static/js/main.4a2b3c.js:45:12)"
  const match = /at\s+.+\s\(?(.+):(\d+):(\d+)\)?/.exec(stack);
  return match ? {
    file: match[1],
    line: parseInt(match[2]),
    column: parseInt(match[3])
  } : null;
}

配套工作流

  1. 构建时生成 Source Map 文件
  2. 将 Source Map 存储在受限访问的服务器
  3. 通过错误上报的行列号反向查询源码

2. 构建配置示例(Webpack)

javascript
// webpack.config.js
module.exports = {
  devtool: 'hidden-source-map', // 不暴露sourcemap的配置
  output: {
    sourceMapFilename: '[file].map[query]',
    devtoolModuleFilenameTemplate: 'webpack:///[resource-path]'
  }
};

三、具体代码定位方案

1. AST 代码标记

在编译阶段注入代码位置信息:

javascript
// Babel插件示例
const { declare } = require('@babel/helper-plugin-utils');

module.exports = declare((api) => {
  return {
    visitor: {
      FunctionDeclaration(path) {
        const { loc } = path.node;
        path.addComment('leading', `loc:${loc.start.line}:${loc.start.column}`);
      }
    }
  };
});

2. 错误上下文捕获

javascript
function captureCodeContext(sourceUrl, line, range = 5) {
  // 通过fetch获取源文件(需配置CORS)
  return fetch(sourceUrl)
    .then(res => res.text())
    .then(code => {
      const lines = code.split('\n');
      return lines.slice(Math.max(0, line - range), line + range);
    });
}

四、生产环境方案

1. 安全策略

措施说明
Source Map 访问控制仅限内网访问或添加Auth验证
敏感信息脱敏上报前过滤API密钥等敏感内容
数据加密对行列号等关键信息加密传输

2. 上报数据格式示例

json
{
  "error": "TypeError: Cannot read property 'x' of undefined",
  "location": {
    "file": "https://cdn.example.com/static/js/chunk-abc123.js",
    "line": 45,
    "column": 12,
    "compiled": true,
    "context": ["  const data = response.json();", "  return data.x.y; // Error here"]
  },
  "env": {
    "userAgent": "Chrome/103.0",
    "url": "/product/123"
  }
}

五、开源方案推荐

1. Sentry 的实现方式

javascript
Sentry.init({
  dsn: 'YOUR_DSN',
  integrations: [
    new Sentry.Integrations.TryCatch({ 
      XMLHttpRequest: true 
    }),
  ],
  beforeSend(event) {
    // 增强位置信息
    return enrichWithSourceMap(event);
  }
});

2. Fundebug 的源码定位

javascript
fundebug.init({
  apikey: 'YOUR_KEY',
  filters: {
    isFix: function(data) {
      // 自动解析sourcemap
      return data.sourceMap === true;
    }
  }
});

题目要点:

  1. 基础捕获:利用 error 事件获取行列号,但生产环境代码通常被压缩
  2. Source Map:必须与构建流程结合,且需保证安全性
  3. 上下文增强:通过动态获取源码片段辅助诊断
  4. 错误去重:根据文件名+行号+错误类型生成唯一指纹
  5. 性能权衡:Source Map 解析会增加后端处理开销

推荐方案

  • 中小项目:直接使用 Sentry/Fundebug 等成熟方案
  • 大型项目:自建 Source Map 解析服务 + ELK 存储分析
  • 敏感场景:开发环境保留 Source Map,生产环境通过 CI 管道关联