UNPKG

@vfarcic/dot-ai

Version:

AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance

216 lines (215 loc) 7.38 kB
"use strict"; /** * OpenTelemetry Tracer Service * * Provides lazy initialization and management of distributed tracing. * Follows OpenTelemetry best practices with support for multiple exporters. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.getTracer = getTracer; exports.shutdownTracer = shutdownTracer; exports.withSpan = withSpan; const sdk_node_1 = require("@opentelemetry/sdk-node"); const resources_1 = require("@opentelemetry/resources"); const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node"); const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http"); const api_1 = require("@opentelemetry/api"); const config_1 = require("./config"); /** * Global tracer instance (singleton pattern with lazy initialization) */ let tracerInstance = null; /** * OpenTelemetry Tracer implementation */ class OpenTelemetryTracer { sdk = null; config; initialized = false; constructor(config) { this.config = config; } /** * Initialize the OpenTelemetry SDK (called lazily on first use) */ initialize() { if (this.initialized) { return; // Already initialized } if (!this.config.enabled) { if (this.config.debug) { console.log('[Tracing] Tracing is disabled, skipping initialization'); } return; } try { // Validate configuration (0, config_1.validateTracingConfig)(this.config); // Create resource with service identification const serviceResource = (0, resources_1.resourceFromAttributes)({ [semantic_conventions_1.ATTR_SERVICE_NAME]: this.config.serviceName, [semantic_conventions_1.ATTR_SERVICE_VERSION]: this.config.serviceVersion, }); const resource = (0, resources_1.defaultResource)().merge(serviceResource); // Create exporter based on configuration const traceExporter = this.createExporter(); // Initialize Node SDK without auto-instrumentation // All operations are manually instrumented with descriptive spans this.sdk = new sdk_node_1.NodeSDK({ resource, traceExporter, instrumentations: [], // No auto-instrumentation needed }); // Start the SDK this.sdk.start(); this.initialized = true; if (this.config.debug) { console.log('[Tracing] OpenTelemetry initialized successfully', { serviceName: this.config.serviceName, serviceVersion: this.config.serviceVersion, exporterType: this.config.exporterType, }); } } catch (error) { console.error('[Tracing] Failed to initialize OpenTelemetry:', error); throw error; } } /** * Create exporter based on configuration */ createExporter() { switch (this.config.exporterType) { case 'console': if (this.config.debug) { console.log('[Tracing] Using console exporter (outputs to stderr)'); } return new sdk_trace_node_1.ConsoleSpanExporter(); case 'otlp': if (this.config.debug) { console.log('[Tracing] Using OTLP exporter', { endpoint: this.config.otlpEndpoint || 'http://localhost:4318/v1/traces' }); } return new exporter_trace_otlp_http_1.OTLPTraceExporter({ url: this.config.otlpEndpoint || 'http://localhost:4318/v1/traces', }); case 'jaeger': // Jaeger exporter will be added in Phase 3 throw new Error('Jaeger exporter not yet implemented - coming in Phase 3'); case 'zipkin': // Zipkin exporter will be added in Phase 3 throw new Error('Zipkin exporter not yet implemented - coming in Phase 3'); default: throw new Error(`Unknown exporter type: ${this.config.exporterType}`); } } /** * Check if tracing is enabled */ isEnabled() { return this.config.enabled; } /** * Create a new span with utility methods */ startSpan(name, options, parentContext) { if (!this.config.enabled) { // Return a no-op span if tracing is disabled return this.createNoOpSpan(); } // Lazy initialization on first span creation if (!this.initialized) { this.initialize(); } const tracer = api_1.trace.getTracer(this.config.serviceName, this.config.serviceVersion); const ctx = parentContext || api_1.context.active(); const span = tracer.startSpan(name, options, ctx); return this.wrapSpan(span); } /** * Wrap an OpenTelemetry span with utility methods */ wrapSpan(span) { return { span, end() { span.setStatus({ code: api_1.SpanStatusCode.OK }); span.end(); }, endWithError(error) { span.recordException(error); span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message, }); span.end(); }, setAttributes(attributes) { span.setAttributes(attributes); }, addEvent(name, attributes) { span.addEvent(name, attributes); }, }; } /** * Create a no-op span for when tracing is disabled */ createNoOpSpan() { const noopSpan = api_1.trace.getTracer('noop').startSpan('noop'); return this.wrapSpan(noopSpan); } /** * Shutdown the tracer gracefully */ async shutdown() { if (this.sdk && this.initialized) { if (this.config.debug) { console.log('[Tracing] Shutting down OpenTelemetry SDK...'); } await this.sdk.shutdown(); this.initialized = false; if (this.config.debug) { console.log('[Tracing] OpenTelemetry SDK shut down successfully'); } } } } /** * Get or create the global tracer instance */ function getTracer() { if (!tracerInstance) { const config = (0, config_1.loadTracingConfig)(); tracerInstance = new OpenTelemetryTracer(config); } return tracerInstance; } /** * Shutdown the global tracer instance */ async function shutdownTracer() { if (tracerInstance) { await tracerInstance.shutdown(); tracerInstance = null; } } /** * Helper function to wrap async operations with tracing */ async function withSpan(name, fn, options) { const tracer = getTracer(); const span = tracer.startSpan(name, options); try { const result = await fn(span); span.end(); return result; } catch (error) { span.endWithError(error); throw error; } }