成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

手寫個前端小玩具—錯誤捕獲定位工具

開發 前端
我們平時在使用框架開發遇到bug時,比如Vue,如果是在本地環境,我們在控制臺可以很容易的找到出現問題的文件,甚至點擊進入即可直接定位到我們的文件中對應報錯的位置,這樣排查問題就比較方便。

前言

作為一個兢兢業業的前端er,雖然每天都被各種CRUD的需求包圍著,但總歸還是有一顆愛玩的心。

正文

我們在平時的工作中,開發功能的同時不可能把場景考慮的面面俱到,而生產環境往往情況是非常復雜的,用戶錄入進去的數據總是千奇百怪,那如果遇到問題的話,我們又要如何進行排查呢?總不可能讓用戶錄個屏吧哈哈~所以我們就出現了前端埋點的操作,不過埋點的方向以及文章都挺多的,也都挺復雜的,這篇文章我們就講一個比較有趣的錯誤捕獲思路。

我們平時在使用框架開發遇到bug時,比如Vue,如果是在本地環境,我們在控制臺可以很容易的找到出現問題的文件,甚至點擊進入即可直接定位到我們的文件中對應報錯的位置,這樣排查問題就比較方便。而在生產環境,我們可以配置sourcemap,就也能比較方便的定位到問題出現的地方。但這樣的話就會出現一個問題,首先上傳到服務器的包體積就會因為生成了很多map文件而變得很大,其次我們的網站代碼會非常容易暴露甚至是直接被調試,而且這樣子也僅僅是我們自測的時候去發現問題,無法監測到用戶端到底是做了什么操作才出現的問題。

那么,有沒有一個方法是可以監控到客戶端用戶操作時,出現問題的代碼位置呢?

思考:

綜上,我們這次要做的這個工具的目的就比較明確了:

  • 錯誤捕獲
  • 錯誤分析/錯誤定位
  • 錯誤收集/日志輸出

前置

在錯誤捕獲之前,我們先提前了解一個服務端的庫——source-map

使用source-map庫,我們可以通過向該庫暴露出的方法中傳入bug出現的文件對應的map文件,以及錯誤的行數和列數,通過對應的方法解析后,我們可以得到該錯誤出現的源文件以及具體在源文件中的定位。

至此,我們明確了錯誤捕獲中,我們主要就是想拿四個信息:

  • 錯誤的message信息
  • 錯誤出現的文件名
  • 錯誤行數
  • 錯誤列數

那么,我們可不可以設計這樣一個流程呢?

  • 1.在配置文件中將sourcemap的配置打開,從而使得項目打包后會生成map文件。
  • 2.通過編寫webpack插件,監聽webpack打包完成鉤子,在打包完成后觸發,將生成的map文件自動上傳到我們的服務器上。
  • 3.然后在前端,通過錯誤捕獲,將報錯信息傳給我們的服務器,由服務器根據報錯信息再結合map文件,最終解析出我們的報錯行數,同時形成日志輸出出來并記錄下來。

這樣的話,我們就可以非常方便的捕獲錯誤,監控生產問題,同時也實現了一個簡單的webpack插件(又可以拿去和面試官吹水了~)。

錯誤捕獲

onerror

前端的錯誤捕獲我們最常見的當然是window.onerror了,我們可以通過定義window.onerror函數來對全局錯誤進行捕獲。

// main.js
window.onerror = function(message, source, lineno, colno) {
  console.log(message)
  console.log(source)
  console.log(lineno)
  console.log(colno)
}

通過window.onerror我們很容易可以拿到我們想要的具體信息。

圖片圖片

errorHandler

但window.onerror并不能捕獲到框架組件生命周期的錯誤,所以我們可以再補充一個框架的錯誤捕獲,以Vue為例:

// main.js
...
const app = createApp(App)
app.use(store).use(router).mount('#app')

app.config.errorHandler = function (err, vm, info) {
  console.log(err)
  console.log(vm)
  console.log(info)
};

我們在errorHandler事件中,可以拿到錯誤對象err,vue實例,錯誤信息。這里我們并不能像上面onerror錯誤捕獲一樣很方便的取出出錯的行數和列數,但我們能夠拿到一個完整的錯誤堆棧對象,那么我們就可以對錯誤對象的堆棧信息進行處理,提取出我們想要的行數和列數。

這里用到了一個堆棧解析工具——StackTrace-Parser

npm install stacktrace-parser
app.config.errorHandler = function (err, vm, info) {
    const errInfo = stackTraceParser.parse(err.stack)[0]
    const message = err.message // 錯誤message
    const lineno = errInfo.lineNumber // 錯誤行數
    const colno = errInfo.column // 錯誤列數
    const source = errInfo.file // 錯誤出現的文件名
    ...
  };
