source-map 排查线上问题

483 阅读2分钟

背景

目前公司没有  sentry, 打包后的代码也没有  souce-map文件, 出现线上问题, 比较难排查

案例:

现在在控制台拿到这样一段报错信息

chunk-libs.74f9af28.js:1

    Uncaught TypeError: e is not a function
    at chunk-libs.74f9af28.js:1:76904
    at Array.forEach (<anonymous>)
    at new v (chunk-libs.74f9af28.js:1:76876)
    at 56d7 (app.42ce26c3.js:1:90547)
    at b (mainLogin:197:5423)
    at 0 (app.42ce26c3.js:1:96)
    at b (mainLogin:197:5423)
    at n (mainLogin:197:1174)
    at Array.e [as push] (mainLogin:197:1037)
    at app.42ce26c3.js:1:53

image.png

从上面的信息中, 我们能获取到如下信息

  • 打包后文件的 hash, 以及文件的行, 列

  • 报错的信息 

  • 报错的业务文件   mainLogin

操作步骤

1、 本地代码 拉取 线上发版的代码分支, 注意, git  分支的hash一定要是 发版的hash,  否则本地打包后的 source-map 会变化, 比如  2022-07-29 发版本  release 分支 对应的  git hash 是  e2955de

2、调整 vue.config.js 配置 productionSourceMap改为true

3、 本地 npm run build  打包, dist目录会存在  .map 的对应文件

image.png

4、本地创建一个 test-map 目录。用于执行 node 脚本

npm i source-map

image.png

5、app.js完整代码在文末, 根据日志调整  searchSource  里面的文件指向, 还有对应报错信息里面的  行, 列

6、 命令行运行 node app.js

7、根据错误信息, 从上往下进行分析, 报错信息是 错误日志栈, 最先调用的地方,最先报错, 日志从上往下分析

如果是 类库报错, 则更新类库

如果是业务代码报错, 则最先报错的地方应该是 src/views/xxx.vue  文件。 然后再去看对应的文件

从下面的输出判断

// {
//   source: 'webpack:///src/store/index.js',
//   line: 28,
//   column: 23,
//   name: 'Store'
// }

我们找到 项目中的 src/sotre/index.js 指向这里, 和上面的日志是一一对应的

完整代码

const fs = require('fs')
const sourceMap = require('source-map');


const readFile = function (filePath) {
  return new Promise(function (resolve, reject) {
    fs.readFile(filePath, { encoding: 'utf-8' }, function (error, data) {
      if (error) {
        console.log(error)
        return reject(error);
      }
      resolve(JSON.parse(data));
    });
  });
};

// Find the source location
async function searchSource (filePath, line, column) {
  const rawSourceMap = await readFile(filePath)
  const consumer = await new sourceMap.SourceMapConsumer(rawSourceMap);
  const res = consumer.originalPositionFor({
    'line': line,
    'column': column
  });
  consumer.destroy()
  return res
}


searchSource('./chunk-libs.74f9af28.js.map', 1, 76904).then(res => {
  console.log(res)
})
// {
//   source: 'webpack:///node_modules/vuex/dist/vuex.esm.js',
//   line: 427,
//   column: 45,
//   name: 'plugin'
// }

// ----- 分割

// searchSource('./chunk-libs.74f9af28.js.map', 1, 76876).then(res => {
//   console.log(res)
// })
// {
//   source: 'webpack:///node_modules/vuex/dist/vuex.esm.js',
//   line: 427,
//   column: 10,
//   name: 'forEach'
// }

// =============
// searchSource('./app.40e8b854.js.map', 1, 90547).then(res => {
//   console.log(res)
// })
// {
//   source: 'webpack:///src/store/index.js',
//   line: 28,
//   column: 23,
//   name: 'Store'
// }