自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

前言

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

正文

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

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

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

思考:

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

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

前置

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

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

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

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

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

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

這樣的話,我們就可以非常方便的捕獲錯誤,監(jiān)控生產問題,同時也實現了一個簡單的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類型的錯誤,但是經過嘗試發(fā)現不是很好去拿到錯誤的定位信息,同時,考慮到一般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";
  // 服務器因為是一直啟動狀態(tài),所以如果是在啟動后最新上傳的文件,則需要事實進行讀取對應的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庫去解析錯誤信息
  • 輸出錯誤日志

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

完善前端邏輯

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

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

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

// 生產環(huán)境再去做上傳錯誤處理
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。

圖片圖片

在這個鉤子觸發(fā)時,說明打包文件已經被輸出出來了,我們可以去讀取打包文件的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來測試一下效果。

圖片圖片

圖片圖片

然后,我們執(zhí)行npm run build打包操作。

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

圖片圖片

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

圖片圖片

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

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

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

圖片圖片

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

圖片圖片

圖片圖片

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

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

2022-11-28 07:35:52

前端錯誤

2019-07-15 07:58:10

前端開發(fā)技術

2010-12-21 14:08:50

PowerShell

2023-12-18 16:40:23

OxlintJavaScripRust

2024-10-12 08:01:53

2022-08-16 10:44:11

Sentry前端異常

2024-02-28 08:47:29

文件系統(tǒng)數據

2023-08-10 13:46:48

前端資源優(yōu)化

2010-02-26 10:14:25

WCF全局錯誤捕獲

2020-09-27 07:48:40

不用try catch

2021-01-06 16:33:08

前端開發(fā)工具

2013-03-13 11:28:13

測試捕獲錯誤性能測試

2020-12-10 06:01:20

前端Compose方法

2022-01-25 18:11:55

vdomclassfunction

2022-11-03 08:07:54

Python工具navicat

2017-04-11 19:40:52

AR玩具模型

2022-04-11 09:15:44

中間件開源

2024-03-07 08:53:01

前端異步Promise

2022-01-21 08:21:48

前端vdom渲染

2024-05-07 07:04:05

前端調試技巧瀏覽器
點贊
收藏

51CTO技術棧公眾號