added logger
This commit is contained in:
parent
dd27f3bf2c
commit
58ae897e90
13 changed files with 1493 additions and 12 deletions
200
libs/logger/src/middleware.ts
Normal file
200
libs/logger/src/middleware.ts
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/**
|
||||
* Hono middleware for request logging
|
||||
*/
|
||||
|
||||
import type { Context, Next } from 'hono';
|
||||
import { Logger } from './logger';
|
||||
import { generateCorrelationId, createTimer } from './utils';
|
||||
|
||||
export interface LoggingMiddlewareOptions {
|
||||
logger?: Logger;
|
||||
serviceName?: string;
|
||||
skipPaths?: string[];
|
||||
skipSuccessfulRequests?: boolean;
|
||||
logRequestBody?: boolean;
|
||||
logResponseBody?: boolean;
|
||||
maxBodySize?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hono middleware for HTTP request/response logging
|
||||
*/
|
||||
export function loggingMiddleware(options: LoggingMiddlewareOptions = {}) {
|
||||
const {
|
||||
serviceName = 'unknown-service',
|
||||
skipPaths = ['/health', '/metrics', '/favicon.ico'],
|
||||
skipSuccessfulRequests = false,
|
||||
logRequestBody = false,
|
||||
logResponseBody = false,
|
||||
maxBodySize = 1024
|
||||
} = options;
|
||||
|
||||
// Create logger if not provided
|
||||
const logger = options.logger || new Logger(serviceName);
|
||||
|
||||
return async (c: Context, next: Next) => {
|
||||
const url = new URL(c.req.url);
|
||||
const path = url.pathname;
|
||||
|
||||
// Skip certain paths
|
||||
if (skipPaths.some(skipPath => path.startsWith(skipPath))) {
|
||||
return next();
|
||||
} // Generate correlation ID
|
||||
const correlationId = generateCorrelationId();
|
||||
// Store correlation ID in context for later use
|
||||
c.set('correlationId', correlationId);
|
||||
// Set correlation ID as response header
|
||||
c.header('x-correlation-id', correlationId);
|
||||
|
||||
// Start timer
|
||||
const timer = createTimer(`${c.req.method} ${path}`); // Extract request metadata
|
||||
const requestMetadata: any = {
|
||||
method: c.req.method,
|
||||
url: c.req.url,
|
||||
path: path,
|
||||
userAgent: c.req.header('user-agent'),
|
||||
contentType: c.req.header('content-type'),
|
||||
contentLength: c.req.header('content-length'),
|
||||
ip: c.req.header('x-forwarded-for') || c.req.header('x-real-ip') || 'unknown'
|
||||
};
|
||||
|
||||
// Add request body if enabled
|
||||
if (logRequestBody) {
|
||||
try {
|
||||
const body = await c.req.text();
|
||||
if (body) {
|
||||
requestMetadata.body = body.length > maxBodySize
|
||||
? body.substring(0, maxBodySize) + '...[truncated]'
|
||||
: body;
|
||||
}
|
||||
} catch (error) {
|
||||
// Body might not be available or already consumed
|
||||
}
|
||||
} // Log request start
|
||||
logger.http('HTTP Request started', {
|
||||
method: c.req.method,
|
||||
url: c.req.url,
|
||||
userAgent: c.req.header('user-agent'),
|
||||
ip: c.req.header('x-forwarded-for') || c.req.header('x-real-ip') || 'unknown'
|
||||
});
|
||||
|
||||
// Process request
|
||||
await next();
|
||||
|
||||
// Calculate response time
|
||||
const timing = timer.end();
|
||||
|
||||
// Get response information
|
||||
const response = c.res;
|
||||
const status = response.status;
|
||||
|
||||
// Determine log level based on status code
|
||||
const isError = status >= 400;
|
||||
const isSuccess = status >= 200 && status < 300;
|
||||
|
||||
// Skip successful requests if configured
|
||||
if (skipSuccessfulRequests && isSuccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const responseMetadata: any = {
|
||||
correlationId,
|
||||
request: requestMetadata,
|
||||
response: {
|
||||
statusCode: status,
|
||||
statusText: response.statusText,
|
||||
contentLength: response.headers.get('content-length'),
|
||||
contentType: response.headers.get('content-type')
|
||||
},
|
||||
performance: timing
|
||||
};
|
||||
|
||||
// Add response body if enabled
|
||||
if (logResponseBody) {
|
||||
try {
|
||||
const responseBody = await response.clone().text();
|
||||
if (responseBody) {
|
||||
responseMetadata.response.body = responseBody.length > maxBodySize
|
||||
? responseBody.substring(0, maxBodySize) + '...[truncated]'
|
||||
: responseBody;
|
||||
}
|
||||
} catch (error) {
|
||||
// Response body might not be available
|
||||
}
|
||||
} // Log based on status code
|
||||
if (isError) {
|
||||
logger.error('HTTP Request failed', undefined, {
|
||||
correlationId,
|
||||
request: requestMetadata,
|
||||
response: {
|
||||
statusCode: status,
|
||||
statusText: response.statusText,
|
||||
contentLength: response.headers.get('content-length'),
|
||||
contentType: response.headers.get('content-type')
|
||||
},
|
||||
performance: timing
|
||||
});
|
||||
} else {
|
||||
logger.http('HTTP Request completed', {
|
||||
method: c.req.method,
|
||||
url: c.req.url,
|
||||
statusCode: status,
|
||||
responseTime: timing.duration,
|
||||
userAgent: c.req.header('user-agent'),
|
||||
ip: c.req.header('x-forwarded-for') || c.req.header('x-real-ip') || 'unknown'
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Error logging middleware for Hono
|
||||
*/
|
||||
export function errorLoggingMiddleware(logger?: Logger) {
|
||||
return async (c: Context, next: Next) => {
|
||||
const errorLogger = logger || new Logger('error-handler');
|
||||
|
||||
try {
|
||||
await next();
|
||||
} catch (err) {
|
||||
const correlationId = c.get('correlationId') || c.req.header('x-correlation-id');
|
||||
const url = new URL(c.req.url);
|
||||
|
||||
const requestMetadata = {
|
||||
method: c.req.method,
|
||||
url: c.req.url,
|
||||
path: url.pathname,
|
||||
userAgent: c.req.header('user-agent'),
|
||||
ip: c.req.header('x-forwarded-for') || c.req.header('x-real-ip') || 'unknown'
|
||||
};
|
||||
|
||||
errorLogger.error('Unhandled HTTP error', err instanceof Error ? err : new Error(String(err)), {
|
||||
correlationId,
|
||||
request: requestMetadata,
|
||||
response: {
|
||||
statusCode: c.res.status
|
||||
}
|
||||
});
|
||||
|
||||
// Re-throw the error so Hono can handle it
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a child logger with request context for Hono
|
||||
*/
|
||||
export function createRequestLogger(c: Context, baseLogger: Logger): Logger {
|
||||
const correlationId = c.get('correlationId') || c.req.header('x-correlation-id');
|
||||
const url = new URL(c.req.url);
|
||||
|
||||
return baseLogger.child({
|
||||
correlationId,
|
||||
requestId: correlationId,
|
||||
method: c.req.method,
|
||||
path: url.pathname,
|
||||
userAgent: c.req.header('user-agent'),
|
||||
ip: c.req.header('x-forwarded-for') || c.req.header('x-real-ip') || 'unknown'
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue