A 类 鸟巢 用于登录进/出请求的截取器.
NestJS 记录拦截器
一个简单的NestJS截取器 捕捉请求的细节并使用内置记录 锁定器 课。 它会使用默认的 Logger 执行, 除非您将自己的程序传递给您的巢穴应用程序 .
安装
npm install --save @algoan/nestjs-logging-interceptor
使用量
默认使用
使用截取器作为全局截取器(参见 最后段落 (详见本节).
示例
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { LoggingInterceptor } from '@algoan/nestjs-logging-interceptor';
/**
* Core module: This module sets the logging interceptor as a global interceptor
*/
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
],
})
export class CoreModule {}
在上述例子中,拦截器由CoreModule提供. 它可以设置在您主模块使用的任何模块上 .
工厂
也可以手动通过工厂函数传递截取器实例. 这样,就有可能制定 userPrefix 默认上下文消息的首端:
示例
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { LoggingInterceptor } from '@algoan/nestjs-logging-interceptor';
/**
* Core module: This module sets the logging interceptor as a global interceptor
*/
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useFactory: () => new LoggingInterceptor({ userPrefix: 'ExampleApp'}),
},
],
})
export class CoreModule {}
上下文消息将预先由提供的 userPrefix数字 :
[LoggingInterceptor - GET - /error] Incoming request - GET - /error
==>
[ExampleApp - LoggingInterceptor - GET - /error] Incoming request - GET - /error
日志消息
截取器日志 :
- 收到的请求细节
- 发出答复的细节
默认 Logger 信件
# Incoming request details
# info level
[Nest] 27080 - 04/06/2020, 10:11:54 AM [LoggingInterceptor - GET - /] Object:
{
"message": "LoggingInterceptor - GET - /",
"method": "GET",
"body": {},
"headers": {
"host": "localhost:3000",
"user-agent": "insomnia/7.1.1",
"authorization": "Bearer 1234",
"accept": "*/*"
}
}
- 发送请求细节( info + (debug qq警告qq错误))
# Success example
# Info level
[Nest] 27080 - 04/06/2020, 10:11:54 AM [LoggingInterceptor - 200 - GET - /] Object:
{
"message": "LoggingInterceptor - 200 - GET - /",
"body": "Hello World!"
}
# Warning example
# warn level
[Nest] 27080 - 04/06/2020, 10:12:44 AM [LoggingInterceptor - 400 - GET - /badrequest] Object:
{
"method": "GET",
"url": "/badrequest",
"error": {
"response": {
"statusCode": 400,
"message": "Bad Request"
},
"status": 400,
"message": "Bad Request"
},
"body": {},
"message": "LoggingInterceptor - 400 - GET - /badrequest"
}
# Error example
# error level
[Nest] 27080 - 04/06/2020, 10:12:17 AM [LoggingInterceptor - 500 - GET - /error] Object:
{
"method": "GET",
"url": "/error",
"body": {},
"message": "LoggingInterceptor - 500 - GET - /error"
}
+2ms
Error: Internal Server Error
at AppController.error (/Users/philippediep/Documents/workspace/algoan/examples/example-manager/dist/app.controller.js:25:15)
at /Users/philippediep/Documents/workspace/algoan/examples/example-manager/node_modules/@nestjs/core/router/router-execution-context.js:37:29
at InterceptorsConsumer.transformDeffered (/Users/philippediep/Documents/workspace/algoan/examples/example-manager/node_modules/@nestjs/core/interceptors/interceptors-consumer.js:30:28)
at /Users/philippediep/Documents/workspace/algoan/examples/example-manager/node_modules/@nestjs/core/interceptors/interceptors-consumer.js:14:48
at Observable._subscribe (/Users/philippediep/Documents/workspace/algoan/examples/example-manager/node_modules/rxjs/internal/observable/defer.js:10:21)
at Observable._trySubscribe (/Users/philippediep/Documents/workspace/algoan/examples/example-manager/node_modules/rxjs/internal/Observable.js:44:25)
at Observable.subscribe (/Users/philippediep/Documents/workspace/algoan/examples/example-manager/node_modules/rxjs/internal/Observable.js:30:22)
at Object.subscribeToResult (/Users/philippediep/Documents/workspace/algoan/examples/example-manager/node_modules/rxjs/internal/util/subscribeToResult.js:12:23)
at MergeMapSubscriber._innerSub (/Users/philippediep/Documents/workspace/algoan/examples/example-manager/node_modules/rxjs/internal/operators/mergeMap.js:82:53)
at MergeMapSubscriber._tryNext (/Users/philippediep/Documents/workspace/algoan/examples/example-manager/node_modules/rxjs/internal/operators/mergeMap.js:76:14)
化妆
默认情况下,整个请求和回复的正文会被登录. 然而,有效载荷可能包含您想隐藏在日志中的敏感数据. 这个 Log 装饰器提供遮盖选项,可以覆盖每个端点所记录的默认属性. 您只需要提供属性的路径来掩盖请求和/或响应. 相应的值将被占位符所取代 **** 在结果的日志中.
例如:
import { Log } from '@algoan/nestjs-logging-interceptor'
import { Body, Controller } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Post()
@Log({
mask: {
request: [
'lastName', // simple property
'maritalSituation', // object property
'contact.phone', // nested property
'friends.lastName' // property of an array element
],
response: ['lastName'],
},
})
public createUser(@Body() payload: UserDto) {
// create and return the new user
}
}
如果终点被调用为:
{
"firstName": "John",
"lastName": "Doe",
"maritalSituation": {
"status": "MARRIED",
"wife": { "firstName": "Jane", "lastName": "Doe" }
},
"contact": { "email": "[email protected]", "phone": "+330102030405" },
"friends": [
{ "firstName": "James", "lastName": "Smith" },
{ "firstName": "Emma", "lastName": "Cole" }
]
}
登录请求中的机构为:
{
"firstName": "John",
"lastName": "****",
"maritalSituation": "****",
"contact": { "email": "[email protected]", "phone": "****" },
"friends": [
{ "firstName": "James", "lastName": "****" },
{ "firstName": "Emma", "lastName": "****" }
]
}
如果你想掩盖请求/答复的整个内容,你可以通过 true 而不是包含详尽属性列表的数组.
此外,还可以在拦截器级别设置其他选项:
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { LoggingInterceptor } from '@algoan/nestjs-logging-interceptor';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useFactory: () =>
new LoggingInterceptor({
disableMasking: true, // Ignore masking options in the entire applications
maskingPlaceholder: 'hidden', // Replace the default placeholder '****' by a custom one
mask: {
requestHeader: {
password: true, // Mask the header 'password' in the request
authorization: (header: string | string[]) => {
... // Handle the header value to keep non sensitive data for instance
}
},
},
}),
},
],
})
export class CoreModule {}
截断
默认情况下,整个请求和回复的正文会被登录. 然而,您可能用来存储和访问您的日志的监测平台,其日志大小限制会低于您的身体要求. 这个 Log 装饰器提供了可为每个端点截取默认截取选项的截取选项. 你只需要提供极限。 身体记录将在限定时间被截取,或者如果您提供了 truncate 函数.
例如:
import { Log } from '@algoan/nestjs-logging-interceptor'
import { Body, Controller } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Post()
@Log({
truncation: {
limit: 10000,
truncate: (body) => ({
...body,
friends: 'TRUNCATED'
}),
}
})
public createUser(@Body() payload: UserDto) {
// create and return the new user
}
}
如果终点被调用为:
{
"firstName": "John",
"lastName": "Doe",
"maritalSituation": {
"status": "MARRIED",
"wife": { "firstName": "Jane", "lastName": "Doe" }
},
"contact": { "email": "[email protected]", "phone": "+330102030405" },
"friends": [
{ "firstName": "James", "lastName": "Smith" },
{ "firstName": "Emma", "lastName": "Cole" }
// ... thousands of other friends
]
}
登录请求中的机构为:
{
"firstName": "John",
"lastName": "Doe",
"maritalSituation": {
"status": "MARRIED",
"wife": { "firstName": "Jane", "lastName": "Doe" }
},
"contact": { "email": "[email protected]", "phone": "+330102030405" },
"friends": "TRUNCATED"
}
如果没有截取选项,它将被转换为字符串并切入极限.
该选项也可以同时适用于所有路线:
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { LoggingInterceptor } from '@algoan/nestjs-logging-interceptor';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useFactory: () =>
new LoggingInterceptor({
truncation: {
limit: 10000
}
}),
},
],
})
export class CoreModule {}
使用自定义锁定器
鸟巢
在这个例子中,我们将用一个 Pino 日志来覆盖默认的 Logger 执行(参见 此 NestJS 的官方文档(中文(简体) )
import { Module } from '@nestjs/common';
import { CoreModule } from './core/core.module'; // the module importing the @algoan/nestjs-logging-interceptor
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerModule } from 'nestjs-pino';
@Module({
imports: [LoggerModule.forRoot(), CoreModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
然后在应用的靴子,设置 皮诺伐木机 以代替默认的Logger.
import { NestFactory} from '@nestjs/core';
import { Logger } from "nestjs-pino";
import { MainModule } from './main.module';
async function bootstrap() {
const app = await NestFactory.create(MainModule,{
logger: false
});
app.useLogger(app.get(Logger))
await app.listen(3000);
}
bootstrap();
Pino 日志将被设定为 Logger 执行程序.
# With default Logger
[Nest] 84392 - 04/03/2020, 2:02:04 PM [RoutesResolver] AppController {}: +7ms
[Nest] 84392 - 04/03/2020, 2:02:04 PM [RouterExplorer] Mapped {, GET} route +3ms
[Nest] 84392 - 04/03/2020, 2:02:04 PM [RouterExplorer] Mapped {/badrequest, GET} route +0ms
[Nest] 84392 - 04/03/2020, 2:02:04 PM [RouterExplorer] Mapped {/error, GET} route +0ms
[Nest] 84392 - 04/03/2020, 2:02:04 PM [NestApplication] Nest application successfully started +3ms
# With Pino Logger
{"level":30,"time":1585915251917,"pid":83826,"hostname":"computername.local","context":"RoutesResolver","msg":"AppController {}: true","v":1}
{"level":30,"time":1585915251919,"pid":83826,"hostname":"computername.local","context":"RouterExplorer","msg":"Mapped {, GET} route true","v":1}
{"level":30,"time":1585915251919,"pid":83826,"hostname":"computername.local","context":"RouterExplorer","msg":"Mapped {/badrequest, GET} route true","v":1}
{"level":30,"time":1585915251919,"pid":83826,"hostname":"computername.local","context":"RouterExplorer","msg":"Mapped {/error, GET} route true","v":1}
{"level":30,"time":1585915251921,"pid":83826,"hostname":"computername.local","context":"NestApplication","msg":"Nest application successfully started true","v":1}