Skip to content

Commit 7f430cc

Browse files
Merge pull request #1441 from mayorandrew/update-logger
Improve docs about using Logger
2 parents 9b63722 + 6123fff commit 7f430cc

File tree

1 file changed

+48
-10
lines changed

1 file changed

+48
-10
lines changed

content/techniques/logger.md

Lines changed: 48 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,61 @@ 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+
A good practice is to instantiate `Logger` class from `@nestjs/common` in each of our services. We can supply our service name as the `context` argument in the `Logger` constructor, like so:
143152

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).
153+
```typescript
154+
import { Logger, Injectable } from '@nestjs/common';
155+
156+
@Injectable()
157+
class MyService {
158+
private readonly logger = new Logger(MyService.name);
159+
160+
doSomething() {
161+
this.logger.log('Doing something...');
162+
}
163+
}
164+
```
165+
166+
In the default logger implementation, `context` is printed in the square brackets, like `NestFactory` in the example below:
145167

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 internally. That means that our code remains implementation agnostic, while we can easily substitute the default logger for our custom one by calling `app.useLogger()`.
173+
174+
That way if we follow the steps from the 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 (like adding and calling custom methods), move to the next section.
177+
178+
#### Injecting a custom logger
152179

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.
180+
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.
154181

155182
```typescript
156183
import { Injectable, Scope, Logger } from '@nestjs/common';
157184

158185
@Injectable({ scope: Scope.TRANSIENT })
159-
export class MyLogger extends Logger {}
186+
export class MyLogger extends Logger {
187+
customLog() {
188+
this.log('Please feed the cat!');
189+
}
190+
}
160191
```
161192

162193
Next, create a `LoggerModule` with a construction like this:
@@ -172,7 +203,7 @@ import { MyLogger } from './my-logger.service';
172203
export class LoggerModule {}
173204
```
174205

175-
Next, import the `LoggerModule` into your feature module. Then set the logger context, and start using the context-aware custom logger, like this:
206+
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:
176207

177208
```typescript
178209
import { Injectable } from '@nestjs/common';
@@ -183,11 +214,16 @@ export class CatsService {
183214
private readonly cats: Cat[] = [];
184215

185216
constructor(private myLogger: MyLogger) {
217+
// Due to transient scope, CatsService has its own unique instance of MyLogger,
218+
// so setting context here will not affect other instances in other services
186219
this.myLogger.setContext('CatsService');
187220
}
188221

189222
findAll(): Cat[] {
223+
// You can call all the default methods
190224
this.myLogger.warn('About to return cats!');
225+
// And your custom methods
226+
this.myLogger.customLog();
191227
return this.cats;
192228
}
193229
}
@@ -203,6 +239,8 @@ app.useLogger(new MyLogger());
203239
await app.listen(3000);
204240
```
205241

242+
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. 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.
243+
206244
#### Use external logger
207245

208246
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)