UNPKG

arvo-core

Version:

The core Arvo package which provides application tier core primitives and contract system for building production-grade event-driven application. Provides ArvoEvent (CloudEvents-compliant), ArvoContract for type-safe service interfaces, event factories, O

233 lines (232 loc) 10.8 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.makeOpenTelemetryContextContext = exports.getOtelHeaderFromSpan = exports.OTelNull = exports.exceptionToSpan = exports.logToSpan = exports.ArvoOpenTelemetry = void 0; exports.currentOpenTelemetryHeaders = currentOpenTelemetryHeaders; var api_1 = require("@opentelemetry/api"); var core_1 = require("@opentelemetry/core"); var ArvoExecution_1 = require("./ArvoExecution"); /** * Singleton class for managing OpenTelemetry instrumentation across libraries */ var ArvoOpenTelemetry = /** @class */ (function () { function ArvoOpenTelemetry(tracer) { this.tracer = tracer !== null && tracer !== void 0 ? tracer : api_1.trace.getTracer('arvo-instrumentation', '1.0.0'); } /** * Gets or creates the singleton instance of ArvoOpenTelemetry. * This method ensures only one instance of ArvoOpenTelemetry exists throughout the application. * * @param {Object} [config] - Optional configuration object for initializing the instance * @param {Tracer} [config.tracer] - Optional custom OpenTelemetry tracer instance. * If not provided, defaults to a tracer with name 'arvo-instrumentation' * * @returns {ArvoOpenTelemetry} The singleton instance of ArvoOpenTelemetry * * @example * // Get instance with default tracer * const telemetry = ArvoOpenTelemetry.getInstance(); * * @example * // Get instance with custom tracer * const customTracer = trace.getTracer('custom-tracer', '2.0.0'); * const telemetry = ArvoOpenTelemetry.getInstance({ tracer: customTracer }); * * @remarks * The tracer configuration is only applied when creating a new instance. * Subsequent calls with different tracer configurations will not modify the existing instance. */ ArvoOpenTelemetry.getInstance = function (config) { if (ArvoOpenTelemetry.instance === null) { ArvoOpenTelemetry.instance = new ArvoOpenTelemetry(config === null || config === void 0 ? void 0 : config.tracer); } return ArvoOpenTelemetry.instance; }; /** * Forces a reinitialization of the ArvoOpenTelemetry instance. * Use this method with caution as it will affect all existing traces and spans. * * @param {Object} config - Configuration object for reinitializing the instance * @param {Tracer} [config.tracer] - Optional custom OpenTelemetry tracer instance * @param {boolean} [config.force=false] - If true, skips active span checks * * @throws {Error} If there are active spans and force is not set to true * @throws {Error} If called before instance initialization * * @example * // Safe reinitialization * const customTracer = trace.getTracer('new-tracer', '2.0.0'); * ArvoOpenTelemetry.reinitialize({ tracer: customTracer }); */ ArvoOpenTelemetry.reinitialize = function (config) { if (!ArvoOpenTelemetry.instance) { throw new Error('Cannot reinitialize before initialization. Call getInstance first.'); } // Check for active spans unless force is true if (!config.force) { var activeSpan = api_1.trace.getActiveSpan(); if (activeSpan) { throw new Error('Cannot reinitialize while spans are active. ' + 'Either end all spans or use force: true (not recommended)'); } } // Create new instance ArvoOpenTelemetry.instance = new ArvoOpenTelemetry(config.tracer); }; /** * Creates and manages an active span for a given operation. This function provides two modes of operation: * 1. Automatic span management (default): Handles span lifecycle, status, and error recording * 2. Manual span management: Gives full control to the user when disableSpanManagement is true * * @template F - Function type that accepts a Span parameter and returns a value * @param {Object} param - Configuration object for the span * @param {string} param.name - Name of the span to be created * @param {F} param.fn - Function to execute within the span context. Receives the span as a parameter * @param {SpanOptions} [param.spanOptions] - Optional configuration for the span creation * @param {boolean} [param.disableSpanManagement] - When true, disables automatic span lifecycle management * @param {Object} [param.context] - Optional context configuration for span inheritance * @param {string} param.context.inheritFrom - Specifies the type of context inheritance ('TRACE_HEADERS' | 'CONTEXT') * @param {OpenTelemetryHeaders} param.context.traceHeaders - Required when inheritFrom is 'TRACE_HEADERS' * @param {Context} param.context.context - Required when inheritFrom is 'CONTEXT' * @returns {ReturnType<F>} The return value of the executed function */ ArvoOpenTelemetry.prototype.startActiveSpan = function (param) { var _a; var _b, _c, _d; var parentContext; if (param.context) { if (param.context.inheritFrom === 'TRACE_HEADERS' && param.context.traceHeaders.traceparent) { parentContext = (0, exports.makeOpenTelemetryContextContext)(param.context.traceHeaders.traceparent, param.context.traceHeaders.tracestate); } else if (param.context.inheritFrom === 'CONTEXT') { parentContext = param.context.context; } } var span = this.tracer.startSpan(param.name, __assign(__assign({}, ((_b = param.spanOptions) !== null && _b !== void 0 ? _b : {})), { attributes: __assign((_a = {}, _a[ArvoExecution_1.ArvoExecution.ATTR_SPAN_KIND] = ArvoExecution_1.ArvoExecutionSpanKind.INTERNAL, _a), ((_d = (_c = param.spanOptions) === null || _c === void 0 ? void 0 : _c.attributes) !== null && _d !== void 0 ? _d : {})) }), parentContext); return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), function () { if (param.disableSpanManagement) { return param.fn(span); } span.setStatus({ code: api_1.SpanStatusCode.OK }); try { return param.fn(span); } catch (e) { (0, exports.exceptionToSpan)(e); span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e.message, }); throw e; } finally { span.end(); } }); }; ArvoOpenTelemetry.instance = null; return ArvoOpenTelemetry; }()); exports.ArvoOpenTelemetry = ArvoOpenTelemetry; /** * Logs a message to a span with additional parameters. * @param params - The parameters for the log message. * @param span - The span to log the message to. If not provided, the active span is used. * If no active span is available, the message is logged to the console. */ var logToSpan = function (params, span) { if (span === void 0) { span = api_1.trace.getActiveSpan(); } var level = params.level, message = params.message, restParams = __rest(params, ["level", "message"]); var toLog = __assign({ 'log.severity': level, 'log.message': message, 'log.timestamp': performance.now() }, restParams); if (span) { span.addEvent('log', toLog); } else { console.log(JSON.stringify(toLog, null, 2)); } }; exports.logToSpan = logToSpan; /** * Logs an exception to a span and sets exception-related attributes. * @param error - The error object to be logged. * @param span - The span to log the exception to. If not provided, the active span is used. * If no active span is available, the error is logged to the console. */ var exceptionToSpan = function (error, span) { if (span === void 0) { span = api_1.trace.getActiveSpan(); } if (span) { span.setAttributes({ 'exception.type': error.name, 'exception.message': error.message, }); span.recordException(error); } else { console.error(error); } }; exports.exceptionToSpan = exceptionToSpan; /** * A constant representing a null or not applicable value in OpenTelemetry context. */ exports.OTelNull = 'N/A'; /** * Retrieves the current OpenTelemetry headers from the active context. * @returns An object containing the traceparent and tracestate headers. */ function currentOpenTelemetryHeaders() { var propagator = new core_1.W3CTraceContextPropagator(); var carrier = { traceparent: null, tracestate: null, }; var setter = { set: function (carrier, key, value) { if (carrier && typeof carrier === 'object') { carrier[key] = value; } }, }; propagator.inject(api_1.context.active(), carrier, setter); return { traceparent: carrier.traceparent, tracestate: carrier.tracestate, }; } var getOtelHeaderFromSpan = function (span) { var _a; return ({ traceparent: "00-".concat(span.spanContext().traceId, "-").concat(span.spanContext().spanId, "-01"), tracestate: (_a = span.spanContext().traceState) !== null && _a !== void 0 ? _a : null, }); }; exports.getOtelHeaderFromSpan = getOtelHeaderFromSpan; // Helper function to extract context from traceparent and tracestate var makeOpenTelemetryContextContext = function (traceparent, tracestate) { var extractedContext = api_1.propagation.extract(api_1.context.active(), { traceparent: traceparent, tracestate: tracestate !== null && tracestate !== void 0 ? tracestate : undefined, }); return extractedContext; }; exports.makeOpenTelemetryContextContext = makeOpenTelemetryContextContext;