/** * Production-ready logger configuration for DataBridge APIs * * Features: * - Structured JSON logging for production * - Pretty printing for development * - Request/response tracking with correlation IDs * - Sensitive data redaction * - Compatible with: Datadog, New Relic, ELK, Grafana Loki * - Log rotation ready */ import type { FastifyRequest, FastifyReply } from 'fastify'; /** * Environment-aware logger configuration */ export const loggerConfig = { development: { transport: { target: 'pino-pretty', options: { translateTime: 'HH:MM:ss', ignore: 'pid,hostname', colorize: true, }, }, level: 'debug', }, production: { level: 'info', formatters: { level: (label: string) => { return { level: label.toUpperCase() }; }, }, timestamp: () => `,"time":"${new Date().toISOString()}"`, redact: { paths: [ 'req.headers.authorization', 'req.headers.cookie', 'req.body.password', 'req.body.token', 'req.body.secret', 'res.headers["set-cookie"]', ], remove: true, }, }, test: { level: 'silent', }, }; /** * Get logger configuration based on environment */ export function getLoggerConfig() { const env = process.env.NODE_ENV || 'development'; return loggerConfig[env as keyof typeof loggerConfig] || loggerConfig.development; } /** * Create custom serializers for request/response logging */ export const serializers = { req: (request: FastifyRequest) => { return { method: request.method, url: request.url, path: request.routeOptions?.url || request.url, parameters: request.params, query: request.query, headers: { host: request.headers.host, userAgent: request.headers['user-agent'], referer: request.headers.referer, }, remoteAddress: request.ip, remotePort: request.socket.remotePort, }; }, res: (reply: FastifyReply) => { return { statusCode: reply.statusCode, headers: { contentType: reply.getHeader('content-type'), contentLength: reply.getHeader('content-length'), }, }; }, }; /** * Get logger configuration for Fastify * Fastify will create the Pino instance internally */ export function getFastifyLoggerConfig() { const config = getLoggerConfig(); return { ...config, serializers, }; } /** * Request logger middleware configuration * Logs in Apache Common Log Format compatible structure */ export const requestLoggerConfig = { // Log all requests logLevel: 'info', // Customize request logging customProps: (req: FastifyRequest, res: FastifyReply) => ({ correlationId: req.id, userAgent: req.headers['user-agent'], referer: req.headers.referer, }), // Custom success message customSuccessMessage: (req: FastifyRequest, res: FastifyReply, responseTime: number) => { return `${req.method} ${req.url} ${res.statusCode} - ${responseTime.toFixed(2)}ms`; }, // Custom error message customErrorMessage: (req: FastifyRequest, res: FastifyReply, error: Error) => { return `${req.method} ${req.url} ${res.statusCode} - ${error.message}`; }, // Include response time customAttributeKeys: { responseTime: 'responseTime', req: 'req', res: 'res', err: 'err', }, }; /** * Database query logger * Logs slow queries and errors */ export function createQueryLogger(logger: any) { return { logQuery: (query: string, duration: number, params?: any) => { if (duration > 1000) { // Log slow queries (>1s) logger.warn({ type: 'slow_query', query, duration, params, }, 'Slow database query detected'); } else if (process.env.NODE_ENV === 'development') { // Log all queries in development logger.debug({ type: 'query', query, duration, params, }, 'Database query executed'); } }, logError: (error: Error, query?: string) => { logger.error({ type: 'query_error', error: error.message, stack: error.stack, query, }, 'Database query failed'); }, }; } /** * Export logs in Apache Common Log Format * Format: host ident authuser date request status bytes */ export function formatAsApacheLog(req: FastifyRequest, res: FastifyReply, responseTime: number): string { const remoteAddr = req.ip || '-'; const remoteUser = '-'; // Add auth user if available const timestamp = new Date().toISOString(); const method = req.method; const url = req.url; const protocol = `HTTP/${req.raw.httpVersion}`; const status = res.statusCode; const contentLength = res.getHeader('content-length') || '-'; const referer = req.headers.referer || '-'; const userAgent = req.headers['user-agent'] || '-'; return `${remoteAddr} - ${remoteUser} [${timestamp}] "${method} ${url} ${protocol}" ${status} ${contentLength} "${referer}" "${userAgent}" ${responseTime.toFixed(3)}ms`; } /** * Validation error logger */ export function logValidationError(logger: any, req: FastifyRequest, errors: any[]) { logger.warn({ type: 'validation_error', method: req.method, url: req.url, errors: errors.map(err => ({ path: err.path?.join('.'), message: err.message, code: err.code, })), }, 'Request validation failed'); } /** * Security event logger */ export function logSecurityEvent( logger: any, event: string, req: FastifyRequest, details?: any ) { logger.warn({ type: 'security_event', event, method: req.method, url: req.url, ip: req.ip, userAgent: req.headers['user-agent'], details, }, `Security event: ${event}`); }