前端異常---日志上報服務搭建


前端異常---日志上報服務搭建

關於前端異常分類與捕獲可以看看我的這篇文章
JavaScript 網頁異常捕獲

既然異常已經捕獲到了,那我們怎么處理呢,如何上報,需要上報哪些內容?


一、日志分類


1、一般日志分類等級

log、debug、info、warn、error

2、分場景使用日志上報類型

log:    記錄流程信息
debug: 記錄調試關鍵信息
info:  記錄業務功能點,是否觸發成功或者失敗
warn:   頁面警告信息
error: 頁面錯誤或者業務異常信息

3、日志上報信息附帶信息

1、用戶id、session、用戶名
2、當前錯誤信息
3、可以用來重現、推斷當前錯誤發生的信息
4、上報時間
5、日志等級
等等

4、日志上報策略

1、達量上傳,設置一個緩存數量,到達即上報。因為不能一發生錯誤就要上報,會影響用戶的網絡。
2、日志埋點處各自有各自的上報等級。
需要有一個總配置地方,配置當前的上報等級
這樣各處埋點可以判斷當前需要上報的日志等級,等級小於設置值的話,不可上報。
3、本地緩存若暫存過多,需要刪除前面的數據
4、抽樣上報
5、設置緩存有效時間
等等

上報之后,接下來的步驟就是在服務端收集分析歸類展示,所以需要我們搭建一整套日志解析系統

今天我們來嘗試使用 badjs 相關項目,搭建一整套日志分析系統


二、badjs 服務安裝


1、前期預備工作

為了快速搭建,我們統一使用 docker 安裝

備注:windows 環境使用 docker,需要安裝 Docker Desktop

a、mysql 安裝

docker 安裝 mysql

備注:mysql 安裝好后,需要從 badjs-web (需要先把項目下載下來)項目中的 db 目錄下

使用 create.sql 初始化 web 相關的數據庫

b、mongodb 安裝

docker 安裝 mongo
備注:不可設置密碼


2、項目安裝

github 克隆項目到本地

git clone https://github.com/BetterJS/badjs-installer

子項目下載以及依賴安裝

// 克隆下載 badjs-acceptor、badjs-mq、badjs-storage、badjs-web 項目
yarn clone

// 安裝各項目的依賴
yarn install

3、修改配置項

1、修改 badjs-acceptor 項目的 project.debug.json/project.json

注意:這里是日志上報的地方,客戶端初始化 badjs-report 時候需要設置的 url 屬性即是這里的服務地址

http://{badjs-acceptor:port}/badjs

// 修改 port 屬性: 從 80 改為 8083; 
// 因為 node 默認沒有 80 端口的權限,需要你使用管理員權限才可以使用
{
    "port": 8083
}

2、修改 badjs-web 項目的 project.debug.json/project.json

// 修改 mysql 屬性,配置我們 docker 安裝好的 mysql 用戶密碼與端口
{
    "mysql" : {
        "url" : "mysql://root:123456@localhost:3306/badjs"
    }
}

4、啟動項目

// 啟動各項目
yarn start

查看 badjs-web 的啟動端口,訪問 http://localhost:port 可以看到日志后台管理服務頁面


三、badjs 各模塊關系


1、badjs-acceptor 接受客戶端上報的日志

badjs-acceptor 收到日志上報,發送到 badjs-mq
package.json 中配置 dispatcher 分發屬性, 表示向 badjs-mq 請求的信息:如請求端口(10001)

2、badjs-mq 消息隊列,保證消息有序穩定被接受

badjs-mq 接收 badjs-acceptor 的請求
package.json 中配置 acceptor 接收屬性, 表示用來接收信息所配置的接口信息:如端口(10001)。

badjs-mq 再分發到 badjs-storage
package.json 中配置 dispatcher 分發屬性, 表示向 badjs-storage 請求的信息:如端口(10000)

3、badjs-storage 存儲模塊

badjs-storage: 接收來自 badjs-mq 的請求,再寫入到 mongodb
package.json 中配置 acceptor 接收屬性, 表示用來接收信息所配置的接口信息:如端口(10000)

4、badjs-web 日志后台管理系統

badjs-web 查詢日志存儲,分類查看日志信息,解析日志內容
package.json 中配置 acceptor 接收屬性, badjs-acceptor 可請求的端口
package.json 中配置 storage 存儲屬性, 查詢 mongodb 數據
package.json 中配置 mysql 數據庫屬性, 查詢 mysql 數據
等等

四、上報日志插件 badjs-report

badjs-report 重寫了 window.onerror 來捕獲錯誤

1、安裝

yarn add badjs-report

2、初始化

import badjs from 'badjs-report'

