feat: add logging

This commit is contained in:
2026-08-14 17:12:15 +03:00
parent 34c2b79e27
commit 25d7c5780b
25 changed files with 753 additions and 108 deletions
@@ -0,0 +1,67 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import type { NextFunction, Request, Response } from 'express';
import { randomUUID } from 'node:crypto';
import { RequestContextService } from './request-context.service';
import { StructuredLogger } from './structured-logger.service';
const uuid =
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
@Injectable()
export class RequestLoggingMiddleware implements NestMiddleware {
constructor(
private readonly requestContext: RequestContextService,
private readonly logger: StructuredLogger,
) {}
use(request: Request, response: Response, next: NextFunction): void {
const incoming = request.header('x-request-id');
const clientRequestId =
incoming && uuid.test(incoming) ? incoming : undefined;
const existingRequestId = (request as Request & { id?: unknown }).id;
const requestId =
typeof existingRequestId === 'string' ? existingRequestId : randomUUID();
(request as unknown as { id: string }).id = requestId;
const startedAt = performance.now();
response.setHeader('X-Request-ID', requestId);
response.on('finish', () => {
const routeRequest: unknown = request;
const routePath = (routeRequest as { route?: { path?: unknown } }).route
?.path;
const route =
typeof routePath === 'string'
? `${request.baseUrl}${routePath}`
: '<unmatched>';
const context = {
method: request.method,
route,
status: response.statusCode,
durationMs: Math.round(performance.now() - startedAt),
...(response.locals.publicErrorCode
? { code: response.locals.publicErrorCode as string }
: {}),
...(response.locals.errorStack
? { stack: response.locals.errorStack as string }
: {}),
};
const level =
response.statusCode >= 500
? 'error'
: response.statusCode === 429 ||
response.statusCode === 401 ||
response.statusCode === 403
? 'warn'
: 'info';
this.logger.event(level, 'http.request.completed', context);
});
this.requestContext.run(
{
requestId,
...(clientRequestId ? { clientRequestId } : {}),
},
next,
);
}
}