-
-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(be): request logging #63
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
-- CreateTable | ||
CREATE TABLE "RequestLog" ( | ||
"id" BIGSERIAL NOT NULL, | ||
"method" TEXT NOT NULL, | ||
"path" TEXT NOT NULL, | ||
"status" INTEGER NOT NULL, | ||
"duration" INTEGER NOT NULL, | ||
"response" TEXT, | ||
"userAgent" TEXT, | ||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
|
||
CONSTRAINT "RequestLog_pkey" PRIMARY KEY ("id") | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { Injectable, NestMiddleware } from "@nestjs/common"; | ||
import { NextFunction, Request, Response } from "express"; | ||
|
||
import { PrismaService } from "src/modules/prisma/prisma.service"; | ||
|
||
@Injectable() | ||
export class RequestLoggerMiddleware implements NestMiddleware { | ||
constructor(private readonly prisma: PrismaService) {} | ||
|
||
async use(req: Request, res: Response, next: NextFunction) { | ||
const start = Date.now(); | ||
|
||
// Intercept the response to capture the body | ||
const originalSend = res.send; | ||
let responseBody: unknown; | ||
|
||
res.send = (body): Response => { | ||
responseBody = body; // Capture the response body | ||
return originalSend.call(res, body); // Call the original `send` method | ||
}; | ||
|
||
// Attach an event listener to log after the response is sent | ||
res.on("finish", async () => { | ||
const duration = Date.now() - start; | ||
const { | ||
method, | ||
url: path, | ||
headers: { "user-agent": userAgent = null }, | ||
} = req; | ||
const { statusCode } = res; | ||
const responseString = | ||
typeof responseBody === "string" | ||
? responseBody | ||
: JSON.stringify(responseBody); | ||
|
||
const ignoreResponse = ["/v1/stop/all", "/v1/platform/"].some( | ||
(item) => path.startsWith(item), | ||
); | ||
|
||
if (path.startsWith("/status")) { | ||
return; | ||
} | ||
|
||
try { | ||
// Log the request details to the database | ||
await this.prisma.requestLog.create({ | ||
data: { | ||
method, | ||
path, | ||
status: statusCode, | ||
duration, | ||
userAgent, | ||
response: ignoreResponse ? null : responseString, | ||
}, | ||
}); | ||
} catch (error) { | ||
console.error("Failed to log request:", error); | ||
} | ||
}); | ||
Comment on lines
+23
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential PII leakage from logging full response bodies Storing the complete response body in the database may inadvertently capture sensitive or personally identifiable information (PII), leading to security and compliance risks. It's important to ensure that sensitive data is not logged. Recommendations:
|
||
|
||
next(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,6 @@ import { | |
departureSchema, | ||
type DepartureSchema, | ||
} from "src/modules/departure/schema/departure.schema"; | ||
import { LogInterceptor } from "src/modules/logger/log.interceptor"; | ||
import { | ||
metroOnlySchema, | ||
vehicleTypeSchema, | ||
|
@@ -28,7 +27,7 @@ import { metroOnlyQuery } from "src/swagger/query.swagger"; | |
|
||
@ApiTags("departure") | ||
@Controller("departure") | ||
@UseInterceptors(CacheInterceptor, LogInterceptor) | ||
@UseInterceptors(CacheInterceptor) | ||
@CacheTTL(4 * 1000) | ||
Comment on lines
+30
to
31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Logging implementation for departure endpoints needs enhancement The RequestLoggerMiddleware implementation has some limitations that should be addressed for departure endpoints:
Required changes:
🔗 Analysis chainVerify logging coverage for departure endpoints with complex queries The removal of LogInterceptor is consistent with the logging refactor. However, these departure endpoints handle complex query parameters and potentially large response payloads. We should ensure that the new RequestLoggerMiddleware:
🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Verify logging configuration for departure endpoints
# Check if the middleware implements response size limits
rg -A 5 "class RequestLoggerMiddleware" | rg "response.*body|maxSize|truncate"
# Check if query parameter logging is implemented
ast-grep --pattern 'class RequestLoggerMiddleware {
$$$
log($$$) {
$$$
query: $$$
$$$
}
}'
# Check for cache-aware logging configuration
rg "CacheInterceptor.*RequestLoggerMiddleware|RequestLoggerMiddleware.*CacheInterceptor" -A 5
Length of output: 287 Script: #!/bin/bash
# Let's try a different approach to find the middleware implementation
# Find the middleware file first
fd "request.*logger.*middleware" --type f
# Search for logging-related patterns in the codebase
rg -g "!{test,spec}*" "implements.*NestMiddleware|extends.*NestMiddleware" -A 5
# Look for request/response logging patterns
rg -g "!{test,spec}*" "class.*Logger|class.*Log" -A 10
# Check for query parameter handling in the codebase
rg -g "!{test,spec}*" "req\.query|request\.query" -A 3
Length of output: 5062 Script: #!/bin/bash
# Let's examine the request logger middleware implementation in detail
# Check the complete implementation of request logger middleware
cat apps/backend/src/middleware/request-logger-middleware.ts
# Look for request log schema to understand what's being logged
fd "prisma.schema" --type f --exec cat {}
# Check for any configuration related to response size limits
fd "config|constants" --type f | rg -i "log|size|limit" --files-with-matches | xargs cat
Length of output: 2297 |
||
export class DepartureController { | ||
constructor( | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Controller, OnModuleInit } from "@nestjs/common"; | ||
import { Cron, CronExpression } from "@nestjs/schedule"; | ||
|
||
import { LogsCleanupService } from "src/modules/logs-cleanup/logs-cleanup.service"; | ||
|
||
@Controller("logs-cleanup") | ||
export class LogsCleanupController implements OnModuleInit { | ||
constructor(private readonly logsCleanupService: LogsCleanupService) {} | ||
|
||
async onModuleInit(): Promise<void> { | ||
return this.logsCleanupService.cleanupLogs(); | ||
} | ||
|
||
@Cron(CronExpression.EVERY_10_MINUTES) | ||
async cronLogsCleanup(): Promise<void> { | ||
return this.logsCleanupService.cleanupLogs(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Module } from "@nestjs/common"; | ||
|
||
import { LogsCleanupController } from "src/modules/logs-cleanup/logs-cleanup.controller"; | ||
import { LogsCleanupService } from "src/modules/logs-cleanup/logs-cleanup.service"; | ||
|
||
@Module({ | ||
controllers: [LogsCleanupController], | ||
providers: [LogsCleanupService], | ||
imports: [], | ||
}) | ||
export class LogsCleanupModule {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { Injectable } from "@nestjs/common"; | ||
|
||
import { LogLevel, LogMessage } from "src/enums/log.enum"; | ||
import { LoggerService } from "src/modules/logger/logger.service"; | ||
import { PrismaService } from "src/modules/prisma/prisma.service"; | ||
|
||
const MAX_COUNT = 500_000; | ||
|
||
@Injectable() | ||
export class LogsCleanupService { | ||
constructor( | ||
private readonly prisma: PrismaService, | ||
private readonly logger: LoggerService, | ||
) {} | ||
|
||
async cleanupLogs(): Promise<void> { | ||
try { | ||
const recordCount = await this.prisma.requestLog.count(); | ||
|
||
if (recordCount < MAX_COUNT) { | ||
return; | ||
} | ||
|
||
const last = await this.prisma.requestLog.findFirst({ | ||
orderBy: { createdAt: "desc" }, | ||
skip: MAX_COUNT, | ||
}); | ||
|
||
if (!last) { | ||
return; | ||
} | ||
|
||
const { count } = await this.prisma.requestLog.deleteMany({ | ||
where: { | ||
createdAt: { | ||
lte: last.createdAt, | ||
}, | ||
}, | ||
}); | ||
|
||
await this.logger.createLog( | ||
LogLevel.log, | ||
LogMessage.REQUEST_LOGS_CLEANUP, | ||
{ | ||
message: "Successfully removed old logs", | ||
count, | ||
}, | ||
); | ||
} catch (error) { | ||
await this.logger.createLog( | ||
LogLevel.error, | ||
LogMessage.REQUEST_LOGS_CLEANUP, | ||
{ | ||
message: "Failed to cleanup logs", | ||
error: JSON.stringify(error), | ||
}, | ||
); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add index and field constraints to RequestLog model
Consider the following improvements to optimize queries and ensure data integrity:
The changes:
📝 Committable suggestion