補充

錯誤捕獲還有一個onunhandledrejection的事件,用于捕獲Promise類型的錯誤,但是經過嘗試發現不是很好去拿到錯誤的定位信息,同時,考慮到一般Promise我們會使用catch去處理異常的操作,所以這里就暫時不處理這個類型的錯誤事件了。

至此,我們的捕獲相關的邏輯已經完成,剩下的就是如何設計服務端,如何將這些信息傳遞給服務端并完成解析了。

錯誤分析/錯誤定位

服務端,我們設計兩個接口,一個用于上傳map文件(upload),一個用于接收錯誤信息(sendErrorLog)。

上傳接口就不多說了,主要就是在前端打包完成之后,服務端接收傳過來的map文件。我們主要看一下接收錯誤信息的接口邏輯。

const handleErrorMessage = require("./utils/index");
...
app.post("/sendErrorLog", (req, res) => {
  handleErrorMessage(req.body);
  res.send("hello");
});
// utils/index.js
const fs = require("fs");
const { SourceMapConsumer } = require("source-map");
const path = require("path");

// 讀取壓縮代碼和對應的source map
const arr = fs.readdirSync(path.resolve(__dirname, "../uploads"));
const sourceMap = {};
for (let i = 0; i < arr.length; i++) {
  fs.readFile(
    path.resolve(__dirname, "../uploads", arr[i]),
    "utf-8",
    function (err, data) {
      if (err) {
        return err;
      }
      sourceMap[arr[i]] = data;
    }
  );
}

module.exports = function handleErrorMessage(message) {
  const errorLine = message.lineno;
  const errorCol = message.colno;
  const jsName = message.source.split("/").pop();
  const sourceName = jsName + ".map";
  // 服務器因為是一直啟動狀態,所以如果是在啟動后最新上傳的文件,則需要事實進行讀取對應的map文件
  if (!sourceMap[sourceName]) {
    sourceMap[sourceName] = fs.readFileSync(
      path.resolve(__dirname, "../uploads", sourceName),
      "utf-8"
    );
  }
  SourceMapConsumer.with(sourceMap[sourceName], null, (consumer) => {
    // 在源碼堆棧中定位報錯位置
    const originalPosition = consumer.originalPositionFor({
      line: errorLine,
      column: errorCol,
    });

    console.log("Error occurred at:");
    console.log("file:" + originalPosition.source);
    console.log("line:" + originalPosition.line);
    console.log("column:" + originalPosition.column);
    console.log("message:" + message.message);
  });
};

整體的思路就是:

  • 服務器啟動時讀取upload文件夾下的所有map文件,將對應文件的內容讀取出來
  • 在sendErrorLog接口被調用后,通過source-map庫去解析錯誤信息
  • 輸出錯誤日志

這里考慮到一般服務器我們都是一直啟動的狀態,所以在調用解析邏輯之前,先判斷souceMap數據是否已經讀取出來,如果沒有讀取出來,再同步去讀取,之后再去解析錯誤信息。

完善前端邏輯

接口已經有了,這里我們再回過頭完善一下前端的邏輯。

首先,我們根據前面對錯誤捕獲的了解,完成一下錯誤上傳的邏輯,:

// main.js
import axios from 'axios'
import * as stackTraceParser from 'stacktrace-parser';
...

// 生產環境再去做上傳錯誤處理
if (process.env.NODE_ENV == "production") {
// 捕獲框架內部錯誤
  app.config.errorHandler = function (err, vm, info) {
    const errInfo = stackTraceParser.parse(err.stack)[0]
    const message = err.message
    const lineno = errInfo.lineNumber
    const colno = errInfo.column
    const source = errInfo.file
    axios
      .post("http://127.0.0.1:3000/sendErrorLog", {
        message,
        lineno,
        colno,
        source,
      })
      .then((data) => {
        console.log(data);
      });
  };

  // 捕獲js報錯
  window.onerror = function(message, source, lineno, colno) {
    axios
        .post("http://127.0.0.1:3000/sendErrorLog", {
          message,
          lineno,
          colno,
          source,
        })
        .then((data) => {
          console.log(data);
        });
  }
}

然后,我們開始實現map文件上傳的邏輯。

我們先去找一個webpack打包完成輸出文件后的鉤子——afterEmit。

圖片圖片

在這個鉤子觸發時,說明打包文件已經被輸出出來了,我們可以去讀取打包文件的js文件夾,從中過濾出map文件,上傳至服務器,同時在打包文件中將map文件進行刪除操作。

const pluginName = "SendMapWebpackPlugin";
const fs = require("fs");
const axios = require("axios");
const path = require('path')

