Updated Quickstart (markdown)
parent
8b492447d1
commit
557992b3e9
@ -1,65 +0,0 @@
|
||||
# Getting Started
|
||||
|
||||
Let's start from making a normal NestJS app.
|
||||
|
||||
```ts
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@Get()
|
||||
getHello(): string {
|
||||
return this.appService.getHello();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Important:** This decorator package is only accepting asynchronous function for now.
|
||||
|
||||
So we should change things to use async.
|
||||
|
||||
```ts
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@Get()
|
||||
async getHello(): Promise<string> {
|
||||
return await this.appService.getHello();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is the template you will get when you start with `nest new [name]`.
|
||||
|
||||
Now install `nestlogged` package.
|
||||
|
||||
```sh
|
||||
npm install nestlogged
|
||||
```
|
||||
|
||||
Or,
|
||||
|
||||
```sh
|
||||
yarn add nestlogged
|
||||
```
|
||||
|
||||
# Add Logging
|
||||
|
||||
In this package, logging decorator is separated by two kind.
|
||||
|
||||
1. LoggedRoute is used for methods in Controllers, with method decorators like @Get, @Post, etc.
|
||||
|
||||
2. LoggedFunction is used for methods in any other class methods, like service.
|
||||
|
||||
So, we can add a LoggedRoute decorator in controller example above.
|
||||
|
||||
```ts
|
||||
|
||||
```
|
622
Tutorial.md
Normal file
622
Tutorial.md
Normal file
@ -0,0 +1,622 @@
|
||||
# Getting Started
|
||||
|
||||
Let's start from making a normal NestJS app.
|
||||
|
||||
```ts
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@Get()
|
||||
getHello(): string {
|
||||
return this.appService.getHello();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Important:** This decorator package is only accepting asynchronous function for now.
|
||||
|
||||
So we should change things to use async.
|
||||
|
||||
```ts
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@Get()
|
||||
async getHello(): Promise<string> {
|
||||
return await this.appService.getHello();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is the template you will get when you start with `nest new [name]`.
|
||||
|
||||
Now install `nestlogged` package.
|
||||
|
||||
```sh
|
||||
npm install nestlogged
|
||||
```
|
||||
|
||||
Or,
|
||||
|
||||
```sh
|
||||
yarn add nestlogged
|
||||
```
|
||||
|
||||
# Basic Usage
|
||||
## Add Logs To Your Route
|
||||
|
||||
In this package, logging decorator is separated by two kind.
|
||||
|
||||
1. LoggedRoute is used for methods in Controllers, with method decorators like @Get, @Post, etc.
|
||||
|
||||
2. LoggedFunction is used for methods in any other class methods, like service.
|
||||
|
||||
So, we can add a LoggedRoute decorator in controller example above.
|
||||
|
||||
```ts
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
import { LoggedRoute } from 'nestlogged'; // Import...
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@LoggedRoute() // Add!
|
||||
@Get()
|
||||
async getHello(): Promise<string> {
|
||||
return await this.appService.getHello();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Note: LoggedRoute decorator should be always on top of method decorators.
|
||||
> If LoggedRoute is not placed correctly, it won't recognize its path and method.
|
||||
> If you want to know why, check out [Sequence of Decorator]() section in [Dive Deeper](https://github.com/Worplo/nestlogged/wiki/Dive-Deeper) page.
|
||||
|
||||
It will automatically add logs to your route, and log it when the route method is called.
|
||||
|
||||
```md
|
||||
[Nest] 2378244 - 12/10/2023, 6:53:49 PM LOG [AppController] HIT HTTP AppController::/[GET] (getHello)
|
||||
[Nest] 2378244 - 12/10/2023, 6:53:49 PM LOG [AppController] RETURNED RESPONSE AppController::/[GET] (getHello)
|
||||
```
|
||||
|
||||
Note that it uses Logger class provided by `@nestjs/common` package.
|
||||
|
||||
### Changing the logging path
|
||||
|
||||
Basically LoggedRoute decorator automatically detect its path and method, and add to log.
|
||||
|
||||
But if you want, You can also provide its path to LoggedRoute decorator.
|
||||
|
||||
```ts
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
import { LoggedRoute } from 'nestlogged';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@LoggedRoute('helloWorld') // give a path you want to log
|
||||
@Get()
|
||||
async getHello(): Promise<string> {
|
||||
return await this.appService.getHello();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this way, the LoggedRoute decorator will log like this:
|
||||
|
||||
```md
|
||||
[Nest] 2442776 - 12/10/2023, 7:04:49 PM LOG [AppController] HIT HTTP AppController::helloWorld[GET] (getHello)
|
||||
[Nest] 2442776 - 12/10/2023, 7:04:49 PM LOG [AppController] RETURNED RESPONSE AppController::helloWorld[GET] (getHello)
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Here, just path / changed to 'helloWorld' that we provided to decorator!
|
||||
```
|
||||
|
||||
## Add Logs To Your Class Methods
|
||||
|
||||
You can also add logs to your class methods, as simple as the LoggedRoute decorator above.
|
||||
|
||||
For example, in `app.service.ts` file:
|
||||
|
||||
```ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
async getHello(): Promise<string> {
|
||||
return 'Hello World!';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, add `LoggedFunction` decorator:
|
||||
|
||||
```ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { LoggedFunction } from 'nestlogged'; // Import...
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
@LoggedFunction // Add!
|
||||
async getHello(): Promise<string> {
|
||||
return 'Hello World!';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Unlike `LoggedRoute`, it can be placed without any rule, if you want.
|
||||
|
||||
The `LoggedFunction` decorator will automatically add logs to the call time.
|
||||
|
||||
When the `getHello` function called, it will automatically logs:
|
||||
|
||||
```md
|
||||
[Nest] 2459083 - 12/10/2023, 7:08:11 PM LOG [AppService] CALL getHello
|
||||
[Nest] 2459083 - 12/10/2023, 7:08:11 PM LOG [AppService] RETURNED getHello
|
||||
```
|
||||
|
||||
Since it is called from the http route `getHello`, the full log will printed.
|
||||
|
||||
```md
|
||||
[Nest] 2459083 - 12/10/2023, 7:08:11 PM LOG [AppController] HIT HTTP AppController::helloWorld[GET] (getHello)
|
||||
[Nest] 2459083 - 12/10/2023, 7:08:11 PM LOG [AppService] CALL getHello
|
||||
[Nest] 2459083 - 12/10/2023, 7:08:11 PM LOG [AppService] RETURNED getHello
|
||||
[Nest] 2459083 - 12/10/2023, 7:08:11 PM LOG [AppController] RETURNED RESPONSE AppController::helloWorld[GET] (getHello)
|
||||
```
|
||||
|
||||
In this log, you can see that the `getHello` in `AppController` is calling the `getHello` function in AppService, and then return, and return.
|
||||
|
||||
You can easily see the flow of the http request. Pretty cool, isn't it?
|
||||
|
||||
## Parameter Logging
|
||||
|
||||
You can also add a parameter log when the function or route is called.
|
||||
|
||||
Let's modify the previous app and add some parameter, so we can get some arguments.
|
||||
|
||||
`app.controller.ts`
|
||||
```ts
|
||||
import { Controller, Get, Query } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
import { LoggedRoute } from 'nestlogged';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@LoggedRoute()
|
||||
@Get()
|
||||
async getHello(
|
||||
@Query() { name = 'World' }: { name?: string },
|
||||
): Promise<string> {
|
||||
return await this.appService.getHello(name);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
`app.service.ts`
|
||||
```ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { LoggedFunction } from 'nestlogged';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
@LoggedFunction
|
||||
async getHello(name: string): Promise<string> {
|
||||
return `Hello ${name}!`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It's not that much different.
|
||||
|
||||
We add a query parameter `name`, pass to AppService's getHello, return Hello [name].
|
||||
|
||||
Now, if we add a query `?name=NestLogged` to url, you can get "Hello NestLogged!" instead of "Hello World!".
|
||||
|
||||
But still, decorators does not add any log for parameter.
|
||||
|
||||
What if we want to track the value of parameter `name`?
|
||||
|
||||
Just add @LoggedParam to parameter.
|
||||
|
||||
`app.controller.ts`
|
||||
```ts
|
||||
import { Controller, Get, Query } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
import { LoggedParam, LoggedRoute } from 'nestlogged';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@LoggedRoute()
|
||||
@Get()
|
||||
async getHello(
|
||||
// vvvvvvvvvvvvvvvvvvvvv
|
||||
@LoggedParam('query') @Query() { name = 'World' }: { name?: string },
|
||||
): Promise<string> {
|
||||
return await this.appService.getHello(name);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`app.service.ts`
|
||||
```ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { LoggedFunction, LoggedParam } from 'nestlogged';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
@LoggedFunction
|
||||
async getHello(@LoggedParam('name') name: string): Promise<string> {
|
||||
// ^^^^^^^^^^^^^^^^^^^^
|
||||
return `Hello ${name}!`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `LoggedParam` decorator will make a log for your parameter.
|
||||
|
||||
After change, the log will change like below:
|
||||
|
||||
```md
|
||||
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
||||
[Nest] 2555365 - 12/10/2023, 7:28:22 PM LOG [AppController] HIT HTTP AppController::/[GET] (getHello) WITH query={"name":"NestLogged"}
|
||||
vvvvvvvvvvvvvvvvvvvv
|
||||
[Nest] 2555365 - 12/10/2023, 7:28:22 PM LOG [AppService] CALL getHello WITH name=NestLogged
|
||||
[Nest] 2555365 - 12/10/2023, 7:28:22 PM LOG [AppService] RETURNED getHello
|
||||
[Nest] 2555365 - 12/10/2023, 7:28:22 PM LOG [AppController] RETURNED RESPONSE AppController::/[GET] (getHello)
|
||||
```
|
||||
|
||||
The first parameter of LoggedParam decides its name.
|
||||
|
||||
The second parameter, `options`, will be explained right after.
|
||||
|
||||
### Object Logging
|
||||
|
||||
So, we logged parameter. (Yay)
|
||||
|
||||
Now we're gonna make a simple user registration.
|
||||
|
||||
We'll make a route and service.
|
||||
|
||||
`types.d.ts`
|
||||
```ts
|
||||
export interface RegistrationBody {
|
||||
username: string;
|
||||
password: string;
|
||||
email: string;
|
||||
}
|
||||
```
|
||||
|
||||
`app.controller.ts`
|
||||
```ts
|
||||
import { BadRequestException, Body, Controller, Post } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
import { LoggedParam, LoggedRoute } from 'nestlogged';
|
||||
import { RegistrationBody } from './types';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@LoggedRoute()
|
||||
@Post('register')
|
||||
async userRegistration(
|
||||
@LoggedParam('data') @Body() data: RegistrationBody,
|
||||
): Promise<void> {
|
||||
if (
|
||||
(await this.appService.checkUsernameDuplication(data.username)) ||
|
||||
(await this.appService.checkEmailDuplication(data.email))
|
||||
) {
|
||||
throw new BadRequestException('Duplicated User');
|
||||
}
|
||||
|
||||
await this.appService.addUser(data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`app.service.ts`
|
||||
```ts
|
||||
import { RegistrationBody } from './types.d';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { LoggedFunction, LoggedParam } from 'nestlogged';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
@LoggedFunction
|
||||
async checkUsernameDuplication(
|
||||
@LoggedParam('username') username: string,
|
||||
): Promise<boolean> {
|
||||
/* Magic Function */
|
||||
}
|
||||
|
||||
@LoggedFunction
|
||||
async checkEmailDuplication(
|
||||
@LoggedParam('email') email: string,
|
||||
): Promise<boolean> {
|
||||
/* Magic Function */
|
||||
}
|
||||
|
||||
@LoggedFunction
|
||||
async addUser(
|
||||
@LoggedParam('data') data: RegistrationBody,
|
||||
) {
|
||||
/* Magic Function */
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For conciseness, we skipped service function implementations.
|
||||
|
||||
For now, let's assume that all service functions works without problem, and check duplication methods are always returning false.
|
||||
|
||||
Now, we're going to put the data using this route.
|
||||
|
||||
```sh
|
||||
$ curl -X POST "http://localhost:3000/register" -d "{\"username\": \"NestLogged\", \"password\": \"SuperSecretForUs\", \"email\": \"nestlogged@worplo.com\"}" -H "Content-Type: application/json"
|
||||
```
|
||||
|
||||
```md
|
||||
[Nest] 2649573 - 12/10/2023, 7:50:09 PM LOG [AppController] HIT HTTP AppController::register[POST] (userRegistration) WITH data={"username":"NestLogged","password":"SuperSecretForUs","email":"nestlogged@worplo.com"}
|
||||
[Nest] 2649573 - 12/10/2023, 7:50:09 PM LOG [AppService] CALL checkUsernameDuplication WITH username=NestLogged
|
||||
[Nest] 2649573 - 12/10/2023, 7:50:09 PM LOG [AppService] RETURNED checkUsernameDuplication
|
||||
[Nest] 2649573 - 12/10/2023, 7:50:09 PM LOG [AppService] CALL checkEmailDuplication WITH email=nestlogged@worplo.com
|
||||
[Nest] 2649573 - 12/10/2023, 7:50:09 PM LOG [AppService] RETURNED checkEmailDuplication
|
||||
[Nest] 2649573 - 12/10/2023, 7:50:09 PM LOG [AppService] CALL addUser WITH data={"username":"NestLogged","password":"SuperSecretForUs","email":"nestlogged@worplo.com"}
|
||||
[Nest] 2649573 - 12/10/2023, 7:50:09 PM LOG [AppService] RETURNED addUser
|
||||
[Nest] 2649573 - 12/10/2023, 7:50:09 PM LOG [AppController] RETURNED RESPONSE AppController::register[POST] (userRegistration)
|
||||
```
|
||||
|
||||
Well, that works, but... Our super secret is revealed in log.
|
||||
|
||||
That is a big problem. Fortunately, we have a solution for this.
|
||||
|
||||
the `LoggedParam` decorator has a second parameter, `options`, for this situation.
|
||||
|
||||
There is a two options, `includePath` and `excludePath`.
|
||||
|
||||
We want to **exclude** our secret in the log, so we can use option `excludePath`.
|
||||
|
||||
`app.controller.ts`
|
||||
```ts
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@LoggedRoute()
|
||||
@Post('register')
|
||||
async userRegistration(
|
||||
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
||||
@LoggedParam('data', { excludePath: ['password'] })
|
||||
@Body()
|
||||
data: RegistrationBody,
|
||||
): Promise<void> {
|
||||
if (
|
||||
(await this.appService.checkUsernameDuplication(data.username)) ||
|
||||
(await this.appService.checkEmailDuplication(data.email))
|
||||
) {
|
||||
throw new BadRequestException('Duplicated User');
|
||||
}
|
||||
|
||||
await this.appService.addUser(data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`app.service.ts`
|
||||
```ts
|
||||
import { RegistrationBody } from './types.d';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { LoggedFunction, LoggedParam } from 'nestlogged';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
// ...
|
||||
@LoggedFunction
|
||||
async addUser(
|
||||
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
||||
@LoggedParam('data', { excludePath: ['password'] }) data: RegistrationBody,
|
||||
): Promise<RegistrationBody> {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, if we see the log:
|
||||
|
||||
```md
|
||||
[Nest] 2688311 - 12/10/2023, 7:57:17 PM LOG [AppController] HIT HTTP AppController::register[POST] (userRegistration) WITH data={"username":"NestLogged","email":"nestlogged@worplo.com"}
|
||||
[Nest] 2688311 - 12/10/2023, 7:57:17 PM LOG [AppService] CALL checkUsernameDuplication WITH username=NestLogged
|
||||
[Nest] 2688311 - 12/10/2023, 7:57:17 PM LOG [AppService] RETURNED checkUsernameDuplication
|
||||
[Nest] 2688311 - 12/10/2023, 7:57:17 PM LOG [AppService] CALL checkEmailDuplication WITH email=nestlogged@worplo.com
|
||||
[Nest] 2688311 - 12/10/2023, 7:57:17 PM LOG [AppService] RETURNED checkEmailDuplication
|
||||
[Nest] 2688311 - 12/10/2023, 7:57:17 PM LOG [AppService] CALL addUser WITH data={"username":"NestLogged","email":"nestlogged@worplo.com"}
|
||||
[Nest] 2688311 - 12/10/2023, 7:57:17 PM LOG [AppService] RETURNED addUser
|
||||
[Nest] 2688311 - 12/10/2023, 7:57:17 PM LOG [AppController] RETURNED RESPONSE AppController::register[POST] (userRegistration)
|
||||
```
|
||||
|
||||
There is no secret anymore. Super simple, right?
|
||||
|
||||
We can also use `includePath` like: `@LoggedParam('data', { includePath: ['username', 'email'] })`.
|
||||
|
||||
# Advanced Usage
|
||||
## Logger Injection
|
||||
|
||||
So, that was basic usage.
|
||||
|
||||
But how can we get the logger, print whatever logs we want, while not losing the advantage of this decorator?
|
||||
|
||||
You can easily inject the logger using `InjectLogger` decorator.
|
||||
|
||||
Like this:
|
||||
|
||||
```ts
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@LoggedRoute()
|
||||
@Post('register')
|
||||
async userRegistration(
|
||||
@LoggedParam('data', { excludePath: ['password'] })
|
||||
@Body()
|
||||
data: RegistrationBody,
|
||||
@InjectLogger logger: ScopedLogger,
|
||||
): Promise<void> {
|
||||
if (
|
||||
(await this.appService.checkUsernameDuplication(data.username)) ||
|
||||
(await this.appService.checkEmailDuplication(data.email))
|
||||
) {
|
||||
logger.error('Duplicated data!!');
|
||||
throw new BadRequestException('Duplicated User');
|
||||
}
|
||||
|
||||
logger.log('Now calling addUser..');
|
||||
await this.appService.addUser(data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In later chapter, we will shortly explain what is `ScopedLogger`, and how can we really use it.
|
||||
|
||||
But, for now, just focus on logger injection.
|
||||
|
||||
Because we added `@InjectLogger`, the LoggedRoute knows where to put `ScopedLogger`.
|
||||
|
||||
So it can create and inject the new logger when the function is called.
|
||||
|
||||
You can also inject new `ScopedLogger` to any other methods.
|
||||
|
||||
For example, in service:
|
||||
|
||||
`app.service.ts`
|
||||
```ts
|
||||
// ...
|
||||
@LoggedFunction
|
||||
async addUser(
|
||||
@LoggedParam('data', { excludePath: ['password'] }) data: RegistrationBody,
|
||||
@InjectLogger logger?: ScopedLogger
|
||||
): Promise<RegistrationBody> {
|
||||
logger?.log('Now creating a new user...');
|
||||
return data;
|
||||
}
|
||||
// ...
|
||||
```
|
||||
|
||||
> Note: even if logger parameter is not provided when `addUser` method is called,
|
||||
|
||||
Like before, we can call service normally in the controller. We don't have to fix anything.
|
||||
|
||||
```md
|
||||
[Nest] 2771625 - 12/10/2023, 8:18:05 PM LOG [AppController] -> userRegistration: HIT HTTP AppController::register[POST] (userRegistration) WITH data={"username":"NestLogged","email":"nestlogged@worplo.com"}
|
||||
[Nest] 2771625 - 12/10/2023, 8:18:05 PM LOG [AppService] CALL checkUsernameDuplication WITH username=NestLogged
|
||||
[Nest] 2771625 - 12/10/2023, 8:18:05 PM LOG [AppService] RETURNED checkUsernameDuplication
|
||||
[Nest] 2771625 - 12/10/2023, 8:18:05 PM LOG [AppService] CALL checkEmailDuplication WITH email=nestlogged@worplo.com
|
||||
[Nest] 2771625 - 12/10/2023, 8:18:05 PM LOG [AppService] RETURNED checkEmailDuplication
|
||||
[Nest] 2771625 - 12/10/2023, 8:18:05 PM LOG [AppController] -> userRegistration: Now calling addUser..
|
||||
[Nest] 2771625 - 12/10/2023, 8:18:05 PM LOG [AppService] CALL addUser WITH data={"username":"NestLogged","email":"nestlogged@worplo.com"}
|
||||
[Nest] 2771625 - 12/10/2023, 8:18:05 PM LOG [AppService] RETURNED addUser
|
||||
[Nest] 2771625 - 12/10/2023, 8:18:05 PM LOG [AppController] -> userRegistration: RETURNED RESPONSE AppController::register[POST] (userRegistration)
|
||||
```
|
||||
|
||||
Except our call of injected logger, there is something slightly different.
|
||||
|
||||
Now, it is time to the key of this package. **Scoped Logging**.
|
||||
|
||||
## Scoped Logging
|
||||
|
||||
Simple.
|
||||
|
||||
When the user requested to our backend, there is a **flow**.
|
||||
|
||||
if username is existing in our database,
|
||||
```
|
||||
HIT /register -> CALL checkUsernameDuplication() -> RETURN true -> RETURN RESPONSE Forbidden
|
||||
```
|
||||
|
||||
if email is existing in our database,
|
||||
```
|
||||
HIT /register -> CALL checkUsernameDuplication() -> RETURN false -> CALL checkEmailDuplication() -> RETURN true -> RETURN RESPONSE Forbidden
|
||||
```
|
||||
|
||||
if successful,
|
||||
```
|
||||
HIT /register -> CALL checkUsernameDuplication() -> RETURN false -> CALL checkEmailDuplication() -> RETURN false -> CALL addUser() -> RETURN RegistrationBody -> RETURN RESPONSE Ok
|
||||
```
|
||||
|
||||
It is single line.
|
||||
|
||||
Then, how about reading its flow?
|
||||
|
||||
If there is a lot of request, it's hard to read a single flow.
|
||||
|
||||
So, ScopedLogger, makes a log **scoped**.
|
||||
|
||||
It's really simple to apply.
|
||||
|
||||
Just put existing ScopedLogger to the injected logger parameter place.
|
||||
|
||||
`app.controller.ts`
|
||||
```ts
|
||||
// ...
|
||||
@LoggedRoute()
|
||||
@Post('register')
|
||||
async userRegistration(
|
||||
@LoggedParam('data', { excludePath: ['password'] })
|
||||
@Body()
|
||||
data: RegistrationBody,
|
||||
@InjectLogger logger: ScopedLogger,
|
||||
): Promise<void> {
|
||||
if (
|
||||
(await this.appService.checkUsernameDuplication(data.username, logger)) || // added logger
|
||||
(await this.appService.checkEmailDuplication(data.email, logger)) // added logger
|
||||
) {
|
||||
logger.error('Duplicated data!!');
|
||||
throw new BadRequestException('Duplicated User');
|
||||
}
|
||||
|
||||
logger.log('Now calling addUser..');
|
||||
await this.appService.addUser(data, logger); // added logger
|
||||
}
|
||||
// ...
|
||||
```
|
||||
|
||||
In this way, logger will be like this:
|
||||
|
||||
```md
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: HIT HTTP AppController::register[POST] (userRegistration) WITH data={"username":"NestLogged","email":"nestlogged@worplo.com"}
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: -> checkUsernameDuplication: CALL checkUsernameDuplication WITH username=NestLogged
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: -> checkUsernameDuplication: There is no duplication!
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: -> checkUsernameDuplication: RETURNED checkUsernameDuplication
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: -> checkEmailDuplication: CALL checkEmailDuplication WITH email=nestlogged@worplo.com
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: -> checkEmailDuplication: There is no duplication!
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: -> checkEmailDuplication: RETURNED checkEmailDuplication
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: Now calling addUser..
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: -> addUser: CALL addUser WITH data={"username":"NestLogged","email":"nestlogged@worplo.com"}
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: -> addUser: Now creating a new user...
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: -> addUser: RETURNED addUser
|
||||
[Nest] 2859432 - 12/10/2023, 8:34:57 PM LOG [AppController] -> userRegistration: RETURNED RESPONSE AppController::register[POST] (userRegistration)
|
||||
```
|
||||
|
||||
You see the flow?
|
||||
|
||||
It's little longer than before, but you can easily see the flow.
|
||||
|
||||
But it's still hard to see the **single** flow.
|
||||
|
||||
For instance, if user 'a' and 'b' simultaneously call `/register` endpoint, it will be confusing to read only user `a`'s log.
|
Loading…
x
Reference in New Issue
Block a user