Skip to content

Commit 4e6228f

Browse files
author
Andrew Starostin
committed
docs(logger): write about using logger instance and warn about logger: false
1 parent 9a6b951 commit 4e6228f

File tree

1 file changed

+56
-10
lines changed

1 file changed

+56
-10
lines changed

content/techniques/logger.md

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ You can tell Nest to use your extended logger for system logging by passing an i
105105

106106
For more advanced logging functionality, you'll want to take advantage of dependency injection. For example, you may want to inject a `ConfigService` into your logger to customize it, and in turn inject your custom logger into other controllers and/or providers. To enable dependency injection for your custom logger, create a class that implements `LoggerService` and register that class as a provider in some module. For example, you can
107107

108-
1. Define a `MyLogger` class that either extends the built-in `Logger` or completely overrides it, as shown in previous sections.
108+
1. Define a `MyLogger` class that either extends the built-in `Logger` or completely overrides it, as shown in previous sections. Be sure to implement the `LoggerService` interface.
109109
2. Create a `LoggerModule` as shown below, and provide `MyLogger` from that module.
110110

111111
```typescript
@@ -133,30 +133,68 @@ await app.listen(3000);
133133

134134
Here we use the `get()` method on the `NestApplication` instance to retrieve the singleton instance of the `MyLogger` object. This technique is essentially a way to "inject" an instance of a logger for use by Nest. The `app.get()` call retrieves the singleton instance of `MyLogger`, and depends on that instance being first injected in another module, as described above.
135135

136-
You can also inject this `MyLogger` provider in your feature classes, thus ensuring consistent logging behavior across both Nest system logging and application logging. See <a href="techniques/logger#using-the-logger-for-application-logging">Using the logger for application logging</a> below for more information.
136+
The only downside of this solution is that your first initialization messages won't be printed anywhere at all, so in rare cases you may miss some important initialization errors.
137+
Alternatively you can print first initialization messages using default logger, and then switch to your custom one:
137138

138-
The only downside of this solution is that your first initialization messages won't be handled by your logger instance, though, it shouldn't really matter at this point.
139+
```typescript
140+
const app = await NestFactory.create(ApplicationModule);
141+
app.useLogger(app.get(MyLogger));
142+
await app.listen(3000);
143+
```
144+
145+
You can also inject this `MyLogger` provider in your feature classes, thus ensuring consistent logging behavior across both Nest system logging and application logging. See <a href="techniques/logger#using-the-logger-for-application-logging">Using the logger for application logging</a> and <a href="techniques/logger#injecting-a-custom-logger">Injecting a custom logger</a> below for more information.
139146

140147
#### Using the logger for application logging
141148

142-
We can combine several of the techniques above to provide consistent behavior and formatting across both Nest system logging and our own application event/message logging. In this section, we'll achieve this with the following steps:
149+
We can combine several of the techniques above to provide consistent behavior and formatting across both Nest system logging and our own application event/message logging.
150+
151+
The best practice is to instantiate `Logger` class from `@nestjs/common` in each of our services.
152+
We can supply our service name as the `context` argument in the `Logger` constructor, like so:
143153

144-
1. We extend the built-in logger and customize the `context` portion of the log message (e.g., the phrase `NestFactory` in square brackets in the log line shown below).
154+
```typescript
155+
import { Logger, Injectable } from '@nestjs/common';
156+
157+
@Injectable()
158+
class MyService {
159+
logger = new Logger(MyService.name);
160+
161+
doSomething() {
162+
this.logger.log('Doing something...');
163+
}
164+
}
165+
```
145166

167+
In the default logger `context` is printed in the square brackets, like `NestFactory` in the example below:
146168
```bash
147169
[Nest] 19096 - 12/08/2019, 7:12:59 AM [NestFactory] Starting Nest application...
148170
```
149171

150-
2. We inject a [transient](/fundamentals/injection-scopes) instance of the `Logger` into our feature modules so that each one has its own custom context.
151-
3. We supply this extended logger for Nest to use for system logging.
172+
If we supply a custom logger via `app.useLogger`, it will actually be used by Nest automatically.
173+
That means that our code remains implementation agnostic, while we can easily substitute the default logger for our custom one by calling `app.useLogger`.
174+
That way if we follow the steps from previous section and call `app.useLogger(app.get(MyLogger))`, the following calls to `this.logger.log` from `MyService` would result in calls to method `log` from `MyLogger` instance.
175+
176+
This should be suitable for most cases. But if you need more customization for the Logger (like adding and calling custom methods), please follow to the next section.
177+
178+
#### Injecting a custom logger
179+
180+
Sometimes you may want to add custom methods to your logger class.
181+
In order to use these methods you'll have to inject a custom logger to your services.
182+
We can use a [transient](/fundamentals/injection-scopes) scope for the logger so that each one of our services has its own custom context.
183+
If we maintain the `LoggerService` interface in our custom logger, we can still set our logger globally with `app.useLogger`.
152184

153-
To start, extend the built-in logger with code like the following. We supply the `scope` option as configuration metadata for the `Logger` class, specifying a [transient](/fundamentals/injection-scopes) scope, to ensure that we'll have a unique instance of the `Logger` in each feature module. In this example, we do not extend the individual `Logger` methods (like `log()`, `warn()`, etc.), though you may choose to do so.
185+
To start, extend the built-in logger with code like the following. We supply the `scope` option as configuration metadata for the `Logger` class, specifying a [transient](/fundamentals/injection-scopes) scope, to ensure that we'll have a unique instance of the `MyLogger` in each feature module. In this example, we do not extend the individual `Logger` methods (like `log()`, `warn()`, etc.), though you may choose to do so.
154186

155187
```typescript
156188
import { Injectable, Scope, Logger } from '@nestjs/common';
157189

