UNPKG

cdk-serverless-agentic-api

Version:

CDK construct for serverless web applications with CloudFront, S3, Cognito, API Gateway, and Lambda

287 lines 8.6 kB
"use strict"; /** * Structured logging utilities for Lambda functions * Provides consistent logging format across all Lambda functions */ Object.defineProperty(exports, "__esModule", { value: true }); exports.MetricsCollector = exports.Logger = exports.LogLevel = void 0; exports.createLoggerFromEvent = createLoggerFromEvent; exports.withPerformanceLogging = withPerformanceLogging; var LogLevel; (function (LogLevel) { LogLevel["ERROR"] = "ERROR"; LogLevel["WARN"] = "WARN"; LogLevel["INFO"] = "INFO"; LogLevel["DEBUG"] = "DEBUG"; })(LogLevel || (exports.LogLevel = LogLevel = {})); /** * Structured logger class for Lambda functions */ class Logger { constructor(service, context = {}, version = '1.0.0') { this.service = service; this.context = context; this.version = version; this.logLevel = this.parseLogLevel(process.env.LOG_LEVEL || 'INFO'); } /** * Parse log level from string */ parseLogLevel(level) { const upperLevel = level.toUpperCase(); return Object.values(LogLevel).includes(upperLevel) ? upperLevel : LogLevel.INFO; } /** * Check if log level should be logged */ shouldLog(level) { const levels = [LogLevel.ERROR, LogLevel.WARN, LogLevel.INFO, LogLevel.DEBUG]; const currentLevelIndex = levels.indexOf(this.logLevel); const messageLevelIndex = levels.indexOf(level); return messageLevelIndex <= currentLevelIndex; } /** * Create structured log entry */ createLogEntry(level, message, data, error, duration) { const entry = { timestamp: new Date().toISOString(), level, message, service: this.service, version: this.version, context: this.context }; if (data !== undefined) { entry.data = data; } if (error) { entry.error = { name: error.name, message: error.message, stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }; } if (duration !== undefined) { entry.duration = duration; } return entry; } /** * Log structured entry to console */ log(entry) { if (!this.shouldLog(entry.level)) { return; } const logMessage = JSON.stringify(entry); switch (entry.level) { case LogLevel.ERROR: console.error(logMessage); break; case LogLevel.WARN: console.warn(logMessage); break; case LogLevel.DEBUG: console.debug(logMessage); break; default: console.log(logMessage); } } /** * Log error message */ error(message, error, data) { this.log(this.createLogEntry(LogLevel.ERROR, message, data, error)); } /** * Log warning message */ warn(message, data) { this.log(this.createLogEntry(LogLevel.WARN, message, data)); } /** * Log info message */ info(message, data) { this.log(this.createLogEntry(LogLevel.INFO, message, data)); } /** * Log debug message */ debug(message, data) { this.log(this.createLogEntry(LogLevel.DEBUG, message, data)); } /** * Log request start */ logRequestStart(event) { this.info('Request started', { httpMethod: event.httpMethod, path: event.path, queryStringParameters: event.queryStringParameters, headers: this.sanitizeHeaders(event.headers), isBase64Encoded: event.isBase64Encoded }); } /** * Log request end with duration */ logRequestEnd(statusCode, duration, responseSize) { this.info('Request completed', { statusCode, duration, responseSize }); } /** * Log authentication event */ logAuthentication(success, userId, groups, reason) { this.info('Authentication event', { success, userId, groups, reason }); } /** * Log authorization event */ logAuthorization(success, requiredGroup, userGroups, reason) { this.info('Authorization event', { success, requiredGroup, userGroups, reason }); } /** * Sanitize headers to remove sensitive information */ sanitizeHeaders(headers) { if (!headers) return headers; const sanitized = { ...headers }; const sensitiveHeaders = ['authorization', 'cookie', 'x-api-key', 'x-auth-token']; // Check for case-insensitive matches Object.keys(sanitized).forEach(key => { const lowerKey = key.toLowerCase(); if (sensitiveHeaders.includes(lowerKey)) { sanitized[key] = '[REDACTED]'; } }); return sanitized; } /** * Create child logger with additional context */ child(additionalContext) { const newContext = { ...this.context, ...additionalContext }; return new Logger(this.service, newContext, this.version); } /** * Update context for current logger */ updateContext(additionalContext) { this.context = { ...this.context, ...additionalContext }; } } exports.Logger = Logger; /** * Create logger from API Gateway event */ function createLoggerFromEvent(event, service = 'lambda-function') { // Handle null or undefined events if (!event) { return new Logger(service, {}, process.env.API_VERSION || '1.0.0'); } const context = { requestId: event.requestContext?.requestId, path: event.path, method: event.httpMethod, stage: event.requestContext?.stage, sourceIp: event.requestContext?.identity?.sourceIp, userAgent: event.headers?.['User-Agent'] || event.headers?.['user-agent'], correlationId: event.headers?.['X-Correlation-ID'] || event.headers?.['x-correlation-id'] }; // Extract user information from Cognito claims if available const claims = event.requestContext?.authorizer?.claims; if (claims) { context.userId = claims['cognito:username'] || claims.sub; context.userGroups = claims['cognito:groups'] ? claims['cognito:groups'].split(',') : []; } return new Logger(service, context, process.env.API_VERSION || '1.0.0'); } /** * Performance monitoring decorator for Lambda handlers */ function withPerformanceLogging(handler, logger) { return (async (...args) => { const startTime = Date.now(); try { const result = await handler(...args); const duration = Date.now() - startTime; logger.info('Handler execution completed', { duration }); return result; } catch (error) { const duration = Date.now() - startTime; logger.error('Handler execution failed', error, { duration }); throw error; } }); } /** * Metrics collection utilities */ class MetricsCollector { constructor(logger) { this.logger = logger; } /** * Record custom metric */ recordMetric(name, value, unit = 'Count', dimensions) { this.logger.info('Custom metric recorded', { metricName: name, value, unit, dimensions }); } /** * Record API response time */ recordResponseTime(endpoint, method, duration, statusCode) { this.recordMetric('ResponseTime', duration, 'Milliseconds', { endpoint, method, statusCode: statusCode.toString() }); } /** * Record error count */ recordError(errorType, endpoint, method) { this.recordMetric('ErrorCount', 1, 'Count', { errorType, endpoint: endpoint || 'unknown', method: method || 'unknown' }); } /** * Record business metric */ recordBusinessMetric(name, value, userId, userGroups) { this.recordMetric(name, value, 'Count', { userId: userId || 'anonymous', userGroups: userGroups?.join(',') || 'none' }); } } exports.MetricsCollector = MetricsCollector; //# sourceMappingURL=logging.js.map