UNPKG

@gati-framework/runtime

Version:

Gati runtime execution engine for running handler-based applications

133 lines 4.91 kB
/** * @module runtime/metrics-client * @description Metrics and observability client for Gati runtime */ import * as api from '@opentelemetry/api'; import { logger } from './logger.js'; /** * Runtime metrics client implementation */ export class RuntimeMetricsClient { serviceName; serviceVersion; tracer; metrics; counters = new Map(); gauges = new Map(); histograms = new Map(); constructor(serviceName = 'gati-runtime', serviceVersion = '1.0.0') { this.serviceName = serviceName; this.serviceVersion = serviceVersion; this.tracer = api.trace.getTracer(serviceName, serviceVersion); // Initialize simple metrics implementation this.metrics = { createCounter: (name, help, labelNames = []) => ({ inc: (labels = {}, value = 1) => { logger.debug({ name, labels, value }, 'Counter incremented'); } }), createGauge: (name, help, labelNames = []) => ({ set: (labels = {}, value) => { logger.debug({ name, labels, value }, 'Gauge set'); } }), createHistogram: (name, help, labelNames = [], buckets) => ({ observe: (labels = {}, value) => { logger.debug({ name, labels, value }, 'Histogram observed'); } }) }; } incrementCounter(name, labels = {}, value = 1) { const key = `${name}_${JSON.stringify(labels)}`; if (!this.counters.has(key) && this.metrics) { const labelNames = Object.keys(labels); this.counters.set(key, this.metrics.createCounter(name, `Counter for ${name}`, labelNames)); } const counter = this.counters.get(key); if (counter) { counter.inc(labels, value); } logger.debug({ name, labels, value }, 'Counter incremented'); } setGauge(name, value, labels = {}) { const key = `${name}_${JSON.stringify(labels)}`; if (!this.gauges.has(key) && this.metrics) { const labelNames = Object.keys(labels); this.gauges.set(key, this.metrics.createGauge(name, `Gauge for ${name}`, labelNames)); } const gauge = this.gauges.get(key); if (gauge) { gauge.set(labels, value); } logger.debug({ name, labels, value }, 'Gauge set'); } recordHistogram(name, value, labels = {}) { const key = `${name}_${JSON.stringify(labels)}`; if (!this.histograms.has(key) && this.metrics) { const labelNames = Object.keys(labels); this.histograms.set(key, this.metrics.createHistogram(name, `Histogram for ${name}`, labelNames)); } const histogram = this.histograms.get(key); if (histogram) { histogram.observe(labels, value); } logger.debug({ name, labels, value }, 'Histogram recorded'); } 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(); } } logWithContext(level, message, context) { const span = api.trace.getActiveSpan(); const traceContext = span ? { traceId: span.spanContext().traceId, spanId: span.spanContext().spanId, } : {}; logger[level]({ ...context, ...traceContext }, message); } recordAudit(event, context) { const auditLog = { timestamp: new Date().toISOString(), event, requestId: context.requestId, handlerId: context.handlerId, version: context.version, userId: context.userId, action: context.action, resource: context.resource, result: context.result, metadata: context.metadata, }; logger.info(auditLog, 'Audit event'); // Increment audit counter this.incrementCounter('audit_events_total', { event, action: context.action, result: context.result, }); } } /** * Default metrics client instance */ export const metricsClient = new RuntimeMetricsClient(); //# sourceMappingURL=metrics-client.js.map