自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何在 Node.js 中正確的使用日志對象

開發(fā) 前端
日志,是開發(fā)者排查問題的非常重要的手段,有時候甚至是唯一的,所以如何合理并正確的打印日志,成了開發(fā)時的重中之重。

日志,是開發(fā)者排查問題的非常重要的手段,有時候甚至是唯一的,所以如何合理并正確的打印日志,成了開發(fā)時的重中之重。

Node.js 中打日志的方式,一般有幾種:

  1. 主動展示
  2. 被動記錄

這兩種方式都可以由不同的模塊來實現(xiàn),我們接下去就來看看怎么選擇。

**常見的主動展示

**

一般來說,主動一般發(fā)生在開發(fā)期,不確定狀態(tài)的時候,我們會打印一些消息,比如常見的。 

  1. console.log('hello world'); 

這就是最簡單的主動打印的例子。

但是大多數(shù)場景下,我們都不會使用 console 來進行打印,畢竟除了內(nèi)置之外,在性能和功能方面沒有特別的優(yōu)勢。

除了大眾都知道的 console 模塊,在 Node.js 領(lǐng)域還有一個較為知名的 debug 模塊。

 

可以根據(jù)命名空間打印出不同顏色的輸出,但是最最有用的,則是他的環(huán)境變量控制能力。 

默認情況下(不包含任何環(huán)境變量),控制臺不會有任何輸出,而當 DEBUG 環(huán)境變量被賦值的時候,對應的命名空間的輸出才會被打印到 stdout。 

  1. $ DEBUG=* node app.js 

由于 debug 模塊由 TJ 出品,并且在非常早的時候就投入,使用過于廣泛,至今仍有非常多的模塊使用了它。

Node.js 官方一直希望能夠內(nèi)置一個 debug 模塊。從 v0.11.3 開始,終于加上了一個 util.debuglog 方法。

它的功能和 debug 模塊類似,同時是內(nèi)置的模塊,所以逐步也有一些模塊開始過渡到它。 

  1. const util = require('util'); 
  2. const debuglog = util.debuglog('foo');  
  3. debuglog('hello from foo [%d]', 123); 

它的開關(guān)也類似,使用的是 NODE_DEBUG 環(huán)境變量,應該是特意和 debug 模塊做了區(qū)分。 

  1. $ NODE_DEBUG=foo node app.js 

被動記錄的方式

除了上面提到的類 console 等方式,我們常見的就是各種日志庫默認記錄的日志,由于這些日志平時只是默默的記錄,并不會過多關(guān)注,只會在特殊需要的時候(比如差錯,定位,計算時)才會查看,所以我們歸類為 “被動的方式”。

大多的三方庫都有類似的功能,比如 log4j,winston,pino 等等。

這些庫的核心功能一般是:

  1. 將日志輸出到不同的渠道(比如控制臺、文本文件)
  2. 日志格式的自定義(文本或者 JSON)
  3. 日志的輸出等級(warn,debug,error)
  4. 其他的一些能力,比如切割和文件輪轉(zhuǎn),壓縮等等

這些庫用起來一般就比較簡單,獲取實例,調(diào)用方法輸出即可。

  1. logger.info('hello world'); 

注意,這里我們會觀察到輸出有一些不一樣的地方。 

  1. 2021-07-22 14:50:59,388 INFO 7739 [xxx] hello world 

整個日志是安裝上面類似標準的結(jié)構(gòu)來進行輸出的,計算是 Error,也是相同的類似格式,那么這個結(jié)構(gòu)包含了哪幾部分東西呢?

日志格式

其實整個日志格式追溯,可以到很久以前,不管是 JAVA 默認的 Simple Logger 結(jié)構(gòu)還是類似 nginx 等反向代理服務器的日志,都會包含一些固定的字段,這些固定的字段長久以來形成了一種輸出約定,將這些字段組合起來,形成了當今的日志格式。

當前的日志格式一般會包括幾個部分。

  • 時間戳
  • 日志等級
  • 進程id(node)
  • 日志的標簽(label,from xxx class)
  • 消息體(字符串或者 error stack)

除此之外,可能還有一些自定義的內(nèi)容,比如執(zhí)行消耗的時間,用戶 id,文本長度等等內(nèi)容。

在文本結(jié)構(gòu)的輸出中,這些字段將被空格(space)分隔,以換行符作為結(jié)尾(\n),這樣可以方便外部的日志采集系統(tǒng)采集,比如阿里云的 SLS 等等。

每個公司會有自己的日志采集和輸出規(guī)范,所以一般常見的庫都會支持自定義的日志格式,但是不管如何變化,基礎(chǔ)的字段(上述)都還會存在。

隨著系統(tǒng)的迭代,先進使用 JSON 格式來記錄日志的方式也逐步出現(xiàn),以 Logstash 為首的一些數(shù)據(jù)(日志)采集分析一體的工具,也逐步的成熟,對結(jié)構(gòu)化的數(shù)據(jù)支持的也很好,所以現(xiàn)在常見的庫也會同步支持 JSON 格式輸出。 

正確的打日志 

在了解了基本的日志庫和體系之后,我們來具體看一看真正打日志的問題。

