Skip to content

Commit 72dd2cb

Browse files
authored
Merge pull request #24 from OlufunbiIK/feat/Add-Comprehensive-Logging-with-Structured-Format-#10
Feat/add comprehensive logging with structured format #10
2 parents 326b811 + 806920c commit 72dd2cb

6 files changed

Lines changed: 145 additions & 12 deletions

File tree

package-lock.json

Lines changed: 60 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"bull": "^4.12.2",
5858
"class-transformer": "^0.5.1",
5959
"class-validator": "^0.14.1",
60+
"cls-hooked": "^4.2.2",
6061
"compression": "^1.7.4",
6162
"cors": "^2.8.5",
6263
"crypto-js": "^4.2.0",
@@ -85,7 +86,7 @@
8586
"sharp": "^0.33.1",
8687
"uuid": "^9.0.1",
8788
"web3": "^4.3.0",
88-
"winston": "^3.11.0",
89+
"winston": "^3.19.0",
8990
"winston-daily-rotate-file": "^4.7.1",
9091
"xlsx": "^0.18.5"
9192
},
@@ -100,7 +101,7 @@
100101
"@types/cors": "^2.8.17",
101102
"@types/crypto-js": "^4.2.1",
102103
"@types/express": "^4.17.21",
103-
"@types/jest": "^29.5.8",
104+
"@types/jest": "^29.5.14",
104105
"@types/jsonwebtoken": "^9.0.5",
105106
"@types/lodash": "^4.14.202",
106107
"@types/multer": "^1.4.11",
@@ -121,7 +122,7 @@
121122
"rimraf": "^5.0.5",
122123
"source-map-support": "^0.5.21",
123124
"supertest": "^6.3.3",
124-
"ts-jest": "^29.1.1",
125+
"ts-jest": "^29.4.6",
125126
"ts-loader": "^9.5.1",
126127
"ts-node": "^10.9.1",
127128
"tsconfig-paths": "^4.2.0",
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { createNamespace } from 'cls-hooked';
2+
3+
export const CORRELATION_ID_NAMESPACE = 'app';
4+
export const CORRELATION_ID_KEY = 'correlationId';
5+
6+
export const correlationNamespace = createNamespace(CORRELATION_ID_NAMESPACE);
7+
8+
export const getCorrelationId = (): string => {
9+
return correlationNamespace.get(CORRELATION_ID_KEY);
10+
};

src/common/logger/logger.module.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { LoggerService } from './logger.service';
44
import { ConfigService } from '@nestjs/config';
55
import * as winston from 'winston';
66
import * as DailyRotateFile from 'winston-daily-rotate-file';
7+
import { LoggingInterceptor } from './logging.interceptor';
78

89
@Global()
910
@Module({
@@ -24,10 +25,7 @@ import * as DailyRotateFile from 'winston-daily-rotate-file';
2425
transports: [
2526
// Console transport for development
2627
new winston.transports.Console({
27-
format: winston.format.combine(
28-
winston.format.colorize(),
29-
winston.format.simple(),
30-
),
28+
format: winston.format.combine(winston.format.colorize(), winston.format.simple()),
3129
}),
3230

3331
// File transport for errors
@@ -60,7 +58,7 @@ import * as DailyRotateFile from 'winston-daily-rotate-file';
6058
}),
6159
}),
6260
],
63-
providers: [LoggerService],
64-
exports: [LoggerService, WinstonModule],
61+
providers: [LoggerService, LoggingInterceptor],
62+
exports: [LoggerService, WinstonModule, LoggingInterceptor],
6563
})
6664
export class LoggerModule {}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
2+
import { Observable, tap } from 'rxjs';
3+
import { getCorrelationId } from './correlation-id';
4+
import { LoggerService } from './logger.service';
5+
6+
@Injectable()
7+
export class LoggingInterceptor implements NestInterceptor {
8+
constructor(private readonly logger: LoggerService) {} // ✅ inject LoggerService
9+
10+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
11+
const req = context.switchToHttp().getRequest();
12+
const correlationId = getCorrelationId();
13+
14+
this.logger.log(
15+
{
16+
message: 'Handling request',
17+
method: req.method,
18+
url: req.url,
19+
correlationId,
20+
},
21+
this.logger['context'], // optional context
22+
);
23+
24+
const now = Date.now();
25+
return next.handle().pipe(
26+
tap(() => {
27+
const res = context.switchToHttp().getResponse();
28+
this.logger.log(
29+
{
30+
message: 'Handling request',
31+
method: req.method,
32+
url: req.url,
33+
correlationId,
34+
},
35+
this.logger['context'], // optional context
36+
);
37+
}),
38+
);
39+
}
40+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Injectable, NestMiddleware } from '@nestjs/common';
2+
import { Request, Response, NextFunction } from 'express';
3+
import { v4 as uuidv4 } from 'uuid';
4+
import { correlationNamespace, CORRELATION_ID_KEY, getCorrelationId } from './correlation-id';
5+
import { LoggerService } from './logger.service';
6+
7+
@Injectable()
8+
export class LoggingMiddleware implements NestMiddleware {
9+
constructor(private readonly logger: LoggerService) {}
10+
11+
use(req: Request, res: Response, next: NextFunction) {
12+
const correlationId = req.headers['x-correlation-id'] || uuidv4();
13+
14+
correlationNamespace.run(() => {
15+
correlationNamespace.set(CORRELATION_ID_KEY, correlationId);
16+
17+
this.logger.logRequest(req.method, req.url, undefined, req.ip);
18+
19+
const start = Date.now();
20+
res.on('finish', () => {
21+
this.logger.logResponse(req.method, req.url, res.statusCode, Date.now() - start);
22+
});
23+
24+
next();
25+
});
26+
}
27+
}

0 commit comments

Comments
 (0)