需求:客戶在使用過程當中頁面報錯時,能夠生成錯誤記錄傳回服務器,以便改進。html
步驟:
一.全局捕獲異常,
二.發送到服務端,
三.生成錯誤日誌。vue
一.全局捕獲異常node
如圖,vue提供了errorHandle這個方法來處理全局異常,更多詳細內容參見官網。ios
我在頁面中寫了一個錯誤的函數,觸發了errorHandler,控制檯打印以下:json
在utils.js中寫了以下代碼:axios
1 //系統錯誤捕獲 2 const errorHandler = (error,vm,info)=>{ 3 getErr(error,vm,info); 4 } 5 6 Vue.config.errorHandler = errorHandler; 7 Vue.prototype.$throw = (error,vm,info)=> errorHandler(error,vm,info);
而後在另外一個公用的js(commonService.js)中:api
/** * 捕獲異常 */ const getErr = async(err,_this,info) => { _this.$store.dispatch('getErr',{ err:err.stack, hook:info } }
說明一下,之因此寫在兩個文件中,是由於項目結構就是這樣的。瀏覽器
二.發送到服務端服務器
接下來就是如何把請求發送到node服務器上了。既然經過dispatch觸發,那就統一在store目錄下的index.js中處理:session
// 捕獲異常,存在node服務器中
async getErr({ commit },{ err_info }) {
console.log({err_info})
await axios.post('/api/getErr',{ err_info }) }
在瀏覽器中調試,發現接口報了500.發現err_info是undefined,即{err_info}是{"err_info":undefined}。找了半天也沒發現問題,因此就再也不經過dispatch觸發,直接在commonService.js中拿到數據就發到node服務器。另外,查資料的時候看到dispatch中最多隻能傳兩個參數,還有一個是commit,全部能夠把其餘的參數拼裝在一個對象中,避免出現undefined。這條沒有實踐過,暫時存疑。因而我在commonService.js中修改了原來的代碼,結果以下:
const getErr = async(err,_this,info) => {
await axios.post('/api/getErr',{ err:err.stack,hook:info }) }
運行一下,請求發送成功,如圖:
三.生成錯誤日誌
OK,如今服務端已經收到請求,可是尚未返回值,因此要開始寫服務端的代碼了。咱們的項目是基於nuxt的,因此代碼在api/index.js中:
// 捕獲異常 router.post('/getErr', (req, res) => { req.session.getErr = {err:req.body.err,hook:req.body.hook}; return res.json({ok: true}) })
終於在network中看到返回值了...接下來就要生成錯誤日誌並保存到服務器了。照例開始查文檔:http://nodejs.cn/api/fs.html。首先要引入文件系統(File System,就是下文的fs):
const fs = require('fs');
寫文件的方法是writeFile,異步地寫入數據到文件,若是文件已經存在,則覆蓋文件。
具體參數以下:
文件名加上時間戳,每次都生成一個新文件,點點點點,因而有了好多日誌文件:
貼一下代碼:
// 捕獲異常 router.post('/getErr', (req, res) => { req.session.getErr = {err:req.body.err,hook:req.body.hook,userInfo:req.body.userInfo}; let time = new Date(); // 記錄錯誤內容 fs.writeFile( 'tm_wap_err_' + time.getTime() +'.txt', '報錯內容:' + req.session.getErr.err + '\r\n' + '所在鉤子:' + req.session.getErr.hook + '\r\n' + '報錯時間:' + time.toLocaleString() + '\r\n' + '用戶信息:' + JSON.stringify(req.session.getErr.userInfo), (err) => { if (err) throw err; }); // console.log(666,req.session.getErr); return res.json({ok: true}); })
這裏我還記錄了時間用戶的登陸狀態。需求大致完成了,想辦法優化一下,每次報錯都生成一個新文件感受太奢侈了,能不能搞個增量更新,統一記錄在一個文件中?既方便查閱又省空間,還能練手,那就搞起來吧!大體思路以下:先判斷文件是否存在,若不存在就建立一個。讀取到文件內容之後再新加上跟新的內容,而後再寫入。這種方式涉及到了I/O操做,可是相比增長不少文件仍是會性能更好吧。最終代碼以下:
// 捕獲異常 router.post('/getErr', (req, res) => { req.session.getErr = { err:req.body.err, hook:req.body.hook, userInfo:req.body.userInfo, url:req.body.url }; let time = new Date(); let content = ''; // 若文件不存在,就建立一個吧! fs.exists("toolmall_wap_err.txt", function(exists) { if(!exists) { fs.writeFile('toolmall_wap_err.txt','', function(err) { if(err) { return console.log(err); } }); } }); // 增量更新日誌文件,先讀取 fs.readFile('toolmall_wap_err.txt','utf8',(err, data) => { if (err) throw err; data += '\r\n'; data += '報錯內容:' + req.session.getErr.err + '\r\n'; data += '所在鉤子:' + req.session.getErr.hook + '\r\n'; data += '報錯時間:' + time.toLocaleString() + '\r\n'; data += '報錯頁面:' + req.session.getErr.url + '\r\n'; data += '用戶信息:' + JSON.stringify(req.session.getErr.userInfo) + '\r\n'; content = data; // 記錄錯誤內容 fs.writeFile( 'toolmall_wap_err.txt', content, (err) => { if (err) throw err; }); }); return res.json({ok: true}); })
最終成果以下:
以上是文件夾,放在了根目錄下。
下面是日誌的內容:
終於完成了,今晚加雞腿~