比如一個簡單調(diào)用遠端服務: 

  1. async invokeRemoteAPI() { 
  2.   const result = await remoteService.got(); 
  3.   return { 
  4.     result 
  5.   }; 

一般,我們會有意識的加上錯誤處理。 

  1. async invokeRemoteAPI() { 
  2.    
  3.   try { 
  4.     const result = await remoteService.got(); 
  5.   } catch(err) { 
  6.     logger.error('got a error, err=', err); 
  7.     throw err; 
  8.   } 
  9.    
  10.   return { 
  11.     result 
  12.   }; 

按照上面的標準格式,這個 logger 還需要其他的一些額外信息,比如: 

  1. async invokeRemoteAPI() { 
  2.    
  3.   const pid = process.pid; 
  4.   const startTime = Date.now(); 
  5.    
  6.   try { 
  7.     const result = await remoteService.got(); 
  8.   } catch(err) { 
  9.     const endTime = Date.now(); 
  10.     logger.error('pid=%s, rt=%s, got a error, err=', pid, Date.now() - startTime, err); 
  11.     throw err; 
  12.   } 
  13.    
  14.   return { 
  15.     result 
  16.   }; 

如果每個代碼都這么寫,就會變得無比冗余,所以,我們會提前將日志的輸出格式定義完畢,這樣,在實際輸出的時候就可以簡化,比如: 

  1. const logger = new CustomLogger({ 
  2.   format: '${timestamp} ${level} ' + process.pid + ${rt}' 
  3. }); 
  4.  
  5. async invokeRemoteAPI() { 
  6.   const startTime = Date.now(); 
  7.    
  8.   try { 
  9.     const result = await remoteService.got(); 
  10.   } catch(err) { 
  11.     const endTime = Date.now(); 
  12.     logger.error('got a error, err=', err, { 
  13.       rt: Date.now() - startTime 
  14.     }); 
  15.     throw err; 
  16.   } 
  17.    
  18.   return { 
  19.     result 
  20.   }; 

所以在特定場景下,如果有固定的日志字段,在日志庫允許自定義的情況下,可以先定義好固定的日志格式。

上下文日志

除了最簡單的通用日志輸出之外,還有一種相對復雜的日志,我們稱之為和上下文(請求)綁定的日志,這類日志會輸出上下文相關(guān)聯(lián)的數(shù)據(jù),比如之前示例中的響應時間,用戶請求的 ip,請求的路由,甚至是鏈路的唯一 ID 等等。

比如: 

  1. 2021-07-22 14:50:59,388 INFO 7739 [-/127.0.0.1/-/0ms GET /] hello world 

這種情況下,再用普通日志的方式加入?yún)?shù)就不合適了。

當然,有些同學會說,我們直接定義一個新的,比如: 

  1. class CustomCtxLogger extends CustomLogger { 
  2.   constructor(ctx, format) { 
  3.     this.ctx = ctx; 
  4.     this.format = format; 
  5.   } 
  6.    
  7.   error(...args) { 
  8.     //xxx 
  9.   } 
  10.    
  11.   info(...args) { 
  12.     //xxx 
  13.   } 

這樣的做法,每次都會讓基類做初始化,會影響部分性能。我們使用另一種方式來減少性能影響,代理傳統(tǒng)日志。

我們來看看最簡單的實現(xiàn)方式,以 koa 為例。 

  1. // 普通日志 
  2. const logger = new CustomLogger(); 
  3.  
  4. class CtxLogger { 
  5.   constructor(ctx, logger) { 
  6.     this.ctx = ctx; 
  7.     this.logger = logger; 
  8.   } 
  9.    
  10.   format() { 
  11.     return '${timestamp} ${level} ' + process.pid + '[${ctx.refer} ${ctx.rt}]' 
  12.   } 
  13.  
  14. app.use(async (ctx, next) => { 
  15.   // 代理原始日志 
  16.   const ctxLogger = new CtxLogger(ctx, logger); 
  17.   ctx.logger = ctxLogger; 
  18.    
  19.   await next(); 
  20. }); 

類似這種通過代理原始日志的方式,即減少了每次初始化新日志時的性能問題,又解決了 ctx 上字段透傳的問題。

這也是常見的上下文日志的實踐。

簡單總結(jié)一下

我們了解了常用的日志庫以及和日志打印的關(guān)系,也簡單的實現(xiàn)了日志庫以及上下文日志的實現(xiàn),是不是現(xiàn)在對日志打印了有了一個基本的了解?

這樣一套下來,相信你對 Node.js 打印日志的方式更加的了解,也在排錯時游刃有余了。 

 

責任編輯:龐桂玉 來源: 前端大全
相關(guān)推薦

2021-07-30 11:20:53

JavaScriptNode.jsWeb Develop

2020-08-05 08:31:51

SSL TLSNode.js

2021-07-03 17:43:03

Node.jsNode變量

2015-08-05 09:33:21

Javawaitnotify

2022-11-17 09:52:12

RHEL 9Node.js

2011-09-09 14:23:13

Node.js

2021-08-20 16:05:28

JavaScript node.js 應用安全

2023-11-26 18:31:41

Linux信號

2022-08-22 07:26:32

Node.js微服務架構(gòu)

2022-09-12 15:58:50

node.js微服務Web

2022-08-28 16:30:34

Node.jsDocker指令

2021-06-15 15:03:21

MongoDBNode.jsCRUD

2019-10-18 10:43:11

JPASpring Boot Flyway

2021-05-18 09:01:39

Node.jsJSON文件

2021-07-15 10:15:52

Node.jsJSON前端

2022-08-12 07:01:00

Node.jsXSS腳本

2021-05-27 09:00:00

Node.js開發(fā)線程

2022-09-04 15:54:10

Node.jsAPI技巧

2017-05-10 09:40:57

Ubuntupm2Nginx

2023-01-10 14:11:26

點贊
收藏

51CTO技術(shù)棧公眾號