158190
@Injectable({ scope: Scope.TRANSIENT })
159-
export class MyLogger extends Logger {}
191+
export class MyLogger extends Logger {
192+
// Implement custom methods or extend default methods here,
193+
// for example:
194+
customLog() {
195+
this.log('Please feed the cat!');
196+
}
197+
}
160198
```
161199

162200
Next, create a `LoggerModule` with a construction like this:
@@ -172,7 +210,7 @@ import { MyLogger } from './my-logger.service';
172210
export class LoggerModule {}
173211
```
174212

175-
Next, import the `LoggerModule` into your feature module. Then set the logger context, and start using the context-aware custom logger, like this:
213+
Next, import the `LoggerModule` into your feature module. Since we extended default `Logger` we have the convenience of using `setContext` method. So we can start using the context-aware custom logger, like this:
176214

177215
```typescript
178216
import { Injectable } from '@nestjs/common';
@@ -183,11 +221,16 @@ export class CatsService {
183221
private readonly cats: Cat[] = [];
184222

185223
constructor(private myLogger: MyLogger) {
224+
// Due to transient scope, CatsService has its own unique instance of MyLogger,
225+
// so setting context here will not affect other instances in other services
186226
this.myLogger.setContext('CatsService');
187227
}
188228

189229
findAll(): Cat[] {
230+
// You can call all the default methods
190231
this.myLogger.warn('About to return cats!');
232+
// And your custom methods
233+
this.myLogger.customLog();
191234
return this.cats;
192235
}
193236
}
@@ -203,6 +246,9 @@ app.useLogger(new MyLogger());
203246
await app.listen(3000);
204247
```
205248

249+
Be mindful that if you supply `logger: false` to `NestFactory.create`, nothing will be logged until you call `useLogger`, so you may miss some important initialization errors.
250+
If you don't mind that some of your initial messages will be logged with the default logger, you can just omit the `logger: false` option.
251+
206252
#### Use external logger
207253

208254
Production applications often have specific logging requirements, including advanced filtering, formatting and centralized logging. Nest's built-in logger is used for monitoring Nest system behavior, and can also be useful for basic formatted text logging in your feature modules while in development, but production applications often take advantage of dedicated logging modules like [Winston](https://github.com/winstonjs/winston). As with any standard Node.js application, you can take full advantage of such modules in Nest.

0 commit comments

Comments
 (0)