feat(server): enable cls plugin to store request id (#9758)

POC
This commit is contained in:
fengmk2
2025-01-20 10:00:21 +00:00
parent fa5e6e1f45
commit 2ae05c28b7
7 changed files with 70 additions and 4 deletions

View File

@@ -1,3 +1,5 @@
import { randomUUID } from 'node:crypto';
import {
DynamicModule,
ForwardReference,
@@ -6,6 +8,7 @@ import {
} from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';
import { get } from 'lodash-es';
import { ClsModule } from 'nestjs-cls';
import { AppController } from './app.controller';
import { getOptionalModuleMetadata } from './base';
@@ -42,6 +45,17 @@ import { REGISTERED_PLUGINS } from './plugins';
import { ENABLED_PLUGINS } from './plugins/registry';
export const FunctionalityModules = [
ClsModule.forRoot({
global: true,
middleware: {
mount: true,
generateId: true,
idGenerator() {
// make every request has a unique id to tracing
return randomUUID();
},
},
}),
ConfigModule.forRoot(),
RuntimeModule,
EventModule,

View File

@@ -12,6 +12,7 @@ import {
import { SocketIoAdapter } from './base/websocket';
import { AuthGuard } from './core/auth';
import { ENABLED_FEATURES } from './core/config/server-feature';
import { responseRequestIdHeader } from './middleware/request-id';
import { serverTimingAndCache } from './middleware/timing';
export async function createApp() {
@@ -31,6 +32,7 @@ export async function createApp() {
}
app.use(serverTimingAndCache);
app.use(responseRequestIdHeader);
app.use(
graphqlUploadExpress({

View File

@@ -1,6 +1,20 @@
import { ConsoleLogger, Injectable } from '@nestjs/common';
import { ConsoleLogger, Injectable, type LogLevel } from '@nestjs/common';
import { ClsServiceManager } from 'nestjs-cls';
// DO NOT use this Logger directly
// Use it via this way: `private readonly logger = new Logger(MyService.name)`
@Injectable()
export class AFFiNELogger extends ConsoleLogger {}
export class AFFiNELogger extends ConsoleLogger {
override stringifyMessage(message: unknown, logLevel: LogLevel) {
const messageString = super.stringifyMessage(message, logLevel);
const requestId = AFFiNELogger.getRequestId();
if (!requestId) {
return messageString;
}
return `<${requestId}> ${messageString}`;
}
static getRequestId(): string | undefined {
return ClsServiceManager.getClsService()?.getId();
}
}

View File

@@ -0,0 +1,16 @@
import { NextFunction, Request, Response } from 'express';
import { ClsServiceManager } from 'nestjs-cls';
import onHeaders from 'on-headers';
export const responseRequestIdHeader = (
_req: Request,
res: Response,
next: NextFunction
) => {
onHeaders(res, () => {
const requestId = ClsServiceManager.getClsService().getId();
res.setHeader('X-Request-Id', requestId);
});
next();
};

View File

@@ -1,22 +1,28 @@
import { LoggingWinston } from '@google-cloud/logging-winston';
import { LoggerService, Provider } from '@nestjs/common';
import { createLogger, transports } from 'winston';
import { createLogger, format, transports } from 'winston';
import { AFFiNELogger as LoggerProvide } from '../../../base/logger';
import { AFFiNELogger } from './logger';
const moreMetadata = format(info => {
info.requestId = LoggerProvide.getRequestId();
return info;
});
export const loggerProvider: Provider<LoggerService> = {
provide: LoggerProvide,
useFactory: () => {
const loggingWinston = new LoggingWinston();
// Create a Winston logger that streams to Cloud Logging
const instance = createLogger({
level: 'log',
level: 'info',
transports: [
new transports.Console(),
// Add Cloud Logging
loggingWinston,
],
format: format.combine(moreMetadata(), format.json()),
});
return new AFFiNELogger(instance);
},