likes
comments
collection
share

掌握主动权——系统错误监控助你降成本、提效率(下)

作者站长头像
站长
· 阅读数 10

前言

本文主要记录了我自己在项目中加入错误监控时,服务端的接口开发的实践过程。可能会存在一些问题,有需要完善的部分。欢迎大家评论区指点、讨论。

1、服务端接口实现

服务端我是用的node环境,关于node使用请自行学习,网上资料很多。我这里就记录一下大致流程和遇到的问题。 搭建好服务环境后,首先要满足的就是我们前端需要的接口:

source-map文件上传接口

上篇文章我们前端项目中自己写了webpack插件,在打包时就会生成source-map文件,同时调用上传接口。这个接口做的事情很简单,就是接收数据、创建文件夹、将source-map内容写入磁盘(写入磁盘时需要注意的是每次上传后的内容要覆盖上一次内容)

  async function upload (req, res) {
    let { mapsArr } = req.body
    // 要存储的源码路径
    const filePath = path.join(__dirname, '../', 'source-maps');
    // 检测文件夹是否存在
    if (!fs.existsSync(filePath)) {
      fs.mkdirSync(filePath)
    }
    // 将source-map内容写入磁盘
    const result = await writeSourceMapFile(filePath, mapsArr)
  }

错误信息上报接口

这个接口主要是需要前端用一个 tyep 字段做一个区分是资源错误还是其他错误。因为资源错误时是拿不到line、column等信息的,所以资源错误时就不需要去解析了,直接返回当前资源错误的url、src信息。

async function img (req, res) {req.query.data)
  const { stack, ...args } = JSON.parse(req.query.data);
  if (args.type === 'resourceError') {
    const srcName = path.basename(stack.src);
    const obj = {
      ...stack,
      name: srcName,
    }
    return
  }
  // 非资源错误时使用堆栈信息去解析真实信息
  const { line, column, url } = stack
  const basename = path.basename(url);
  // 解析源码返回真实的错误信息
  const sourceDatas = await searchSource({ basename, line, column })
}

2、错误信息回溯(错误解析)

使用source-map插件解析错误

安装 npm i source-map,在错误上报后,我们拿到了关键的 错误栈信息,这时候就需要去解析对应的真实源码信息了

 function searchSource ({ basename, line, column, message }) {
   const filePath = path.join(__dirname,
     "../",
     "source-maps",
     basename+'.js.map')
   const rawSourceMap = fs.readFileSync(filePath, 'utf-8')
   // 创建 SourceMapConsumer 对象
   const consumer = new SourceMapConsumer(rawSourceMap)
   // 解析源码,将第 line 行第 column 列的位置信息,转换为对应真实源代码位置信息
   const res = consumer.originalPositionFor({ line, column });
   // 执行完销毁
   consumer.destroy();
   console.log("最终数据:", res)
 }

掌握主动权——系统错误监控助你降成本、提效率(下)

不出意外的话,到这一步,我们就完全还原了错误发生现场,此时的 source 字段就是真实发生错误的文件,linecolumn 字段就是该错误发生的具体行数列数。后续我们把这些信息存储到数据库后,就可以愉快的进行问题回溯了。

解析错误信息时踩坑

  • 在解析时报错如下 掌握主动权——系统错误监控助你降成本、提效率(下)

    这里是因为在使用 new SourceMapConsumer() 构造函数创建 SourceMapConsumer 对象时,如果传递的 sourceMap 参数不是一个符合 Sourcemap 规范的 JavaScript 对象,则会报错。其中,“version” 是必需的参数,因为它指定了源映射的版本号。

    解决:

    // 转换为json对象
    const sourceMap = JSON.parse(filePath, 'utf8')); 
    
    // 检查 sourceMap 是否符合规范 
    if (!sourceMap.hasOwnProperty('version')){ 
        throw new Error('Invalid Sourcemap format: "version" field is missing.'); 
     }
    
  • 根据堆栈行列信息解析后结果字段都为null

    掌握主动权——系统错误监控助你降成本、提效率(下)

    找到对应源码文件,观察:chunk-a8c0af14 内容:

    掌握主动权——系统错误监控助你降成本、提效率(下)

    发现 file 字段文件后缀是.css,奇怪!我们的报错不应该是js里的吗? 这里我一度怀疑是我前端项目打包配置出错了,css和js混了。 而且观察 sourcesContent字段只有样式源码,没有js内容。这样肯定是解析不到的!!!

