最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Vue 2.0 生產(chǎn)環(huán)境前端錯誤日志記錄實踐

2018-12-28 13:24 作者:愛交作業(yè)的D1N910  | 我要投稿

核心內(nèi)容參考自
https://www.cnblogs.com/luozhihao/p/8635507.html

開始實現(xiàn)的時候知道很簡單,中間的時候感覺就難了一點,后面實現(xiàn)出來的時候感覺真的很簡單。
——————————————————————————

?大佬和我進行了一番深刻的交流,總結(jié)起來就一句話,在生產(chǎn)環(huán)境的時候,前端的報錯要能夠記錄下來。

開發(fā)環(huán)境的時候的報錯,我們都能夠直接通過看開發(fā)者工具就能夠看到具體的報錯信息,什么問題,哪一行、哪一列,那些代碼報錯,這是我們不會進行抓取考慮的——這些報錯顯然是我們肯定要解決的。

生產(chǎn)環(huán)境的時候,我們上傳的代碼都是打包以后的代碼,對于打包好了抓取到的代碼報錯,我們可以通過source map查找到具體的代碼報錯,然后進行存儲。
?

摘要:
window全局捕獲錯誤+promise捕獲錯誤+koa2存儲+source map

那么,開始。

——————————————————————————

開始規(guī)劃

俺們村里人,做事都有計劃。
規(guī)劃為兩點,一點是捕獲報錯,一點是存儲報錯。

圖片



捕獲報錯——基礎(chǔ)捕獲(windows全局報錯+promise報錯)

我先解釋下為什么叫基礎(chǔ)捕獲,因為我在搜索相關(guān)的解決方案的時候看到了國內(nèi)外兩家很牛提供前端報錯日志收集解決方案的公司
國內(nèi):FunDebug?https://www.fundebug.com/

圖片



國外:?sentry??https://sentry.io/welcome/

圖片




它們牛不僅僅是因為它們涵蓋的語言、端多,還因為收集錯誤日志的內(nèi)容上非常多,甚至FunDebug還能夠模擬用戶當時是怎么用的。

這也是我們不能選擇這兩家公司的原因。

那你選擇本地版的咯【比我時下的實習生年薪還要高一倍,你覺得BOSS會不會要?

圖片



這就不提了,作為第一版的基礎(chǔ)版,我只需要基礎(chǔ)捕獲就夠了。

window全局報錯捕獲

全局捕獲報錯的時候,使用window.onerror就可以捕獲到了。

But,wait

