@energica-city/shared-amplify-utils
Version:
Shared utilities for AWS Amplify projects
157 lines • 5.51 kB
JavaScript
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