@eventstore/opentelemetry
Version:
[![license][license-badge]][license-badge-url] [![Github action CI workflow][ci-badge]][ci-badge-url]
206 lines • 9.94 kB
JavaScript
;
/* 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