Node.js ELK 日志規(guī)范

一般前端開(kāi)發(fā)同學(xué),對(duì)日志其實(shí)不太敏感,畢竟前端大多數(shù)情況下,不太關(guān)心日志。即使有,也可能調(diào)用一些第三方的統(tǒng)計(jì),比如百度統(tǒng)計(jì)或者別的等。在 Node.js(下文中簡(jiǎn)稱(chēng)node) 推進(jìn)過(guò)程中,也發(fā)現(xiàn)我們平常打日志太隨意,該打的日志沒(méi)有打,打的一些關(guān)鍵日志缺少必要上下文信息,導(dǎo)致在線(xiàn)上定位問(wèn)題的時(shí)候很困難。

本文主要梳理了目前我們團(tuán)隊(duì)在nodejs開(kāi)發(fā)中日志方面存在的問(wèn)題,以及通過(guò)統(tǒng)一日志規(guī)范,希望達(dá)到什么樣的效果。

問(wèn)題

  • node日志不規(guī)范,打日志太隨意
  • 沒(méi)有良好的日志格式、約定的字段,在 ELK 里不能很好的解析&檢索 (PS: ELK文章在路上)
  • 由于node對(duì)接的后端服務(wù)化,調(diào)用鏈不清晰,定位問(wèn)題困難
  • 數(shù)據(jù)部門(mén)對(duì)node日志的使用,沒(méi)有明確的記錄。node修改了日志,導(dǎo)致統(tǒng)計(jì)數(shù)據(jù)異常

目標(biāo)

  • 規(guī)范日志打印字段&格式,便于 ELK 檢索
  • 增強(qiáng)node上下游(nginx/后端)日志格式,加入惟一 requestId,方便微服務(wù)下定位問(wèn)題
  • 統(tǒng)計(jì)應(yīng)用運(yùn)行情況,性能數(shù)據(jù)
  • 維護(hù)數(shù)據(jù)部門(mén)對(duì)node日志的使用情況

實(shí)現(xiàn)方案

日志類(lèi)型

參考一些日志的最佳實(shí)踐,目前將node日志分為如下幾種類(lèi)型(scope):

  • desc: 系統(tǒng)啟動(dòng)、運(yùn)行過(guò)程中,打的日志,表明系統(tǒng)的一些啟動(dòng)日志、啟動(dòng)參數(shù)等,也包含在 不能 捕獲到http上下文的時(shí)候,打的日志
  • stat: 系統(tǒng)性能統(tǒng)計(jì)日志,應(yīng)用會(huì)定時(shí)收集一些性能信息,便于查詢(xún)應(yīng)用當(dāng)前狀態(tài)
  • visit: 每個(gè)http請(qǐng)求相關(guān)的日志,會(huì)包含惟一的 requestId,定位該請(qǐng)求相關(guān)的所有日志
  • biz: 業(yè)務(wù)數(shù)據(jù)相關(guān)日志,主要提供給數(shù)據(jù)統(tǒng)計(jì)使用

日志級(jí)別

只使用 FATALERRORWARNINFODEBUG 等級(jí)。

  • FATAL - 導(dǎo)致程序退出的嚴(yán)重系統(tǒng)級(jí)錯(cuò)誤,不可恢復(fù),當(dāng)錯(cuò)誤發(fā)生時(shí),系統(tǒng)管理員需要立即介入,一般應(yīng)用代碼 使用。
  • ERROR - 運(yùn)行時(shí)異常以及預(yù)期之外的錯(cuò)誤,也需要立即處理,但緊急程度低于FATAL,當(dāng)錯(cuò)誤發(fā)生時(shí),影響了程序的正確執(zhí)行。需要注意的是這兩種級(jí)別屬于服務(wù)自己的錯(cuò)誤,需要管理員介入,用戶(hù)輸入出錯(cuò)不屬于此分類(lèi),請(qǐng)求后端、讀文件、數(shù)據(jù)庫(kù)等超時(shí)、返回錯(cuò)誤結(jié)構(gòu),屬于ERROR
  • WARN - 預(yù)期之外的運(yùn)行時(shí)狀況,表示系統(tǒng)可能出現(xiàn)問(wèn)題。對(duì)于那些目前還不是錯(cuò)誤,然而不及時(shí)處理也會(huì)變成錯(cuò)誤的情況,也可以記為WARN,如磁盤(pán)過(guò)低。
  • INFO - 有意義的事件信息,記錄程序正常的運(yùn)行狀態(tài),比如收到請(qǐng)求,成功執(zhí)行。通過(guò)查看INFO,可以快速定位WARN,ERROR, FATAL。INFO不宜過(guò)多,通常情況下不超過(guò) DEBUG 的10%。
  • DEBUG - 與程序運(yùn)行時(shí)的流程相關(guān)的詳細(xì)信息以及當(dāng)前變量狀態(tài)。

日志格式/字段

日志格式統(tǒng)一采用 JSON ,便于 ELK 解析處理。

日志中的各個(gè)字段的值,都應(yīng)該盡量使用 英文 ,不使用中文。

日志具體字段,分為 基礎(chǔ)數(shù)據(jù) + 擴(kuò)展數(shù)據(jù)。基礎(chǔ)數(shù)據(jù),是底層日志框架自帶的,所有日志都會(huì)包含。擴(kuò)展數(shù)據(jù),不同類(lèi)型的日志,包含不同的字段。

日志基礎(chǔ)數(shù)據(jù)

目前使用的 node-bunyan 日志庫(kù),官方文檔,基礎(chǔ)字段包含如下:

  • v: integer 。bunyan的日志版本號(hào)
  • level: integer。日志級(jí)別對(duì)應(yīng)的數(shù)字
  • name: string。服務(wù)名
  • hostname: string。主機(jī)名
  • pid: integer。進(jìn)程號(hào)
  • time: string。UTC 格式的日期
  • msg: string。日志主體信息

日志擴(kuò)展數(shù)據(jù)

下面定義的各個(gè)數(shù)據(jù)類(lèi)型的擴(kuò)展數(shù)據(jù),不是 全部的字段,僅包含該日志類(lèi)型下,必需的字段。這些必需的擴(kuò)展字段,需要在 ELK 中建立索引,方便定位各種問(wèn)題。

  1. desc類(lèi)型日志,擴(kuò)展字段:TODO
  2. stat類(lèi)型日志,擴(kuò)展字段:{ perf: {rss: xxxx, cpu: xxx} }
  3. visit類(lèi)型日志,擴(kuò)展字段:
  4. biz
{
    /////////////  基礎(chǔ)數(shù)據(jù)  ////////
    v: 1,
    level: 20,
    ///////////// 擴(kuò)展字段  ////////
    // 標(biāo)志日志類(lèi)型
    scope: "visit",
    //事件類(lèi)型:在 visit 的日志類(lèi)型下,還會(huì)細(xì)分不同的事件,比如 client-req、client-res、 普通trace、請(qǐng)求后端service-start, service-end, service-err等。
    event: "trace",
    //客戶(hù)端ID,追蹤用戶(hù)、設(shè)備會(huì)話(huà)。在web端,可以是長(zhǎng)期的cookie;在APP端,可以是device-id等
    rrdid: "",
    //本次請(qǐng)求的惟一ID,串聯(lián)本次請(qǐng)求的所有相關(guān)日志
    req_id: "some-uuid-for-request",
    //本次請(qǐng)求的用戶(hù)ID
    uid: "",
    //本次請(qǐng)求的客戶(hù)端相關(guān)數(shù)據(jù),通過(guò)  ctx.logger 打日志時(shí),自動(dòng)加上
    d: {
        url: "/some/path?include-query",
        //客戶(hù)端ip
        ip: "10.138.10.1",
        //客戶(hù)端的 userAgent
        ua: ""
    },
    //本次node請(qǐng)求的處理時(shí)間,毫秒
    tm: 500,
    //該日志相關(guān)的上下文數(shù)據(jù),盡量拼成一個(gè)字符串,放在 extra 里
    extra: "",

    //ERROR 級(jí)別日志,最好包含error相關(guān)信息,比如請(qǐng)求后端相關(guān)參數(shù)等
    err: {
        msg: "",
        stack: ""
    },

    //調(diào)用后端服務(wù)相關(guān)參數(shù)和響應(yīng)
    service_req: {
        host: "",
        path: "",
        payload: ""
    },
    service_res: {
        //http狀態(tài)碼
        http_code: 200,
        //響應(yīng)時(shí)間
        tm: 100,
        //響應(yīng)的body
        body: "",
        //異常信息
        err: ""
    }
}

