- 蘇格團隊
- 做者:MaxPan
- 交流QQ羣:855833773
在自定義Egg.js的請求級別日誌這篇文章中,咱們實現了自定義請求級別的日誌模塊。看上去功能是完整了,但好像還缺點什麼。javascript
你們在根據日誌追查問題的過程當中,不少時候看到了某條log信息想去找出處,可是實際上代碼裏面打相同類型的log地方可能不止一處,這時你就比較難去定位這行log究竟是哪裏打的。html
舉個最極端的例子java
//home.js
class AppController extends app.Controller {
async first() {
this.ctx.swLog.info('in controller');
await this.ctx.render('first.html');
}
async second(){
this.ctx.swLog.info('in controller')
await this.ctx.render('second.html');
}
}
複製代碼
上面的例子雖然比較極端,可是咱們在代碼中不免會碰到相似的狀況。兩個route對於的controller中都打印了相同的log,你在查日誌的時候,是沒法區分log究竟是first裏面打的仍是second裏面打的。api
這個時候,咱們就須要在日誌打印的時候,同時也將調用日誌時的文件名和代碼行數記錄下來一併打印,效果以下bash
[2018-11-02 19:25:09.665][22896][home.js:4][/] in controller
複製代碼
查了好久的Nodejs文檔,發現Nodejs的api中並無直接提供咱們想到的信息,因此只能另找出路。app
回憶咱們以往的開發,這類的信息好像只有在Nodejs拋出異常的時候看到過。每當Nodejs拋出異常時,咱們都能看到一堆異常調用的堆棧,裏面就有咱們想要的信息,咱們從這開始入手。async
咱們先手動創造一個異常對象,並打印出來post
function getException() {
try {
throw Error('');
} catch (err) {
return err;
}
}
let err = getException();
console.log(err);
複製代碼
console的信息以下圖:性能
在圖上咱們能夠看到,咱們想要的信息ui
err對象在console的時候,會直接輸出err對象中的stack屬性,該屬性是個字符串,咱們能夠經過一系列的字符串操做,拿到咱們想要的文件名和行數。
接下來咱們開始對日誌模塊代碼進行改造,新增一個getCallerFileNameAndLine方法,以下:
getCallerFileNameAndLine(){
function getException() {
try {
throw Error('');
} catch (err) {
return err;
}
}
const err = getException();
const stack = err.stack;
const stackArr = stack.split('\n');
let callerLogIndex = 0;
for (let i = 0; i < stackArr.length; i++) {
if (stackArr[i].indexOf('Map.Logger') > 0 && i + 1 < stackArr.length) {
callerLogIndex = i + 1;
break;
}
}
if (callerLogIndex !== 0) {
const callerStackLine = stackArr[callerLogIndex];
return `[${callerStackLine.substring(callerStackLine.lastIndexOf(path.sep) + 1, callerStackLine.lastIndexOf(':'))}]`;
} else {
return '[-]';
}
}
複製代碼
最後咱們每條打印的日誌後面,都會跟上文件名和行數
有的同窗可能擔憂,每次打log都拋一個異常,會不會對性能形成影響。
我在getCallerFileNameAndLine
方法先後進行打點統計,平均執行時間在2ms
左右,因此是能夠忽略不計的。