- Notifications
You must be signed in to change notification settings - Fork95
A Nest module wrapper form winston logger
License
gremo/nest-winston
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
ANest module wrapper forwinston logger.
Table of Contents
- Installation
- Quick start
- Async configuration
- Replacing the Nest logger
- Replacing the Nest logger (also for bootstrapping)
- Injection and usage summary
- Utilities
- Logger methods
- Upgrade
- Contributing
- License
npm install --save nest-winston winston
Having troubles configuringnest-winston
? Clone this repository andcd
in a sample:
cd sample/quick-startnpm installnpm run start:dev
If you want to upgrade to a major or minor version, have a look at theupgrade section.
ImportWinstonModule
into the rootAppModule
and use theforRoot()
method to configure it. This method accepts the same options object ascreateLogger()
function from the winston package:
import{Module}from'@nestjs/common';import{WinstonModule}from'nest-winston';import*aswinstonfrom'winston';@Module({imports:[WinstonModule.forRoot({// options}),],})exportclassAppModule{}
Afterward, the winston instance will be available to inject across entire project (and in your feature modules, beingWinstonModule
a global one) using theWINSTON_MODULE_PROVIDER
injection token:
import{Controller,Inject}from'@nestjs/common';import{WINSTON_MODULE_PROVIDER}from'nest-winston';import{Logger}from'winston';@Controller('cats')exportclassCatsController{constructor(@Inject(WINSTON_MODULE_PROVIDER)privatereadonlylogger:Logger){}}
Caveats: because the way Nest works, you can't inject dependencies exported from the root module itself (using
exports
). If you useforRootAsync()
and need to inject a service, that service must be either imported using theimports
options or exported from aglobal module.
Maybe you need to asynchronously pass your module options, for example when you need a configuration service. In such case, use theforRootAsync()
method, returning an options object from theuseFactory
method:
import{Module}from'@nestjs/common';import{WinstonModule}from'nest-winston';import*aswinstonfrom'winston';@Module({imports:[WinstonModule.forRootAsync({useFactory:()=>({// options}),inject:[],}),],})exportclassAppModule{}
The factory might be async, can inject dependencies withinject
option and import other modules using theimports
option.
Alternatively, you can use theuseClass
syntax:
WinstonModule.forRootAsync({useClass:WinstonConfigService,})
With the above code, Nest will create a new instance ofWinstonConfigService
and its methodcreateWinstonModuleOptions
will be called in order to provide the module options.
This module also provides theWinstonLogger
class (custom implementation of theLoggerService
interface) to be used by Nest for system logging. This will ensure consistent behavior and formatting across both Nest system logging and your application event/message logging.
Change yourmain.ts
as shown below:
import{WINSTON_MODULE_NEST_PROVIDER}from'nest-winston';asyncfunctionbootstrap(){constapp=awaitNestFactory.create(AppModule);app.useLogger(app.get(WINSTON_MODULE_NEST_PROVIDER));awaitapp.listen(3000);}bootstrap();
Then inject the logger using theWINSTON_MODULE_NEST_PROVIDER
token and theLoggerService
typing:
import{Controller,Inject,LoggerService}from'@nestjs/common';import{WINSTON_MODULE_NEST_PROVIDER}from'nest-winston';@Controller('cats')exportclassCatsController{constructor(@Inject(WINSTON_MODULE_NEST_PROVIDER)privatereadonlylogger:LoggerService){}}
Under the hood, theWinstonLogger
class uses the configured winston logger instance (throughforRoot
orforRootAsync
), forwarding all calls to it.
Important: by doing this, you give up the dependency injection, meaning that
forRoot
andforRootAsync
are not needed and shouldn't be used. Remove them from your main module.
Using the dependency injection has one minor drawback. Nest has to bootstrap the application first (instantiating modules and providers, injecting dependencies, etc.) and during this process the instance ofWinstonLogger
is not yet available, which means that Nest falls back to the internal logger.
One solution is to create the logger outside of the application lifecycle, using thecreateLogger
function, and pass it toNestFactory.create
. Nest will then wrap our winston logger (the same instance returned by thecreateLogger
method) into theLogger
class, forwarding all calls to it:
import{WinstonModule}from'nest-winston';asyncfunctionbootstrap(){constapp=awaitNestFactory.create(AppModule,{logger:WinstonModule.createLogger({// options (same as WinstonModule.forRoot() options)})});awaitapp.listen(3000);}bootstrap();
An alternative is to provide directly an instance of Winston in the options. This allows you to keep a reference to the instance and interact with it.
import{createLogger}from'winston';import{WinstonModule}from'nest-winston';asyncfunctionbootstrap(){// createLogger of Winstonconstinstance=createLogger({// options of Winston});constapp=awaitNestFactory.create(AppModule,{logger:WinstonModule.createLogger({ instance,}),});awaitapp.listen(3000);}bootstrap();
The usage afterwards for both solutions is the same. First, change your main module to provide theLogger
service:
import{Logger,Module}from'@nestjs/common';@Module({providers:[Logger],})exportclassAppModule{}
Then inject the logger simply by type hinting it withLogger
from@nestjs/common
:
import{Controller,Logger}from'@nestjs/common';@Controller('cats')exportclassCatsController{constructor(privatereadonlylogger:Logger){}}
Alternative syntax using theLoggerService
typing and the@Inject
decorator:
import{Controller,Inject,Logger,LoggerService}from'@nestjs/common';@Controller('cats')exportclassCatsController{constructor(@Inject(Logger)privatereadonlylogger:LoggerService){}}
Here is a summary of the three techniques explained above:
Injection token | Typing | Module config | Usage |
---|---|---|---|
WINSTON_MODULE_PROVIDER | Logger fromwinston | Yes | + Your application/message logging |
WINSTON_MODULE_NEST_PROVIDER | LoggerService from@nestjs/common | Yes | + Your application/message logging + Nest logger |
none | Logger from@nestjs/common | No | + Your application/message logging + Nest logger + Application bootstrapping |
The module also provides a custom Nest-like special formatter for console transports namednestLike
. Supported options:
colors
: enable console colors, defaults totrue
, unlessprocess.env.NO_COLOR
is set (same behaviour of Nest > 7.x)prettyPrint
: pretty format log metadata, defaults totrue
processId
: includes the Node Process ID (process.pid
) in the output, defaults totrue
appName
: includes the provided application name in the output, defaults totrue
Note: When providing partial options, unspecified options will retain their default values.
import{Module}from'@nestjs/common';import{utilitiesasnestWinstonModuleUtilities,WinstonModule}from'nest-winston';import*aswinstonfrom'winston';@Module({imports:[WinstonModule.forRoot({transports:[newwinston.transports.Console({format:winston.format.combine(winston.format.timestamp(),winston.format.ms(),nestWinstonModuleUtilities.format.nestLike('MyApp',{colors:true,prettyPrint:true,processId:true,appName:true,}),),}),// other transports...],// other options}),],})exportclassAppModule{}
Note: the logger instance has different logger methods, and each takes different arguments. To make sure the logger is being formatted the same way across the board take note of the following:
debug(message: any,context?: string)log(message: any,context?: string)error(message: any,stack?:string,context?: string)fatal(message: any,stack?:string,context?: string)verbose(message: any,context?: string)warn(message: any,context?: string)
Example:
import{Controller,Get,Logger}from'@nestjs/common';import{AppService}from'./app.service';@Controller()exportclassAppController{constructor(privatereadonlyappService:AppService,privatereadonlylogger:Logger,){} @Get()getHello():string{this.logger.log('Calling getHello()',AppController.name);this.logger.debug('Calling getHello()',AppController.name);this.logger.verbose('Calling getHello()',AppController.name);this.logger.warn('Calling getHello()',AppController.name);try{thrownewError()}catch(e){this.logger.error('Calling getHello()',e.stack,AppController.name);}returnthis.appService.getHello();}}
Some notes about upgrading to a major or minor version.
- The exported type
NestLikeConsoleFormatOptions
has slightly changed:prettyPrint
is now optional andcolors
has been added. - The
nestLike
formatter has the newcolors
option: if not provided, colors will be used according to Nest "approach" (disabled if env variableprocess.env.NO_COLOR
is defined). Before output was always colorized.
All types of contributions are encouraged and valued. See theContributing guidelines, the community looks forward to your contributions!
This project is released under the under terms of theISC License.
About
A Nest module wrapper form winston logger