什么時(shí)候打日志

開(kāi)發(fā)者目前只關(guān)心 visit 類(lèi)型的日志,即和某一次http請(qǐng)求相關(guān)聯(lián)的日志。descstat類(lèi)型的日志,統(tǒng)一由開(kāi)發(fā)框架封裝后實(shí)現(xiàn),業(yè)務(wù)開(kāi)發(fā) 不用 關(guān)心。下面講的,都是針對(duì) visit 類(lèi)型的日志。

一次http請(qǐng)求,會(huì)打出一系列相關(guān)聯(lián)的日志。在node層,通常一次請(qǐng)求,會(huì)進(jìn)一步轉(zhuǎn)發(fā)給N個(gè)后端服務(wù),然后對(duì)后端數(shù)據(jù)進(jìn)行一些處理、合并等操作,最后渲染頁(yè)面或是輸出JSON。因此,一次請(qǐng)求相關(guān)的日志,大體分為以下幾種 event

  • client-req: client請(qǐng)求到達(dá)node層,統(tǒng)一由框架打日志,開(kāi)發(fā) 關(guān)心
  • service-start: node對(duì)某個(gè)后端服務(wù)發(fā)起請(qǐng)求,由通用請(qǐng)求庫(kù)負(fù)責(zé)打日志,開(kāi)發(fā) 關(guān)心
  • service-end: node請(qǐng)求某個(gè)后端服務(wù)結(jié)束,由通用請(qǐng)求庫(kù)負(fù)責(zé)打日志,開(kāi)發(fā) 關(guān)心
  • service-err: node請(qǐng)求后端服務(wù)異常,由通用請(qǐng)求庫(kù)負(fù)責(zé)打日志,開(kāi)發(fā) 關(guān)心。調(diào)用后端服務(wù)異常,日志級(jí)別為 WARN不是 ERROR
  • trace: node中業(yè)務(wù)層打的日志,如果異常,能幫助定位本次請(qǐng)求相關(guān)問(wèn)題
  • client-res: 結(jié)束client的請(qǐng)求,打印本次請(qǐng)求的http code,本次請(qǐng)求處理時(shí)間等,由框架統(tǒng)一打,開(kāi)發(fā) 關(guān)心

開(kāi)發(fā)同學(xué)在打日志時(shí),應(yīng)該謹(jǐn)慎的選擇級(jí)別,INFO(含)級(jí)別以上,都應(yīng)該能對(duì)定位問(wèn)題、具體業(yè)務(wù)統(tǒng)計(jì)需求有要求,才能使用。大部分情況下,可以使用 DEBUG 級(jí)別,線(xiàn)上 不會(huì) 開(kāi)啟DEBUG級(jí)別。

具體方法調(diào)用

針對(duì)打印 visit類(lèi)型的日志,調(diào)用 ctx.logger(基于Koa的框架) 屬性打日志,推薦參數(shù)都傳遞 JSON,具體方法如下:

ctx.logger.debug({msg: "", "extra": "a=1 b=2 c=value"});
ctx.logger.info({msg: "xxx", "extra": "其他的額外字段"});
ctx.logger.warn({msg: "xxx", "extra": "額外上下文數(shù)據(jù)"});
//ERROR級(jí)別日志,應(yīng)該提供 Error 對(duì)象
ctx.logger.error({msg: 'xxx', err: error, extra: ""});

注意1,額外的參數(shù),推薦存放在 extra 字段中,統(tǒng)一拼成 string;如果確實(shí)有必要單獨(dú)出每個(gè)字段, 禁止 額外的參數(shù)占用上述通用字段名!!

注意2,基礎(chǔ)數(shù)據(jù)中的msg字段,禁止 包含具體的上下文數(shù)據(jù),和該日志相關(guān)的上下文數(shù)據(jù),應(yīng)該拼成字符串,放在單獨(dú)的 extra 字段中。比如,某個(gè)用戶(hù)登錄接口,希望統(tǒng)計(jì)調(diào)用次數(shù),可以這樣打印:

ctx.logger.info({msg: "user login", "extra": 'mobile=18712387101 code=xxxx k3=value3'});

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,595評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,560評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,035評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,814評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,224評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,444評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,988評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,804評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,998評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,237評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,665評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,927評(píng)論 1 287
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,706評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,993評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容