144 lines
3.4 KiB
TypeScript
144 lines
3.4 KiB
TypeScript
/**
|
|
* OperationContext - Unified context for handler operations
|
|
*/
|
|
|
|
import { getLogger, type Logger } from '@stock-bot/logger';
|
|
|
|
interface ServiceResolver {
|
|
resolve<T>(serviceName: string): T;
|
|
resolveAsync<T>(serviceName: string): Promise<T>;
|
|
}
|
|
|
|
export interface OperationContextOptions {
|
|
handlerName: string;
|
|
operationName: string;
|
|
parentLogger?: Logger;
|
|
container?: ServiceResolver;
|
|
metadata?: Record<string, any>;
|
|
traceId?: string;
|
|
}
|
|
|
|
export class OperationContext {
|
|
public readonly logger: Logger;
|
|
public readonly traceId: string;
|
|
public readonly metadata: Record<string, any>;
|
|
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<string, any>;
|
|
traceId?: string;
|
|
} = {}
|
|
): OperationContext {
|
|
return new OperationContext({
|
|
handlerName,
|
|
operationName,
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Resolve a service from the container
|
|
*/
|
|
resolve<T>(serviceName: string): T {
|
|
if (!this.container) {
|
|
throw new Error('No service container available');
|
|
}
|
|
return this.container.resolve<T>(serviceName);
|
|
}
|
|
|
|
/**
|
|
* Resolve a service asynchronously from the container
|
|
*/
|
|
async resolveAsync<T>(serviceName: string): Promise<T> {
|
|
if (!this.container) {
|
|
throw new Error('No service container available');
|
|
}
|
|
return this.container.resolveAsync<T>(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<void> {
|
|
this.logCompletion(true);
|
|
}
|
|
|
|
/**
|
|
* Create child context
|
|
*/
|
|
createChild(operationName: string, metadata?: Record<string, any>): 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)}`;
|
|
}
|
|
}
|