class SendMapWebpackPlugin {
  apply(compiler) {
    const outputPath = compiler.options.output.path;
    compiler.hooks.afterEmit.tap(pluginName, (compilation) => {
      console.log("webpack 構建");
      console.log(process.env.NODE_ENV);
      if (process.env.NODE_ENV == "production") {
        fs.readdir(outputPath + "/js", function (err, data) {
          if (data) {
            data.forEach((v) => {
              // 如果讀取到的數據是以map結尾,則將map文件上傳到服務器
              if (v.endsWith(".map")) {
                const file = fs.readFileSync(
                  path.resolve(__dirname, "../dist/js", v),
                  "utf-8"
                );
                axios({
                  url: "http://127.0.0.1:3000/upload",
                  method: "post",
                  data: { file, fileName: v },
                  headers: {
                    "Content-Type": "application/octet-stream",
                  },
                })
                  .then((res) => {
                    console.log("success");
                    fs.rm(path.resolve(__dirname, "../dist/js", v), (err) => {
                      if(err) {
                        console.log(err)
                        return
                      }
                      console.log('delete success')
                    })
                  })
                  .catch((err) => {
                    console.log(err);
                  });
              }
            });
          }
        });
      }
    });
  }
}
...

測試效果

邏輯寫完了,我們在前端代碼中留下一些bug來測試一下效果。

圖片圖片

圖片圖片

然后,我們執行npm run build打包操作。

可以看到我們打包完成后的dist文件夾中,已經沒有了map文件:

圖片圖片

而在服務端,我們接收到了這些map文件。

圖片圖片

上傳map文件邏輯沒有問題,接下來,我們看一下錯誤解析邏輯。

我們可以在本地安裝一個serve包,便于我們快捷的以dist文件夾為基礎起一個小型服務器。

將dist文件夾在終端中打開,執行執行serve -p 8080。

圖片圖片

點擊按鈕觸發bug,我們可以看到錯誤已被成功捕獲,并將對應的信息通過接口傳遞給服務端。

圖片圖片

圖片圖片

在服務端的輸出中,我們可以看到已對錯誤進行了解析,錯誤發生的定位信息已經輸出出來了,對照前端文件中錯誤發生的位置也是沒有問題的。

責任編輯:武曉燕 來源: React
相關推薦

2022-11-28 07:35:52

前端錯誤

2019-07-15 07:58:10

前端開發技術

2010-12-21 14:08:50

PowerShell

2024-10-12 08:01:53

2023-12-18 16:40:23

OxlintJavaScripRust

2022-08-16 10:44:11

Sentry前端異常

2024-02-28 08:47:29

文件系統數據

2010-02-26 10:14:25

WCF全局錯誤捕獲

2023-08-10 13:46:48

前端資源優化

2020-09-27 07:48:40

不用try catch

2021-01-06 16:33:08

前端開發工具

2020-12-10 06:01:20

前端Compose方法

2022-01-25 18:11:55

vdomclassfunction

2013-03-13 11:28:13

測試捕獲錯誤性能測試

2022-11-03 08:07:54

Python工具navicat

2017-04-11 19:40:52

AR玩具模型

2024-03-07 08:53:01

前端異步Promise

2022-01-21 08:21:48

前端vdom渲染

2024-05-07 07:04:05

前端調試技巧瀏覽器

2022-04-11 09:15:44

中間件開源
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品久久久久久久久免费桃花 | 亚洲精品视频一区 | 亚洲国产成人精 | 日本不卡一区二区三区在线观看 | 免费黄色片视频 | 亚洲国产免费 | 99热热99 | 国产精品污www一区二区三区 | 亚洲第一中文字幕 | 国产精品精品视频 | 人人艹人人| 日韩av成人 | 99视频 | 九九av| 亚洲国产精品va在线看黑人 | 中文字幕一区二区在线观看 | 久久久久久国产精品三区 | 美女视频黄的 | 美女国产 | 国产一区二区三区四区五区加勒比 | 97天天干 | 韩国精品一区二区三区 | 99在线免费视频 | 婷婷色在线播放 | 在线免费观看a级片 | 午夜激情在线视频 | 天天干.com| 人人做人人澡人人爽欧美 | 日韩av成人 | 亚洲最大看片网站 | 精品成人免费一区二区在线播放 | 国产乱码精品一品二品 | 国产成人精品午夜视频免费 | 国产在线视频一区二区董小宛性色 | 国产精品国产三级国产aⅴ无密码 | 欧美日韩在线免费观看 | 手机在线一区二区三区 | 欧美国产精品一区二区 | 久久成人一区二区三区 | 国产yw851.c免费观看网站 | 一区中文字幕 |