UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

191 lines 5.4 kB
/** * Abstract base class for all observability exporters * Follows NeuroLink's factory pattern with a unified exporter interface */ import { logger } from "../../utils/logger.js"; /** * Abstract base class for all observability exporters * Provides common functionality: buffering, flush intervals, health checks, retry logic */ export class BaseExporter { name; config; initialized = false; buffer = []; maxBufferSize; retries; flushInterval = null; lastExportTime = 0; constructor(name, config) { this.name = name; this.config = config; this.maxBufferSize = config.maxBufferSize ?? 100; this.retries = config.retries ?? 3; } /** * Ping the exporter's backend to verify connectivity * Override this in subclasses to provide backend-specific health check */ async ping() { // Default implementation does nothing // Subclasses should override this to make an actual API call } /** * Buffer a span for batch export * Triggers flush if buffer is full */ bufferSpan(span) { this.buffer.push(span); if (this.buffer.length >= this.maxBufferSize) { // Use void to explicitly ignore the promise void this.flush(); } } /** * Start automatic flush interval * @param intervalMs - Interval in milliseconds between flushes */ startFlushInterval(intervalMs) { if (this.flushInterval) { clearInterval(this.flushInterval); } this.flushInterval = setInterval(() => { this.flush().catch((error) => { logger.warn(`[${this.name}] Periodic flush failed`, { error: error instanceof Error ? error.message : String(error), }); }); }, intervalMs); } /** * Stop the automatic flush interval */ stopFlushInterval() { if (this.flushInterval) { clearInterval(this.flushInterval); this.flushInterval = null; } } /** * Get exporter name */ getName() { return this.name; } /** * Check if exporter is initialized */ isInitialized() { return this.initialized; } /** * Get number of pending spans in buffer */ getPendingCount() { return this.buffer.length; } /** * Get last export timestamp */ getLastExportTime() { return this.lastExportTime; } /** * Create a standard export result for success */ createSuccessResult(exportedCount, durationMs) { this.lastExportTime = Date.now(); return { success: true, exportedCount, failedCount: 0, durationMs, }; } /** * Create a standard export result for failure */ createFailureResult(spanIds, error, durationMs, retryable = true) { return { success: false, exportedCount: 0, failedCount: spanIds.length, errors: spanIds.map((spanId) => ({ spanId, error, retryable, })), durationMs, }; } /** * Create a standard health status */ createHealthStatus(healthy, errors) { return { healthy, name: this.name, pendingSpans: this.buffer.length, lastExportTime: this.lastExportTime || undefined, errors, }; } /** * Execute an operation with exponential backoff retry * @param operation - The async operation to execute * @param operationName - Name for logging purposes * @returns The result of the operation * @throws The last error if all retries fail */ async withRetry(operation, operationName) { const maxRetries = this.retries; const baseDelay = 1000; let lastError; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await operation(); } catch (error) { lastError = error; if (attempt < maxRetries) { const delay = baseDelay * 2 ** attempt; logger.warn(`[${this.name}] ${operationName} failed, retrying in ${delay}ms`, { attempt: attempt + 1, maxRetries, error: lastError.message, }); await new Promise((resolve) => setTimeout(resolve, delay)); } } } throw lastError; } } /** * No-op exporter for when observability is disabled * Provides zero-overhead behavior */ export class NoOpExporter extends BaseExporter { constructor() { super("noop", { enabled: false }); } async initialize() { this.initialized = true; } async exportSpan(_span) { return this.createSuccessResult(0, 0); } async exportBatch(_spans) { return this.createSuccessResult(0, 0); } async flush() { // No-op } async shutdown() { this.initialized = false; } async healthCheck() { return this.createHealthStatus(true); } } //# sourceMappingURL=baseExporter.js.map