From 6845182677d9acd7801b5f7c73e5080f4541eec1 Mon Sep 17 00:00:00 2001 From: DavisVT Date: Thu, 22 Oct 2026 00:34:13 +0100 Subject: [PATCH] Add slow request logging (#754) - Add SLOW_REQUEST_THRESHOLD_MS environment variable (default 2000ms) - Implement slowRequestLoggerMiddleware to log requests exceeding threshold - Log includes method, path, status_code, duration_ms, wallet (if authenticated), slow_threshold_ms - Log emitted after response is sent, no request/response bodies logged - Register middleware in app.ts --- src/app.ts | 3 +- src/config.schema.ts | 2 + .../request-completion-logger.middleware.ts | 43 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/app.ts b/src/app.ts index 778c80c0..638fb684 100644 --- a/src/app.ts +++ b/src/app.ts @@ -26,7 +26,7 @@ import { schemaVersionMiddleware } from './middlewares/schema-version.middleware import { requestLoggerMiddleware } from './middlewares/request-logger.middleware'; import { requestContextMiddleware } from './middlewares/request-context.middleware'; import { requestEntryLoggerMiddleware } from './middlewares/request-entry-logger.middleware'; -import { requestCompletionLoggerMiddleware } from './middlewares/request-completion-logger.middleware'; +import { requestCompletionLoggerMiddleware, slowRequestLoggerMiddleware } from './middlewares/request-completion-logger.middleware'; import { sessionActivityMiddleware } from './middlewares/session-activity.middleware'; import { bodyParseErrorMiddleware } from './middlewares/body-parse-error.middleware'; import { routeBodySizeLimit } from './middlewares/body-size-limit.middleware'; @@ -47,6 +47,7 @@ app.use(apiVersionMiddleware); app.use(schemaVersionMiddleware); app.use(requestEntryLoggerMiddleware); app.use(requestCompletionLoggerMiddleware); +app.use(slowRequestLoggerMiddleware); // Session activity logging (#1035): registers a res.on('finish') listener // so it can read req.user after route-level auth guards have run. Mount // position only needs to be before the routers. diff --git a/src/config.schema.ts b/src/config.schema.ts index cc289298..f1ff2c4d 100644 --- a/src/config.schema.ts +++ b/src/config.schema.ts @@ -651,6 +651,8 @@ export const envSchema = z BADGE_TOP_TRADER_MAX_RANK: z.coerce.number().int().positive().default(10), // Max mint attempts before a transient failure is treated as terminal. BADGE_MINT_MAX_ATTEMPTS: z.coerce.number().int().positive().default(3), + // Slow request threshold (#754): warn-level log for requests exceeding this duration + SLOW_REQUEST_THRESHOLD_MS: z.coerce.number().int().positive().default(2000), }) .superRefine((data, ctx) => { if (data.MODE === 'production' && data.STELLAR_NETWORK === 'testnet') { diff --git a/src/middlewares/request-completion-logger.middleware.ts b/src/middlewares/request-completion-logger.middleware.ts index d63089d5..08bf2dd1 100644 --- a/src/middlewares/request-completion-logger.middleware.ts +++ b/src/middlewares/request-completion-logger.middleware.ts @@ -1,5 +1,12 @@ import { Request, Response, NextFunction } from 'express'; import { logger } from '../utils/logger.utils'; +import { envConfig } from '../config'; + +interface AuthenticatedRequest extends Request { + user?: { + wallet: string; + }; +} // Log request outcome after the response has been fully sent. // @@ -72,3 +79,39 @@ export const requestCompletionLoggerMiddleware = ( next(); }; + +export const slowRequestLoggerMiddleware = ( + req: AuthenticatedRequest, + res: Response, + next: NextFunction +): void => { + const startTime = process.hrtime(); + let logged = false; + + // Ensure we log once even if multiple events fire. + const logOnce = () => { + if (logged) return; + logged = true; + + const diff = process.hrtime(startTime); + const durationMs = diff[0] * 1e3 + diff[1] * 1e-6; + + if (durationMs >= envConfig.SLOW_REQUEST_THRESHOLD_MS) { + const wallet = req.user?.wallet; + const payload = { + method: req.method, + path: req.path, + status_code: res.statusCode, + duration_ms: Math.round(durationMs), + ...(wallet ? { wallet } : {}), + slow_threshold_ms: envConfig.SLOW_REQUEST_THRESHOLD_MS, + }; + logger.warn(payload, 'Slow request detected'); + } + }; + + res.once('finish', logOnce); + res.once('close', logOnce); + + next(); +};