如果你使用的是MVVM框架,那么在是你用winodw.onerror方法捕獲的報錯,或者叫異常,可能就捕獲不到。因為框架一般都有自身的異常機制來進行捕獲。你可以通過覆蓋這個方法的形式來進行。
比如:
Vue的
Vue.config.errorHandler = function (err, vm, info) { ? ?
????let { ? ? ? ?
????????????message, // 異常信息 ? ? ? ?
????????????name, // 異常名稱 ? ? ? ?
????????????script, ?// 異常腳本url ? ? ? ?
????????????line, ?// 異常行號 ? ? ? ?
????????????column, ?// 異常列號 ? ? ? ?
????????????stack ?// 異常堆棧信息 ? ?
????} = err; ? ?// vm為拋出異常的 Vue 實例 ? ?// info為 Vue 特定的錯誤信息,比如錯誤所在的生命周期鉤子}


在實際項目中,是這么調(diào)用的

// 全局捕獲錯誤

const errorHandler = (error, vm) => {

????reportError(error)? ?
????????? ?//?reportError 是我封裝的上傳報錯信息的內(nèi)容?

}

Vue.config.errorHandler = errorHandler

Vue.prototype.$throw = (error, msg) => errorHandler(error, this)


-----------

在實際的項目運用上,除了這種window全局錯誤捕獲,還要考慮promise下發(fā)生的異步報錯——這種是剛剛我們用的方法難以捕捉的。我們經(jīng)常用try catch來捕獲錯誤,這邊也會遇到不能捕獲的問題。

不過我們知道,promise有專門的catch來捕獲錯誤,但是對于已經(jīng)成熟的大項目來說,一個個去查找catch,去添加我們的上傳錯誤信息的repotrError是不顯示的。

在參考了別人的寫法以后,于是考慮到了去 xxoo promise?——對promise進行一次侵犯。

// 如果瀏覽支持Promise,捕獲promise里面then的報錯,因為promise里面的錯誤onerror和try-catch都無法捕獲

if (Promise && Promise.prototype.then) {

var promiseThen = Promise.prototype.then

/* eslint-disable */

Promise.prototype.then = function(resolve, reject) {

return promiseThen.call(this, _wrapPromiseFunction(resolve), _wrapPromiseFunction(reject))

}

/* eslint-enable */

}

// 異步報錯統(tǒng)一捕捉

var _wrapPromiseFunction = (fn) => {

// 如果fn是個函數(shù),則直接放到try-catch中運行,否則要將類的方法包裹起來,promise中的fn要返回null,不能返回空函數(shù)

if (typeof fn !== 'function') {

return null

}

return function () {

try {

return fn.apply(this, arguments)

} catch (error) {

reportError(error)

throw (error)

}

}

}

?針對線上環(huán)境,如果支持promise,那么我們就會對promise的prototype(見我之前的文章內(nèi)容)的taen方法進行一次重新改造處理,具體的改造方法是返回一個封裝好的try catch函數(shù),相當于是自帶了try catch。如果正常,那么返回正常的函數(shù)調(diào)用方法。這邊有一個小問題——我忘記了為何要再寫個throw方法來拋出異常了,在捕獲了異常以后(reportError),我還需要throw(error)嗎?回頭思考以后編輯下。

通過上面的方法,我們可以捕獲最常見的兩種報錯,基本上滿足了我們的需求。

--------------------------------------

存儲錯誤——與source map 愉快地玩耍

前端拿到錯誤的時候,并不像我想的那么順利,在我這里,我不能夠直接拿到具體的行、具體的列,只能夠通過錯誤棧找到

說起來,如果是開發(fā)環(huán)境中,用webpack開啟的服務器直接捕獲的到的錯誤棧信息如下

SyntaxError: Unexpected token s in JSON at position 4

? ? at JSON.parse (<anonymous>)

? ? at eval (webpack-internal:///1440:455:52)

? ? at eval (webpack-internal:///465:109:15)


而如果是線上打包環(huán)境的話,
拿到的錯誤棧信息如下

SyntaxError: Unexpected token s in JSON at position 4

? ? at JSON.parse (<anonymous>)

? ? at 【馬賽克】11.4e17da6131b2c0b1d277.js:21:9826

? ? at 【馬賽克】app.bc205a634089f0bd3803.js:1:46844

根據(jù)這個錯誤棧信息,我們知道這個錯誤涉及到兩個js文件的名字,對應的行以及對應的列——但是這樣對我們解決bug基本上沒有什么幫助。


不過有了對應的js文件的名字,錯誤的行和列以及足夠了。

開始做存儲錯誤處理的操作;

第一步:生成打包后的js文件對應的map文件

如果你是webpack,可以這么配置

?productionSourceMap: true


然后你就會看到在每一個.js文件下都會有對應的同名的.map文件,這是一一對應的,所以我們很方便就可以聯(lián)系起來?

圖片



第二步:選擇適合自己的source map解析方式,我沒有直接自己去解析map文件,雖然參考了一些網(wǎng)上的案例,最后還是直接用了sourceMap?這個npm包進行解析處理

const sourceMap = require('source-map'); // *核心內(nèi)容,解析source-map的內(nèi)容

第三步:解析我們打包的報錯代碼

我使用的是node服務器,已知


SyntaxError: Unexpected token s in JSON at position 4

? ? at JSON.parse (<anonymous>)

? ? at 【馬賽克】11.4e17da6131b2c0b1d277.js:21:9826

? ? at 【馬賽克】app.bc205a634089f0bd3803.js:1:46844

如上的錯誤信息,我只要獲得錯誤的文件名【找到對應的map文件】?,行數(shù),列數(shù)這三個數(shù)據(jù)即可。


核心代碼如下:

const fs = require('fs'); // 讀取文件

const path = require('path'); // 讀取文件路徑

const readline = require('readline'); // 按行讀取文件

const sourceMap = require('source-map'); // *核心內(nèi)容,解析source-map的內(nèi)容

/*

* 按行讀取文件內(nèi)容

* 返回:字符串數(shù)組

* 參數(shù):fReadName:文件名路徑

* ? ? ?callback:回調(diào)函數(shù)

* */

/*

* 按行讀取文件內(nèi)容

* 返回:字符串數(shù)組

* 參數(shù):fReadName:文件名路徑

* ? ? ?callback:回調(diào)函數(shù)

* */

async function readFileToArr(fReadName, callback){

? var fRead = fs.createReadStream(fReadName);

? var objReadline = readline.createInterface({

? ? input:fRead

? });

? var arr = new Array();

? objReadline.on('line',function (line) {

? ? arr.push(line);

? });

? await new Promise((resolve, reject) => {

? ? objReadline.on('close',function () {

? ? ? callback(arr)

? ? ? resolve('done!')

? ? });

? })

}


/**

? ? * ctx 內(nèi)容

? ? * @param ret.name // 報錯對應的名稱

? ? * @param ret.source // 報錯文件路徑

? ? * @param ret.line // 報錯文件行號

? ? * @param ret.lcolumn // 報錯文件列號

? ?*/


let {

? ? ?userAgent,

? ? ?pathname,

? ? ?name,

? ? ?error,

? ?} = ctx.request.body


// 下面的解析錯誤棧的方式,是因為我不知道為何不能直接拿到報錯文件路徑、行號、列號所做的hack,如果你能直接拿到,可以不這么做。直接看核心代碼即可

? ?// 得到js地址的正則表達式

? ?var geJsMap = /(?<=js\/).*?(?=:)/


? ?// 存儲了js鏈接,不一定只有一個

? ?const jsList = []


? ?// 存儲了每個js文件報錯對應的行

? ?const lineList = []


? ?// 存儲了每個js文件報錯對應的列

? ?const columnList = []


? ?// 存儲了得到的真正的資源文件的內(nèi)容

? ?const sourceret = []


? ??

// 取得js文件、報錯的行和列
error.split('at').forEach(element => {

? ? if (element.match(geJsMap)) {

? ? // 取得js文件

? ? jsList.push(element.match(geJsMap)[0])

? }

? // 取得報錯的行和列

? if (element.split(':').length > 2) {

? ? lineList.push(element.split(':')[1])

? ? columnList.push(element.split(':')[2].split('\\n')[0])

? }

? ?});


? ?// 遍歷顯示異常的js文件

? ?for (let i in jsList) {

? ? ?// 得到js文件對應的.map文件

? ? ?let fileUrl = `${jsList[i]}.map`;


? ? ?// 得到解析了map文件的smc對象 核心內(nèi)容

? ? ?let smc = new sourceMap.SourceMapConsumer(fs.readFileSync(resolve(`../js/${fileUrl}`), 'utf8')); // 返回一個promise對象


? ? ?// 通過originalPositionFor獲得result文件的內(nèi)容

? ? ?await smc.then(function (result) {
? // 核心內(nèi)容

? let ret = result.originalPositionFor({

? ? line: parseInt(lineList[i]), // 壓縮后的行號

? ? column: parseInt(columnList[i])// 壓縮后的列號

? ?//?到了這一步,你可以獲得下面的內(nèi)容

圖片


? ? ? ?});


? ? ? ?/**

? ? ? ? * 解析原始報錯數(shù)據(jù)

? ? ? ? * @param ret.name // 報錯對應的名稱

? ? ? ? * @param ret.source // 報錯文件路徑

? ? ? ? * @param ret.line // 報錯文件行號

? ? ? ? * @param ret.lcolumn // 報錯文件列號

? ? ? ?*/

? ? ? ?sourceret[i] = ret

? ? ?})

? ?}


// 下面是錦上添花的存儲錯誤日志內(nèi)容,大家看看就行

? ?// 打印錯誤

? ?const errorTime = `${new Date().toLocaleString()}\r\n`

? ?const errorUserAgent = `${userAgent}\r\n`

? ?const errorPath = `${pathname}\r\n`

? ?const errorName = `${name}\r\n`

? ?let errorStack = ''

? ?for (let i in sourceret) {

? ? ?errorStack += `${sourceret[i].name}\r\n${sourceret[i].source.slice(sourceret[i].source.indexOf('src'))}:${sourceret[i].line}:${sourceret[i].column}\r\n`

? ? ?await readFileToArr(sourceret[i].source.slice(sourceret[i].source.indexOf('src')), (arr) => {

? ? ? ?let leftSpace = ''

? ? ? ?for (let j = 0; j < arr[sourceret[i].line - 1].trim().indexOf(sourceret[i].name); j++) {

? ? ? ? ?leftSpace += '-'

? ? ? ?}

? ? ? ?leftSpace += '△\r\n'

? ? ? ?errorStack += arr[sourceret[i].line - 1].trim() + '\r\n' + leftSpace

? ? ?})

? ?}


? ?let data = errorTime + errorUserAgent + errorPath + errorName + errorStack + '\r\n'

? ?// 當前時間

? ?const nowLocalDate = `${new Date().getFullYear()}-${new Date().getMonth() + 1}-${new Date().getDate()}`

? ?/** 打印錯誤內(nèi)容到{當前時間}_error.log */

? ?fs.appendFile(`${nowLocalDate}_error.log`, data, function (err) {

? ? ?if (err) {

? ? ? ?console.log(err)

? ? ?}

? ?})



最后

這個錯誤日志的存儲最終暫時還是擱置了,因為還涉及到埋點等的問題,需要我們后期進一步的研究。希望這個文章能夠給想要進行線上前端報錯存儲的你帶來一些幫助。

Vue 2.0 生產(chǎn)環(huán)境前端錯誤日志記錄實踐的評論 (共 條)

分享到微博請遵守國家法律
凌海市| 理塘县| 军事| 临朐县| 郎溪县| 安溪县| 肥城市| 星子县| 栾城县| 民乐县| 新巴尔虎左旗| 漳浦县| 辽宁省| 原平市| 紫阳县| 湘乡市| 乌兰察布市| 莱州市| 隆昌县| 安达市| 武胜县| 南乐县| 旬邑县| 玉龙| 佛坪县| 皮山县| 夏河县| 虹口区| 安国市| 沙田区| 镇安县| 罗定市| 屯昌县| 荔浦县| 德庆县| 贵定县| 博白县| 湖南省| 齐齐哈尔市| 禹城市| 大石桥市|