UNPKG

@gati-framework/observability

Version:

Observability stack for Gati framework - Prometheus, Grafana, Loki, and Tracing

104 lines (103 loc) 3.38 kB
import { NodeSDK } from '@opentelemetry/sdk-node'; import { resourceFromAttributes } from '@opentelemetry/resources'; import { SEMRESATTRS_SERVICE_NAME } from '@opentelemetry/semantic-conventions'; import * as api from '@opentelemetry/api'; export class DistributedTracing { sdk; tracer; constructor(config) { const resource = resourceFromAttributes({ [SEMRESATTRS_SERVICE_NAME]: config.serviceName, 'service.version': config.serviceVersion || '1.0.0', 'deployment.environment': config.environment || 'production', }); if (config.autoInstrument !== false) { this.sdk = new NodeSDK({ resource, }); this.sdk.start(); } this.tracer = api.trace.getTracer(config.serviceName, config.serviceVersion || '1.0.0'); } createSpan(name, attributes) { return this.tracer.startSpan(name, { attributes, }); } async withSpan(name, fn, attributes) { const span = this.createSpan(name, attributes); try { const result = await api.context.with(api.trace.setSpan(api.context.active(), span), async () => await fn(span)); span.setStatus({ code: api.SpanStatusCode.OK }); return result; } catch (error) { span.setStatus({ code: api.SpanStatusCode.ERROR, message: error instanceof Error ? error.message : 'Unknown error', }); span.recordException(error); throw error; } finally { span.end(); } } addEvent(name, attributes) { const span = api.trace.getActiveSpan(); if (span) { span.addEvent(name, attributes); } } setAttribute(key, value) { const span = api.trace.getActiveSpan(); if (span) { span.setAttribute(key, value); } } recordException(error) { const span = api.trace.getActiveSpan(); if (span) { span.recordException(error); } } getTraceContext() { const span = api.trace.getActiveSpan(); if (!span) return undefined; const spanContext = span.spanContext(); return `${spanContext.traceId}-${spanContext.spanId}`; } async shutdown() { if (this.sdk) { await this.sdk.shutdown(); } } } export function createTracingMiddleware(tracing) { return (req, res, next) => { const span = tracing.createSpan(`HTTP ${req.method} ${req.path}`, { 'http.method': req.method, 'http.url': req.url, 'http.route': req.route?.path || req.path, }); const traceContext = tracing.getTraceContext(); if (traceContext) { res.setHeader('X-Trace-Id', traceContext); } res.on('finish', () => { span.setAttribute('http.status_code', res.statusCode); if (res.statusCode >= 400) { span.setStatus({ code: api.SpanStatusCode.ERROR, message: `HTTP ${res.statusCode}`, }); } else { span.setStatus({ code: api.SpanStatusCode.OK }); } span.end(); }); next(); }; }