Files
order_site/apps/backend/src/middleware/access-log.ts
T

59 lines
2.0 KiB
TypeScript

import type { Request, Response, NextFunction } from 'express'
import { createRequestId, logDebug, logInfo } from '../utils/logger.js'
export function accessLogMiddleware(req: Request, res: Response, next: NextFunction): void {
const startedAt = Date.now()
const requestId = resolveRequestId(req)
req.requestId = requestId
res.setHeader('X-Request-Id', requestId)
res.on('finish', () => {
if (shouldSkipAccessLog(req.originalUrl, res.statusCode)) {
return
}
const writeLog =
resolveAccessLogLevel(req.method, res.statusCode) === 'debug' ? logDebug : logInfo
writeLog('[http/access]', 'request completed', {
requestId,
method: req.method,
originalUrl: req.originalUrl,
statusCode: res.statusCode,
durationMs: Date.now() - startedAt,
ip: req.ip,
forwardedFor: String(req.headers['x-forwarded-for'] || ''),
userAgent: String(req.headers['user-agent'] || ''),
referer: String(req.headers.referer || ''),
contentLength: Number(res.getHeader('content-length') || 0),
actorUserId: req.adminSession?.userId || '',
actorUsername: req.adminSession?.username || '',
actorRole: req.adminSession?.role || '',
})
})
next()
}
export function resolveAccessLogLevel(method: unknown, statusCode: unknown): 'debug' | 'info' {
const normalizedMethod = String(method || '')
.trim()
.toUpperCase()
const normalizedStatus = Number(statusCode || 0)
return ['GET', 'HEAD'].includes(normalizedMethod) &&
normalizedStatus >= 200 &&
normalizedStatus < 400
? 'debug'
: 'info'
}
function resolveRequestId(req: Request): string {
const fromHeader = String(req.headers['x-request-id'] || '').trim()
return fromHeader || createRequestId('req')
}
function shouldSkipAccessLog(originalUrl: string, statusCode: number): boolean {
const pathname = String(originalUrl || '').split('?')[0] || ''
return ['/health', '/health/live', '/health/ready'].includes(pathname) && Number(statusCode) < 400
}