教你在Nodejs中如何獲取當前函數被調用的行數及文件名

  • 蘇格團隊
  • 做者: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左右,因此是能夠忽略不計的。

相關文章
相關標籤/搜索