import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; export interface RequestLog { method: string; url: string; statusCode: number; responseTime: number; requestId: string; userAgent?: string; clientIp: string; requestIdHeader?: string; } export async function loggingMiddleware(fastify: FastifyInstance) { // Generate request ID if not present fastify.addHook('onRequest', (request: FastifyRequest, reply: FastifyReply, done) => { const requestId = request.headers['x-request-id'] || request.headers['x-correlation-id'] || `req-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; request.headers['x-request-id'] = requestId; (request as any).requestId = requestId; done(); }); // Log request start fastify.addHook('onRequest', (request: FastifyRequest, reply: FastifyReply) => { fastify.log.info({ event: 'request_start', method: request.method, url: request.url, requestId: (request as any).requestId, userAgent: request.headers['user-agent'], clientIp: request.ip || request.headers['x-forwarded-for'] || 'unknown', }); }); // Log response fastify.addHook('onResponse', (request: FastifyRequest, reply: FastifyReply, done) => { const log: RequestLog = { method: request.method, url: request.url, statusCode: reply.statusCode, responseTime: reply.elapsedTime, requestId: (request as any).requestId, userAgent: request.headers['user-agent'], clientIp: request.ip || request.headers['x-forwarded-for'] || 'unknown', requestIdHeader: request.headers['x-request-id'] as string, }; // Log based on status code if (reply.statusCode < 300) { fastify.log.info(log); } else if (reply.statusCode < 400) { fastify.log.warn(log); } else if (reply.statusCode < 500) { fastify.log.warn(log); } else { fastify.log.error(log); } done(); }); }