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
JavaScript
;
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;