经过查询资料、请教掘友、自己排查webpack配置折腾一番后发现,我是没有开启 productionSourceMap: true选项,只配置了 devtool: 'source-map', 这样虽然打包时生成 sourceMap 文件了,但是还需要开启 productionSourceMap: true 后才能生成具体的sourceMap内容。 这块真的醉了,太粗心了。修改配置后重新打包就没问题了。

3、错误信息持久化

还原了错误发生的现场后,我们就需要做数据持久化了,这里我使用的是 MongoDB 数据库

对于前端错误信息的上报,推荐使用 NoSQL 数据库,特别是针对非常大量快速变化的数据。其中,常用的 NoSQL 数据库包括 MongoDB、Cassandra、Redis等。 这些数据库具有高可扩展性高并发性高吞吐量等特点,可以有效地存储和处理前端错误信息,并支持实时查询和分析。

mongoose创建模型

为了方便,这里使用 mongoose 来操作数据库。具体 mongoose操作自行学习。

创建约束对象、映射、创建模型对象、且对外暴露。

const mongoose = require('mongoose')
// 引入模式对象
let Schema = mongoose.Schema
// 创建约束对象
let errorInfoRule = new Schema({
  errorCount: {
    tyep: Number,
    default: 0
  },
  errorType: {
    type: String,
    required: true,
  },
  message: {
    type: String,
    required: true,
  },
  date: {
    type: String,
    default: Date.now()
  },
  stack: {
    type: Object,
    default: {}
  },
})
// 映射、创建模型对象、且对外暴露
module.exports = mongoose.model('errorInfo', errorInfoRule)

保存错误数据到数据库

当错误信息上报、解析后我们对格式化后的错误数据进行保存到数据库。

const errorInfoModel = require('../model/errorInfoModel')
// 保存错误信息到数据库
async function addErrorInfo (res, baseData, stack) {
  let { type, message, date } = baseData;
  if (!!type && !!message) {
    let conditions = {
      errorType: type,
      message: message,
      date: date,
      stack: stack
    }
    try {
       const result = await errorInfoModel.create(conditions)
        if (result) {
          res.status(200)
          res.send('错误信息上报成功!')
        }
      } catch (err) {
        console.log('添加错误数据到数据库错误:', err)
     }
  } else {
    console.log('缺少字段!')
    res.status(400)
    res.send('缺少字段!')
  }
}

这时刷新数据库就可以看到数据已经保存了

掌握主动权——系统错误监控助你降成本、提效率(下)

4、错误信息回显

最后一步就是整一个前端页面来进行错误信息回显了,我这里就先做了一个简单的表格去展示一下错误数据的关键信息,还可以进行按ID、错误类型、错误次数等方式进行过滤搜索,方便我们查看错误溯源。 后期可以根据自己项目的情况更细化的完善和扩展数据监控回显功能,比如以图表形式展示、计算出错频率、错误报警通知等

最终效果图

掌握主动权——系统错误监控助你降成本、提效率(下)

总结

我觉得首先是不要怕错,对于新鲜事物只要勇敢迈出第一步就是对的,只有尝试自己不擅长的东西才能收获更多!本文只是做了一个简单的错误监控实现,对于复杂场景还需进一步扩展。如果感兴趣的小伙伴可以去深入这方面研究,学无止境,加油。

转载自:https://juejin.cn/post/7271790634083500088
评论
请登录