badjs.init({
    // 必須配置項
    id: 1 // 此 id 為 badjs-web 啟動后,申請的項目的 id,上報的日志根據該 id 區分業務模塊
    url: 'http://badjs-acceptor啟動后的地址', // 日志上報到的地方

    // 選擇配置
    uin: 123, // 指定用戶的 id (該插件默認讀取 qq uin)
    delay: 1000, // 延遲多少毫秒,合並緩沖區中的上報(默認 1000)
    ignore: [/Script error/i], // 忽略某個錯誤,遇到該錯誤不進行上報
    random: 1, // 抽樣上報, 值可以設置 0-1 之間。1 表示 100% 上報(默認為 1)
    repeat: 5, // 重復上報次數(對於同一個錯誤超過多少次不上報;避免單個用戶同一錯誤上報過多的情況)
    onReport: function(id, errObj) {}, // 上報日志之后的回調。id 為上報的 id,errorObj 為上報的錯誤對象
    submit: function(url) {}, // 覆蓋原來的上報方式,原來是使用 new Image() 形式上報,可以修改成自己想要上報的方式,比如使用 post 內部構造好的 url
    ext: {}, // 擴展屬性,后端做擴展處理屬性。設置了 ext 的值,就會作為 'ext=設置的值' 合並到構造好的上報 url 中
    offlineLog: false, // 是否開啟離線日志(默認不開啟為 false)
    offlineLogExp: 5 // 離線有效時間(默認最近5天)
})

3、手動上報

a、badjs.report('error msg')
b、badjs.report({
    msg: 'error msg', // 需要上報的錯誤信息
    target: 'error.js', // 發生錯誤的 js 文件
    rowNum: 1, // 發生錯誤的行數
    colNum: 2 // 發生錯誤的列數
})

4、延遲上報

暫存

badjs.push('error msg')
badjs.push({
    msg: 'error msg', // 需要上報的錯誤信息
    target: 'error.js', // 發生錯誤的 js 文件
    rowNum: 1, // 發生錯誤的行數
    colNum: 2 // 發生錯誤的列數
})

立即上報

badjs.report({
    msg: 'error msg', // 需要上報的錯誤信息
    target: 'error.js', // 發生錯誤的 js 文件
    rowNum: 1, // 發生錯誤的行數
    colNum: 2 // 發生錯誤的列數
})

5、上報離線日志

badjs.reportOfflineLog()

五、項目使用示例

import BJ_REPORT from 'badjs-report'
import Vue from 'vue'

// 環境ID枚舉
let ENV_ID_ENUM = {
    DEV: 1,
    SIT: 2,
    UAT: 3,
    PRO: 4
}
let curEnv = '',
    origin = window.location.origin
if (origin.indexOf('dev.xxx.com') > -1) {
    // http://dev.xxx.com
    // DEV 環境
    curEnv = 'DEV'
} else if (origin.indexOf('sit.xxx.com') > -1) {
    // http://sit.xxx.com
    // SIT 環境
    curEnv = 'SIT'
} else if (origin.indexOf('uat.xxx.com') > -1) {
    // http://uat.xxx.com
    // UAT 環境
    curEnv = 'UAT'
} else if (origin.indexOf('m.xxx.com') > -1) {
    // http://pro.xxx.com
    // PRO 環境
    curEnv = 'PRO'
}
let envID = ENV_ID_ENUM[curEnv] || ENV_ID_ENUM.DEV

// 初始化日志上報插件
BJ_REPORT.init({
    id: envID, // 不指定 id 將不上報,
    url: 'http://{badjs-acceptor:port}/badjs'
})

// 初始化項目時,可以暴露一個全局的 vm 實例,方便上傳需要的信息
window.rootvm = new Vue({}) // 初始化項目

// 初始化監聽異常
init(window.rootvm)
function init(rootInstance) {
    Vue.config.errorHandler = function(err, curInstance, info) {
        // vm 為拋出異常的 Vue 實例
        // info 為 Vue 特定的錯誤信息,比如錯誤所在的生命周期鈎子
        let {
            message, // 異常信息
            // name, // 異常名稱
            // script, // 異常腳本url
            line, // 異常行號
            column, // 異常列號
            stack // 異常堆棧信息
        } = err
        log(message, stack, line, column, curInstance)
        console.log('vue errorHandler :>> ', err, curInstance, info)
    }
    window.addEventListener(
        'error',
        e => {
            let { colno, lineno, message, filename } = e
            log(message, filename, lineno, colno, rootInstance)
            console.log('addEventListener error 捕獲階段>>>異常:', e)
        },
        true
    )
    window.addEventListener('unhandledrejection', function(e) {
        // e.preventDefault(); // 阻止異常向上拋出
        let { reason } = e
        log(JSON.stringify(reason), '', '', '', rootInstance)
        console.log('Promise 異常 unhandledrejection :', e)
    })
}

function log(msg, target, rowNum, colNums, vminstance) {
    let msgs = `***[${msg}]***`,
        state = (vminstance && vminstance.$store && vminstance.$store.state) || {}

    // 用戶信息
    if (state.userInfo) {
        let { phoneNumber, userId } = state.userInfo
        msgs += `***[phone:${phoneNumber}--userId:${userId}]***`
    }
    // 路由信息
    let { name, fullPath } = (vminstance && vminstance.$route) || {}
    msgs += `***[router-name: ${name} -- router-fullpath: ${fullPath}]***`

    BJ_REPORT.report({
        msg: msgs,
        target,
        rowNum,
        colNums
    })
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM