/** * OperationContext - Unified context for handler operations */ import { getLogger, type Logger } from '@stock-bot/logger'; interface ServiceResolver { resolve(serviceName: string): T; resolveAsync(serviceName: string): Promise; } export interface OperationContextOptions { handlerName: string; operationName: string; parentLogger?: Logger; container?: ServiceResolver; metadata?: Record; traceId?: string; } export class OperationContext { public readonly logger: Logger; public readonly traceId: string; public readonly metadata: Record; private readonly container?: ServiceResolver; private readonly startTime: Date; constructor(options: OperationContextOptions) { this.container = options.container; this.metadata = options.metadata || {}; this.traceId = options.traceId || this.generateTraceId(); this.startTime = new Date(); this.logger = options.parentLogger || getLogger(`${options.handlerName}:${options.operationName}`, { traceId: this.traceId, metadata: this.metadata, }); } /** * Creates a new OperationContext with automatic resource management */ static create( handlerName: string, operationName: string, options: { container?: ServiceResolver; parentLogger?: Logger; metadata?: Record; traceId?: string; } = {} ): OperationContext { return new OperationContext({ handlerName, operationName, ...options, }); } /** * Resolve a service from the container */ resolve(serviceName: string): T { if (!this.container) { throw new Error('No service container available'); } return this.container.resolve(serviceName); } /** * Resolve a service asynchronously from the container */ async resolveAsync(serviceName: string): Promise { if (!this.container) { throw new Error('No service container available'); } return this.container.resolveAsync(serviceName); } /** * Add metadata to the context */ addMetadata(key: string, value: any): void { this.metadata[key] = value; } /** * Get execution time in milliseconds */ getExecutionTime(): number { return Date.now() - this.startTime.getTime(); } /** * Log operation completion with metrics */ logCompletion(success: boolean, error?: Error): void { const executionTime = this.getExecutionTime(); if (success) { this.logger.info('Operation completed successfully', { executionTime, metadata: this.metadata, }); } else { this.logger.error('Operation failed', { executionTime, error: error?.message, stack: error?.stack, metadata: this.metadata, }); } } /** * Cleanup method */ async dispose(): Promise { this.logCompletion(true); } /** * Create child context */ createChild(operationName: string, metadata?: Record): OperationContext { return new OperationContext({ handlerName: 'child', operationName, parentLogger: this.logger, container: this.container, traceId: this.traceId, metadata: { ...this.metadata, ...metadata }, }); } /** * Generate a unique trace ID */ private generateTraceId(): string { return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } }