Nest 附帶一個默認的內(nèi)部日志記錄器實現(xiàn),它在實例化過程中以及在一些不同的情況下使用,比如發(fā)生異常等等(例如系統(tǒng)記錄)。這由 @nestjs/common 包中的 Logger 類實現(xiàn)。你可以全面控制如下的日志系統(tǒng)的行為:
你也可以使用內(nèi)置日志記錄器,或者創(chuàng)建你自己的應用來記錄你自己應用水平的事件和消息。
更多高級的日志功能,可以使用任何 Node.js 日志包,比如Winston,來生成一個完全自定義的生產(chǎn)環(huán)境水平的日志系統(tǒng)。
要禁用日志,在(可選的)Nest 應用選項對象中向 NestFactory.create() 傳遞第二個參數(shù)設置 logger 屬性為 false 。
const app = await NestFactory.create(ApplicationModule, {
logger: false,
});
await app.listen(3000);
你也可以只啟用特定日志級別,設置一個字符串形式的 logger 屬性數(shù)組以確定要顯示的日志水平,如下:
const app = await NestFactory.create(ApplicationModule, {
logger: ['error', 'warn'],
});
await app.listen(3000);
數(shù)組中的字符串可以是以下字符串的任意組合: log , error , warn , debug 和 verbose 。
你可以通過設置 NO_COLOR 環(huán)境變量為非空字符串來禁用默認日志信息的顏色
你可以提供一個自定義日志記錄器應用,并由 Nest 作為系統(tǒng)記錄使用,這需要設置logger 屬性到一個滿足 LoggerService 接口的對象。例如,你可以告訴 Nest 使用內(nèi)置的全局 JavaScript console 對象(其實現(xiàn)了 LoggerService 接口),如下:
const app = await NestFactory.create(ApplicationModule, {
logger: console,
});
await app.listen(3000);
應用你的自定義記錄器很簡單。只要簡單實現(xiàn)以下 LoggerService 接口中的每個方法就可以:
import { LoggerService } from '@nestjs/common';
export class MyLogger implements LoggerService {
/**
* Write a 'log' level log.
*/
log(message: any, ...optionalParams: any[]) {}
/**
* Write an 'error' level log.
*/
error(message: any, ...optionalParams: any[]) {}
/**
* Write a 'warn' level log.
*/
warn(message: any, ...optionalParams: any[]) {}
/**
* Write a 'debug' level log.
*/
debug?(message: any, ...optionalParams: any[]) {}
/**
* Write a 'verbose' level log.
*/
verbose?(message: any, ...optionalParams: any[]) {}
}
你可以通過 logger 屬性為 Nest 應用的選項對象提供一個 MyLogger 實例:
const app = await NestFactory.create(ApplicationModule, {
logger: new MyLogger(),
});
await app.listen(3000);
這個技術雖然很簡單,但是沒有為 MyLogger 類應用依賴注入。這會帶來一些挑戰(zhàn),尤其在測試方面,同時也限制了 MyLogger 的重用性。更好的解決方案參見如下的依賴注入部分。
很多實例操作需要創(chuàng)建自己的日志。你不必完全重新發(fā)明輪子。只需繼承內(nèi)置 ConsoleLogger 類以部分覆蓋默認實現(xiàn),并使用 super 將調(diào)用委托給父類。
import { ConsoleLogger } from '@nestjs/common';
export class MyLogger extends ConsoleLogger {
error(message: any, stack?: string, context?: string) {
// add your tailored logic here
super.error.apply(this, arguments);
}
}
你可以按如下使用應用記錄器來記錄部分所述,從你的特征模塊中使用擴展記錄器。
你可以把你的擴展日志記錄器的實例傳遞到應用選項對象的 logger 屬性來讓 Nest 使用你的日志記錄器記錄系統(tǒng)日志(如自定義應用所述),也可以按照如下的依賴注入部分。如果你這樣做,你在調(diào)用 super 時要小心,如上述代碼示例,要委托一個特定的日志方法,調(diào)用其父(內(nèi)置)類,以便 Nest 可以依賴需要的內(nèi)置特征。
你可能需要利用依賴注入的優(yōu)勢來使用高級的日志記錄功能。例如,你可能想把 ConfigService 注入到你的記錄器中來對它自定義,然后把自定義記錄器注入到其他控制器和/或提供者中。要為你的自定義記錄器啟用依賴注入,創(chuàng)建一個實現(xiàn) LoggerService 的類并將其作為提供者注冊在某些模塊中,例如,你可以:
import { Module } from '@nestjs/common';
import { MyLogger } from './my-logger.service.ts';
@Module({
providers: [MyLogger],
exports: [MyLogger],
})
export class LoggerModule {}
通過這個結構,你現(xiàn)在可以提供你的自定義記錄器供其他任何模塊使用。因為你的 MyLogger 類是模塊的一部分,它也可以使用依賴注入(例如,注入一個 ConfigService )。提供自定義記錄器供使用還需要一個技術,即 Nest 的系統(tǒng)記錄(例如,供 bootstrapping 和 error handling )。
由于應用實例化( NestFactory.create() )在任何模塊上下文之外發(fā)生,它不能參與初始化時正常的依賴注入階段。因此我們必須保證至少一個應用模塊導入了 LoggerModule 來觸發(fā) Nest ,從而生成一個我們的 MyLogger 類的單例。
我們可以在之后按照下列知道來告訴 Nest 使用同一個 MyLogger 實例。
const app = await NestFactory.create(ApplicationModule, {
bufferLogs: true,
});
app.useLogger(app.get(MyLogger));
await app.listen(3000);
在上面的例子中,我們把 bufferLogs 設置為 true 以確保所有的日志都會被放入緩沖區(qū)直到一個自定義的日志記錄器被接入(在上面的例子中是 MyLogger )并且應用初始化成功或者失敗。如果初始化失敗,Nest 會回退到原始的 ConsoleLogger 以打印出錯誤信息。你也可以將 autoFlushLogs 設置為 false (默認為 true )來手動刷新日志緩沖區(qū)(使用 Logger#flush() 方法)。
在這里我們在 NestApplication 實例中用了 get() 方法以獲取 MyLogger 對象的單例。這個技術在根本上是個“注入”一個日志記錄器的實例供 Nest 使用的方法。 app.get() 調(diào)用獲取 MyLogger 單例,并且像之前所述的那樣依賴于第一個注入到其他模塊的實例。
你也可以在你的特征類中注入這個 MyLogger 提供者,從而保證 Nest 系統(tǒng)記錄和應用記錄行為一致。參考為應用記錄使用記錄器和注入一個自定義日志記錄器章節(jié)以獲取更多信息。
我們可以組合上述幾種技術來提供一致性的行為和格式化以保證我們的應用事件/消息記錄和 Nest 系統(tǒng)記錄一致。
一個很好的實踐是在每個提供者內(nèi)實例化 @nestjs/common 內(nèi)的 Logger 類。我們可以將提供者的名字當作 context 參數(shù)傳入 Logger 的構造函數(shù),就像這樣:
import { Logger, Injectable } from '@nestjs/common';
@Injectable()
class MyService {
private readonly logger = new Logger(MyService.name);
doSomething() {
this.logger.log('Doing something...');
}
}
在默認的日志記錄器實現(xiàn)中, context 是包裹在方括號中被打印出來,就像下面例子中的 NestFactory :
[Nest] 19096 - 12/08/2019, 7:12:59 AM [NestFactory] Starting Nest application...
如果我們通過 app.useLogger() 提供一個自定義日志記錄器,那么它會在 Nest 內(nèi)部被使用。這就意味著我們的代碼可以保持與實現(xiàn)無關,因為我們可以簡單地調(diào)用 app.useLogger() 用默認日志記錄器來代替自定義的那個。
如果我們跟著前面章節(jié)一步步做下來并且調(diào)用了 app.useLogger(app.get(MyLogger)) ,那么接下來在 MyService 中對 this.logger.log() 的調(diào)用會造成對 MyLogger 實例中方法 log 的調(diào)用。
這個應該在大多數(shù)情況下都適用。但是你如果想要更深入的自定義(比如增加或者調(diào)用自定義方法),請看下一章節(jié)。
通過像下面一樣的擴展內(nèi)置的日志記錄器來開始這一章節(jié)。我們傳入 scope 選項來配置 ConsoleLogger 的元數(shù)據(jù),通過指定瞬態(tài)作用域來保證在每個模塊內(nèi)都有獨一無二的 MyLogger 的實例。在下面的例子中,我們沒有擴展每個單獨的 ConsoleLogger 方法(比如 log() , warn() 之類),盡管你可能會選擇去這么做。
import { Injectable, Scope, ConsoleLogger } from '@nestjs/common';
@Injectable({ scope: Scope.TRANSIENT })
export class MyLogger extends ConsoleLogger {
customLog() {
this.log('Please feed the cat!');
}
}
接下來,我們采用如下結構創(chuàng)建一個 LoggerModule 。
import { Module } from '@nestjs/common';
import { MyLogger } from './my-logger.service';
@Module({
providers: [MyLogger],
exports: [MyLogger],
})
export class LoggerModule {}
然后,在你的模塊中導入 LoggerModule 。因為我們繼承了默認的 Logger ,所以我們可以很方便地調(diào)用 setContext 方法。之后我們就可以像下面一樣開始使用這個包含了上下文的日志記錄器了。
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
constructor(private myLogger: MyLogger) {
// 因為瞬態(tài)作用域的原因, CatService 有屬于自己的獨一無二的 MyLogger 實例,
// 所以在這里設置上下文不會影響到其他提供者的實例
this.myLogger.setContext('CatsService');
}
findAll(): Cat[] {
// 你可以調(diào)用所有的默認方法
this.myLogger.warn('About to return cats!');
// 當然還有你的自定義方法
this.myLogger.customLog();
return this.cats;
}
}
最后,和下面一樣在你的 main.ts 文件中讓 Nest 使用自定義日志記錄器的實例。當然這只是個例子,我們還沒有真正自定義日志記錄器的行為(通過擴展像 log() , warn() 這些 Logger 的方法),所以這一步并不一定需要。但是如果你給這些方法增加了自定義的邏輯而且你想讓 Nest 去使用這個實現(xiàn),那么你還是會需要這一步。
const app = await NestFactory.create(ApplicationModule, {
bufferLogs: true,
});
app.useLogger(new MyLogger());
await app.listen(3000);
除了把 bufferLogs 設置為true,你也可以聲明 logger: false 來臨時禁用日志記錄器。需要注意的是如果你給 NestFactory.create 提供了 logger: false ,在你調(diào)用 useLogger 以前沒有東西會被記錄進日志,所以你可能會錯過一些重要的初始化錯誤。如果你不在意一些初始化信息會使用默認日志記錄器來記錄,那你可以直接設置 logger: false 。
生產(chǎn)環(huán)境應用通常包括特定的記錄需求,包括高級過濾器,格式化和中心化記錄。Nest 的內(nèi)置記錄器用于監(jiān)控 Nest 系統(tǒng)狀態(tài),在開發(fā)時也可以為你的特征模塊提供實用的基礎的文本格式的記錄,但生產(chǎn)環(huán)境可能更傾向于使用類似Winston的模塊,這是一個標準的 Node.js 應用,你可以在 Nest 中體驗到類似模塊的優(yōu)勢。
更多建議: