Skip to content

Commit d8bb612

Browse files
committed
feat(logging): scaffold centralized logging and observability foundation
1 parent e7335ab commit d8bb612

3 files changed

Lines changed: 140 additions & 0 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { utilities as nestWinstonModuleUtilities } from 'nest-winston';
2+
import { format, transports } from 'winston';
3+
4+
export const loggerConfig = {
5+
level: process.env.LOG_LEVEL ?? 'info',
6+
7+
transports: [
8+
new transports.Console({
9+
format: format.combine(
10+
format.timestamp(),
11+
format.errors({ stack: true }),
12+
format.ms(),
13+
nestWinstonModuleUtilities.format.nestLike(
14+
process.env.APP_NAME ?? 'QuestService',
15+
{
16+
prettyPrint: true,
17+
colors: process.env.NODE_ENV !== 'production',
18+
},
19+
),
20+
),
21+
}),
22+
],
23+
};

src/logging/logging.controller.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Controller, Get } from '@nestjs/common';
2+
3+
import { LoggingService } from './logging.service';
4+
5+
@Controller('logs')
6+
export class LoggingController {
7+
constructor(
8+
private readonly loggingService: LoggingService,
9+
) {}
10+
11+
@Get('health')
12+
health() {
13+
return this.loggingService.health();
14+
}
15+
16+
@Get('levels')
17+
levels() {
18+
return {
19+
supported: [
20+
'error',
21+
'warn',
22+
'info',
23+
'debug',
24+
'verbose',
25+
],
26+
active:
27+
process.env.LOG_LEVEL ?? 'info',
28+
};
29+
}
30+
}

src/logging/logging.service.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { Injectable, LoggerService } from '@nestjs/common';
2+
import { createLogger, Logger } from 'winston';
3+
4+
import { loggerConfig } from './config/logger.config';
5+
6+
@Injectable()
7+
export class LoggingService implements LoggerService {
8+
private readonly logger: Logger = createLogger(loggerConfig);
9+
10+
log(message: unknown, context?: string): void {
11+
this.logger.info(message, { context });
12+
}
13+
14+
error(
15+
message: unknown,
16+
trace?: string,
17+
context?: string,
18+
): void {
19+
this.logger.error(message, {
20+
trace,
21+
context,
22+
});
23+
}
24+
25+
warn(message: unknown, context?: string): void {
26+
this.logger.warn(message, { context });
27+
}
28+
29+
debug(message: unknown, context?: string): void {
30+
this.logger.debug(message, { context });
31+
}
32+
33+
verbose(
34+
message: unknown,
35+
context?: string,
36+
): void {
37+
this.logger.verbose(message, { context });
38+
}
39+
40+
metric(
41+
name: string,
42+
value: number,
43+
metadata?: Record<string, unknown>,
44+
) {
45+
this.logger.info({
46+
type: 'metric',
47+
name,
48+
value,
49+
metadata,
50+
timestamp: new Date().toISOString(),
51+
});
52+
}
53+
54+
audit(
55+
action: string,
56+
metadata?: Record<string, unknown>,
57+
) {
58+
this.logger.info({
59+
type: 'audit',
60+
action,
61+
metadata,
62+
timestamp: new Date().toISOString(),
63+
});
64+
}
65+
66+
exception(
67+
error: Error,
68+
metadata?: Record<string, unknown>,
69+
) {
70+
this.logger.error({
71+
type: 'exception',
72+
name: error.name,
73+
message: error.message,
74+
stack: error.stack,
75+
metadata,
76+
timestamp: new Date().toISOString(),
77+
});
78+
}
79+
80+
health() {
81+
return {
82+
logger: 'healthy',
83+
level: process.env.LOG_LEVEL ?? 'info',
84+
timestamp: new Date().toISOString(),
85+
};
86+
}
87+
}

0 commit comments

Comments
 (0)