UNPKG

@energica-city/shared-amplify-utils

Version:

Shared utilities for AWS Amplify projects

157 lines 5.51 kB
import { logger } from '../../log'; import { sanitizeObject } from '../utils/sanitization'; import { setupStructuredLogging } from './utils'; /** * Maximum depth for object sanitization to prevent infinite recursion * @internal */ const MAX_DEPTH = 10; /** * Extract structured information from WebSocket response for logging * * Processes the response object to extract relevant logging information while * applying sanitization rules to exclude sensitive fields. Handles both * standard WebSocket responses and arbitrary response objects. * * @param response - The response object to extract information from * @param config - Configuration specifying which fields to exclude * @returns Sanitized response information for logging * @internal */ function extractResponseInfo(response, config) { const { excludeResponseFields = [] } = config; if (response && typeof response === 'object' && 'statusCode' in response && 'body' in response) { const wsResponse = response; const info = { statusCode: wsResponse.statusCode, isBase64Encoded: wsResponse.isBase64Encoded, }; if (wsResponse.body) { try { const parsedBody = JSON.parse(wsResponse.body); info.body = sanitizeObject(parsedBody, { excludeFields: excludeResponseFields, maxDepth: MAX_DEPTH, }); } catch { info.bodyLength = wsResponse.body.length; info.body = '[Non-JSON response]'; } } if (wsResponse.headers && Object.keys(wsResponse.headers).length > 0) { info.headers = sanitizeObject(wsResponse.headers, { excludeFields: excludeResponseFields, maxDepth: MAX_DEPTH, }); } return info; } return { responseType: typeof response, responseData: sanitizeObject(response, { excludeFields: excludeResponseFields, maxDepth: MAX_DEPTH, }), }; } /** * Create a WebSocket request logging middleware * * This middleware provides comprehensive logging for WebSocket requests and responses. * It captures connection details, message timing, and optionally message bodies while * applying sanitization to exclude sensitive information. * * **Logging Flow:** * 1. Sets up structured logging context with connection details * 2. Logs incoming request with event information * 3. Executes next middleware and measures duration * 4. Logs response information (if response is provided) * 5. Logs completion with total duration * 6. Clears logging context * * **Log Levels:** * - `info`: Request received and response sent * - `debug`: Request completion timing * * **Security Features:** * - Field exclusion for sensitive data * - Object depth limiting to prevent large logs * - JSON parsing safety with fallback handling * - Automatic context cleanup * * @template TTypes - Record of available Amplify model types * @template TSelected - Selected model types for this middleware chain * @template TOutput - Expected output type of the middleware chain * @param config - Configuration options for logging behavior * @returns Middleware function for WebSocket request logging * * @example * ```typescript * const requestLogger = createWebSocketRequestLogger({ * logMessageBody: true, // This enables body logging * excludeEventFields: ['authToken'], // Optional: exclude sensitive fields * }); * * chain.use('logging', requestLogger); * ``` * * @example * ```typescript * // Production logging - minimal message body logging * const productionLogger = createWebSocketRequestLogger({ * logMessageBody: false, * excludeEventFields: ['body', 'headers'], * defaultContext: { environment: 'production' }, * }); * ``` * * @example * ```typescript * // Development logging - detailed logging with message bodies * const devLogger = createWebSocketRequestLogger({ * logMessageBody: true, * maxDepth: 8, * defaultContext: { * environment: 'development', * debug: true * }, * }); * ``` */ export function createWebSocketRequestLogger(config = {}) { const { defaultContext = {} } = config; return async (input, next) => { const startTime = Date.now(); const { context } = input; setupStructuredLogging(input, true, defaultContext); try { logger.info('WebSocket request received', { event: input.event, ...defaultContext, requestId: context?.awsRequestId || undefined, functionName: context?.functionName || undefined, functionVersion: context?.functionVersion || undefined, }); const result = await next(input); if (result !== undefined) { const responseInfo = extractResponseInfo(result, config); const duration = Date.now() - startTime; logger.info('WebSocket response sent', { ...responseInfo, ...defaultContext, duration: `${duration}ms`, requestId: context?.awsRequestId || undefined, }); } return result; } finally { logger.clearContext(); } }; } //# sourceMappingURL=WebSocketRequestLogger.js.map