Skip to content

Commit 6630df7

Browse files
authored
New Logging (#53)
2 parents 4c60bfb + e1a3394 commit 6630df7

3 files changed

Lines changed: 119 additions & 9 deletions

File tree

src/app.module.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ServeStaticModule } from "@nestjs/serve-static";
55
import { AuthModule as BetterAuthModule } from "@thallesp/nestjs-better-auth";
66
import { AppController } from "./app.controller";
77
import { AppService } from "./app.service";
8+
import { logger } from "./common/middlewares/Logger.middleware";
89
import { config } from "./config/app.config";
910
import { AuthModule } from "./core/auth/auth.module";
1011
import { EmailModule } from "./core/email/email.module";
@@ -86,13 +87,7 @@ import { UtilModule } from "./modules/util/util.module";
8687
providers: [AppService, PrismaService, LoggerService],
8788
})
8889
export class AppModule {
89-
// configure(consumer: MiddlewareConsumer) {
90-
// consumer
91-
// .apply(StudentAuthMiddleware)
92-
// .exclude(
93-
// { path: "/student/user/auth", method: RequestMethod.GET },
94-
// { path: "/student/user/auth/callback", method: RequestMethod.GET },
95-
// )
96-
// .forRoutes("*");
97-
// }
90+
configure(consumer: MiddlewareConsumer) {
91+
consumer.apply(logger).forRoutes("*");
92+
}
9893
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { NextFunction, Request, Response } from "express";
2+
import { DiscordWebhook } from "src/utils/discord-webhook";
3+
4+
const discordWebhook = new DiscordWebhook();
5+
6+
export function logger(req: Request, res: Response, next: NextFunction) {
7+
const startTime = Date.now();
8+
let responseBody: any = null;
9+
10+
// Capture original methods
11+
const originalJson = res.json.bind(res);
12+
const originalSend = res.send.bind(res);
13+
const originalEnd = res.end;
14+
15+
// Override json to capture response body
16+
res.json = (body: any) => {
17+
responseBody = body;
18+
return originalJson(body);
19+
};
20+
21+
// Override send to capture response body
22+
res.send = (body: any) => {
23+
if (typeof body === "string") {
24+
try {
25+
responseBody = JSON.parse(body);
26+
} catch {
27+
responseBody = body;
28+
}
29+
} else {
30+
responseBody = body;
31+
}
32+
return originalSend(body);
33+
};
34+
35+
// Override end to send log
36+
res.end = function (chunk?: any, encoding?: any, callback?: any) {
37+
const responseTime = Date.now() - startTime;
38+
39+
// Get user session (attached by better-auth middleware)
40+
const userSession = (req as any).session || null;
41+
42+
// Send to Discord webhook
43+
discordWebhook.send(
44+
discordWebhook.requestEmbed({
45+
method: req.method,
46+
url: req.originalUrl || req.url,
47+
ip: req.ip || req.headers["x-forwarded-for"]?.toString() || "Unknown",
48+
userAgent: req.headers["user-agent"] || "Unknown",
49+
body: req.body,
50+
query: req.query,
51+
headers: req.headers,
52+
userSession,
53+
statusCode: res.statusCode,
54+
responseTime,
55+
responseBody,
56+
}),
57+
);
58+
59+
// Call original end
60+
return originalEnd.call(this, chunk, encoding, callback);
61+
} as typeof res.end;
62+
63+
next();
64+
}

src/utils/discord-webhook.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,55 @@ export class DiscordWebhook {
5959
},
6060
};
6161
}
62+
63+
requestEmbed(data: {
64+
method: string;
65+
url: string;
66+
ip: string;
67+
userAgent: string;
68+
body?: any;
69+
query?: any;
70+
headers?: Record<string, string | string[] | undefined>;
71+
userSession?: any;
72+
statusCode: number;
73+
responseTime: number;
74+
responseBody?: any;
75+
}): EmbedMessage {
76+
const statusColor = data.statusCode >= 400 ? 16711680 : data.statusCode >= 300 ? 16776960 : 65280;
77+
const bodyStr = data.body && Object.keys(data.body).length > 0 ? JSON.stringify(data.body, null, 2).slice(0, 500) : "No body";
78+
const queryStr = data.query && Object.keys(data.query).length > 0 ? JSON.stringify(data.query, null, 2).slice(0, 300) : "No query";
79+
const responseStr = data.responseBody ? JSON.stringify(data.responseBody, null, 2).slice(0, 800) : "No response body";
80+
81+
// Filter sensitive headers
82+
const safeHeaders = data.headers
83+
? Object.fromEntries(
84+
Object.entries(data.headers)
85+
.filter(([key]) => !["authorization", "cookie", "set-cookie"].includes(key.toLowerCase()))
86+
.slice(0, 10),
87+
)
88+
: {};
89+
const headersStr = Object.keys(safeHeaders).length > 0 ? JSON.stringify(safeHeaders, null, 2).slice(0, 400) : "No headers";
90+
91+
// User session as JSON
92+
const sessionStr = data.userSession ? JSON.stringify(data.userSession, null, 2).slice(0, 800) : "Not authenticated";
93+
94+
return {
95+
title: `${data.method} ${data.url}`,
96+
description: [
97+
`**Status:** ${data.statusCode}`,
98+
`**Response Time:** ${data.responseTime}ms`,
99+
`**IP:** ${data.ip}`,
100+
`**User-Agent:** ${data.userAgent?.slice(0, 100) || "Unknown"}`,
101+
`**User Session:** \`\`\`json\n${sessionStr}\`\`\``,
102+
`**Headers:** \`\`\`json\n${headersStr}\`\`\``,
103+
`**Query:** \`\`\`json\n${queryStr}\`\`\``,
104+
`**Request Body:** \`\`\`json\n${bodyStr}\`\`\``,
105+
`**Response Body:** \`\`\`json\n${responseStr}\`\`\``,
106+
].join("\n"),
107+
color: statusColor,
108+
footer: {
109+
text: "Backend Service | ComCamp 37",
110+
},
111+
};
112+
}
62113
}

0 commit comments

Comments
 (0)