feat: log request body (masked, debug level) and drop headers from request logs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2c4f3008cf
commit
c680fb9dc7
@ -6,6 +6,7 @@ import { AuthModule } from './auth/auth.module.js';
|
|||||||
import { JwtAuthGuard } from './auth/jwt-auth.guard.js';
|
import { JwtAuthGuard } from './auth/jwt-auth.guard.js';
|
||||||
import { PermissionsGuard } from './auth/permissions.guard.js';
|
import { PermissionsGuard } from './auth/permissions.guard.js';
|
||||||
import { RequestContextInterceptor } from './auth/request-context.interceptor.js';
|
import { RequestContextInterceptor } from './auth/request-context.interceptor.js';
|
||||||
|
import { BodyLoggingInterceptor } from './logging/body-logging.interceptor.js';
|
||||||
import { TransformInterceptor } from './common/interceptors/transform.interceptor.js';
|
import { TransformInterceptor } from './common/interceptors/transform.interceptor.js';
|
||||||
import { AllExceptionsFilter } from './common/filters/http-exception.filter.js';
|
import { AllExceptionsFilter } from './common/filters/http-exception.filter.js';
|
||||||
import { TenantsModule } from './tenants/tenants.module.js';
|
import { TenantsModule } from './tenants/tenants.module.js';
|
||||||
@ -41,6 +42,7 @@ import { AppService } from './app.service.js';
|
|||||||
{ provide: APP_GUARD, useClass: JwtAuthGuard },
|
{ provide: APP_GUARD, useClass: JwtAuthGuard },
|
||||||
{ provide: APP_GUARD, useClass: PermissionsGuard },
|
{ provide: APP_GUARD, useClass: PermissionsGuard },
|
||||||
{ provide: APP_INTERCEPTOR, useClass: RequestContextInterceptor },
|
{ provide: APP_INTERCEPTOR, useClass: RequestContextInterceptor },
|
||||||
|
{ provide: APP_INTERCEPTOR, useClass: BodyLoggingInterceptor },
|
||||||
{ provide: APP_INTERCEPTOR, useClass: TransformInterceptor },
|
{ provide: APP_INTERCEPTOR, useClass: TransformInterceptor },
|
||||||
{ provide: APP_FILTER, useClass: AllExceptionsFilter },
|
{ provide: APP_FILTER, useClass: AllExceptionsFilter },
|
||||||
],
|
],
|
||||||
|
|||||||
44
src/logging/body-logging.interceptor.ts
Normal file
44
src/logging/body-logging.interceptor.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
|
||||||
|
|
||||||
|
const SENSITIVE_KEYS = new Set([
|
||||||
|
'password',
|
||||||
|
'passwordhash',
|
||||||
|
'oldpassword',
|
||||||
|
'newpassword',
|
||||||
|
'confirmpassword',
|
||||||
|
'refreshtoken',
|
||||||
|
'token',
|
||||||
|
'secret',
|
||||||
|
'authorization',
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** 递归脱敏:命中的字段值替换为 ***。 */
|
||||||
|
function mask(value: unknown): unknown {
|
||||||
|
if (value === null || value === undefined) return value;
|
||||||
|
if (Array.isArray(value)) return value.map(mask);
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
const out: Record<string, unknown> = {};
|
||||||
|
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
||||||
|
out[k] = SENSITIVE_KEYS.has(k.toLowerCase()) ? '***' : mask(v);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 记录请求 body(POST/PUT/PATCH/DELETE),敏感字段脱敏,级别 debug。 */
|
||||||
|
@Injectable()
|
||||||
|
export class BodyLoggingInterceptor implements NestInterceptor {
|
||||||
|
constructor(@InjectPinoLogger(BodyLoggingInterceptor.name) private readonly logger: PinoLogger) {}
|
||||||
|
|
||||||
|
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
|
||||||
|
const request = context.switchToHttp().getRequest();
|
||||||
|
const { method, url, body } = request;
|
||||||
|
if (body && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method) && Object.keys(body).length > 0) {
|
||||||
|
this.logger.debug({ method, url, body: mask(body) }, 'request body');
|
||||||
|
}
|
||||||
|
return next.handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -47,6 +47,18 @@ export function createLoggerModule(): DynamicModule {
|
|||||||
pinoHttp: {
|
pinoHttp: {
|
||||||
level,
|
level,
|
||||||
transport: { targets },
|
transport: { targets },
|
||||||
|
serializers: {
|
||||||
|
req: (req: any) => ({
|
||||||
|
id: req.id,
|
||||||
|
method: req.method,
|
||||||
|
url: req.url,
|
||||||
|
query: req.query,
|
||||||
|
params: req.params,
|
||||||
|
remoteAddress: req.remoteAddress,
|
||||||
|
remotePort: req.remotePort,
|
||||||
|
}),
|
||||||
|
res: (res: any) => ({ statusCode: res.statusCode }),
|
||||||
|
},
|
||||||
mixin() {
|
mixin() {
|
||||||
const src = getCaller();
|
const src = getCaller();
|
||||||
return src ? { src } : {};
|
return src ? { src } : {};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user