UNPKG

@eventstore/opentelemetry

Version:

[![license][license-badge]][license-badge-url] [![Github action CI workflow][ci-badge]][ci-badge-url]

206 lines 9.94 kB
"use strict"; /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-this-alias */ /* eslint-disable @typescript-eslint/ban-types */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Instrumentation = void 0; const api_1 = require("@opentelemetry/api"); const instrumentation_1 = require("@opentelemetry/instrumentation"); const attributes_1 = require("./attributes"); const version_1 = require("./version"); const utils_1 = require("./utils"); const TRACE_ID = "$traceId"; const SPAN_ID = "$spanId"; class Instrumentation extends instrumentation_1.InstrumentationBase { constructor(config = {}) { super(version_1.INSTRUMENTATION_NAME, version_1.INSTRUMENTATION_VERSION, config); } init() { const moduleDefinition = new instrumentation_1.InstrumentationNodeModuleDefinition("@eventstore/db-client", ["6.*"], this._onPatchMain(), this._onUnPatchMain()); return moduleDefinition; } _onPatchMain() { return (moduleExports) => { this.wrap(moduleExports.EventStoreDBClient.prototype, "appendToStream", this._patchAppendToStream()); this.wrap(moduleExports.EventStoreDBClient.prototype, "subscribeToStream", this._patchCatchUpSubscription()); this.wrap(moduleExports.EventStoreDBClient.prototype, "subscribeToAll", this._patchCatchUpSubscription()); this.wrap(moduleExports.EventStoreDBClient.prototype, "subscribeToPersistentSubscriptionToStream", this._patchPersistentSubscription()); this.wrap(moduleExports.EventStoreDBClient.prototype, "subscribeToPersistentSubscriptionToAll", this._patchPersistentSubscription()); return moduleExports; }; } wrap(target, name, replacementFactory) { this._wrap(target, name, (originalMethod) => replacementFactory(originalMethod, name)); } _onUnPatchMain() { return (moduleExports) => { this._diag.debug("un-patching"); this._unwrap(moduleExports.EventStoreDBClient.prototype, "appendToStream"); this._unwrap(moduleExports.EventStoreDBClient.prototype, "subscribeToStream"); this._unwrap(moduleExports.EventStoreDBClient.prototype, "subscribeToPersistentSubscriptionToStream"); this._unwrap(moduleExports.EventStoreDBClient.prototype, "subscribeToPersistentSubscriptionToAll"); }; } _patchAppendToStream() { const instrumentation = this; const tracer = instrumentation.tracer; return function appendToStream(original, operation) { return async function (...args) { const [streamName, events, options] = [...args]; let actualEvents; const uri = await this.resolveUri(); const { hostname, port } = Instrumentation.getServerAddress(uri); const attributes = { [attributes_1.EventStoreDBAttributes.EVENT_STORE_STREAM]: streamName, [attributes_1.EventStoreDBAttributes.SERVER_ADDRESS]: hostname, [attributes_1.EventStoreDBAttributes.SERVER_PORT]: port, [attributes_1.EventStoreDBAttributes.DATABASE_SYSTEM]: version_1.INSTRUMENTATION_NAME, [attributes_1.EventStoreDBAttributes.DATABASE_OPERATION]: operation, }; if (options?.credentials) { attributes[attributes_1.EventStoreDBAttributes.DATABASE_USER] = options.credentials.username; } const span = tracer.startSpan(attributes_1.EventStoreDBAttributes.STREAM_APPEND, { kind: api_1.SpanKind.CLIENT, attributes, }); if (Array.isArray(events)) { actualEvents = events; } else { actualEvents = [events]; } const traceId = span.spanContext().traceId; const spanId = span.spanContext().spanId; actualEvents.forEach((event) => { const metadata = (event.metadata = event.metadata || {}); if ((0, utils_1.isJSONEventData)(event) && typeof metadata === "object") { event.metadata = { ...metadata, [TRACE_ID]: traceId, [SPAN_ID]: spanId, }; } }); try { const result = await original.apply(this, [ streamName, actualEvents, options, ]); return result; } catch (error) { throw Instrumentation.handleError(error, span); } finally { span.end(); } }; }; } static applySubscriptionInstrumentation(spanName, subscription, uri, operation, options, tracer) { if (!(0, utils_1.hasConvertGrpcEventMethod)(subscription)) return; const originalConvertGrpcEvent = subscription.convertGrpcEvent; subscription.convertGrpcEvent = function (grpcEvent) { const resolved = originalConvertGrpcEvent.apply(subscription, [ grpcEvent, ]); const resolvedEvent = resolved; const metadata = resolvedEvent?.event?.metadata; if (typeof metadata !== "object" || metadata === null) return resolved; const parentContext = Instrumentation.restoreContext(metadata); const { hostname, port } = Instrumentation.getServerAddress(uri); const subscriptionId = subscription.id; const attributes = { [attributes_1.EventStoreDBAttributes.EVENT_STORE_STREAM]: resolvedEvent?.event?.streamId, [attributes_1.EventStoreDBAttributes.EVENT_STORE_EVENT_ID]: resolvedEvent?.event?.id, [attributes_1.EventStoreDBAttributes.EVENT_STORE_EVENT_TYPE]: resolvedEvent?.event?.type, [attributes_1.EventStoreDBAttributes.EVENT_STORE_SUBSCRIPTION_ID]: subscriptionId, [attributes_1.EventStoreDBAttributes.SERVER_ADDRESS]: hostname, [attributes_1.EventStoreDBAttributes.SERVER_PORT]: port, [attributes_1.EventStoreDBAttributes.DATABASE_SYSTEM]: version_1.INSTRUMENTATION_NAME, [attributes_1.EventStoreDBAttributes.DATABASE_OPERATION]: operation, [attributes_1.EventStoreDBAttributes.DATABASE_USER]: options?.credentials?.username, }; return api_1.context.with(parentContext, () => { const span = tracer.startSpan(spanName, { attributes, kind: api_1.SpanKind.CONSUMER, }); try { return resolved; } catch (error) { throw Instrumentation.handleError(error, span); } finally { span.end(); } }); }; } _patchCatchUpSubscription() { const instrumentation = this; const tracer = instrumentation.tracer; return function subscribe(original, operation) { return function (...args) { let options; if (operation == "subscribeToStream") { options = args[1]; } else { options = args[0]; } const subscription = original.apply(this, args); this.resolveUri().then((uri) => Instrumentation.applySubscriptionInstrumentation(attributes_1.EventStoreDBAttributes.STREAM_SUBSCIBE, subscription, uri, operation, options, tracer)); return subscription; }; }; } _patchPersistentSubscription() { const instrumentation = this; const tracer = instrumentation.tracer; return function subscribe(original, operation) { return function (...args) { let options; if (operation === "subscribeToPersistentSubscriptionToStream") { options = args[2]; } else { options = args[1]; } const subscription = original.apply(this, args); this.resolveUri().then((uri) => Instrumentation.applySubscriptionInstrumentation(attributes_1.EventStoreDBAttributes.STREAM_SUBSCIBE, subscription, uri, operation, options, tracer)); return subscription; }; }; } } exports.Instrumentation = Instrumentation; Instrumentation.restoreContext = (metadata, isRemote = true) => { const traceId = metadata[TRACE_ID]; const spanId = metadata[SPAN_ID]; const parentContext = api_1.trace.setSpanContext(api_1.context.active(), { traceId, spanId, traceFlags: api_1.TraceFlags.SAMPLED, isRemote, }); return parentContext; }; Instrumentation.handleError = (error, span) => { span.recordException(error); span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message }); throw error; }; Instrumentation.getServerAddress = (resolvedUri) => { const uri = new URL(`http://${resolvedUri}`); const hostname = uri.hostname; const port = uri.port; return { hostname, port }; }; //# sourceMappingURL=instrumentation.js.map