@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
JavaScript
/**
* 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