UNPKG

@myrotvorets/opentelemetry-plugin-knex

Version:
88 lines (87 loc) 4.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.KnexInstrumentation = void 0; const api_1 = require("@opentelemetry/api"); const instrumentation_1 = require("@opentelemetry/instrumentation"); const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); const connectionattributes_1 = require("./connectionattributes"); const supportedVersions = ['^1.0.0', '^2.0.0', '^3.0.0']; const _STORED_PARENT_SPAN = Symbol.for('opentelemetry.stored-parent-span'); class KnexInstrumentation extends instrumentation_1.InstrumentationBase { static COMPONENT = 'knex'; constructor(config) { super('@myrotvorets/opentelemetry-plugin-knex', '1.0.0', config ?? {}); } init() { const { patch, unpatch } = this.getClientPatches(); return [ new instrumentation_1.InstrumentationNodeModuleDefinition('knex', supportedVersions, undefined, undefined, [ new instrumentation_1.InstrumentationNodeModuleFile('knex/lib/client.js', supportedVersions, patch, unpatch), ]), ]; } getClientPatches() { return { patch: (moduleExports, moduleVersion) => { api_1.diag.debug(`Applying patch for knex@${moduleVersion}`); // istanbul ignore else // eslint-disable-next-line @typescript-eslint/unbound-method if (!(0, instrumentation_1.isWrapped)(moduleExports.prototype.queryBuilder)) { this._massWrap([moduleExports.prototype], ['queryBuilder', 'raw'], this.patchAddParentSpan); this._wrap(moduleExports.prototype, 'query', this.patchQuery); } return moduleExports; }, unpatch: (moduleExports, moduleVersion) => { // istanbul ignore else if (moduleExports !== undefined) { api_1.diag.debug(`Removing patch for knex@${moduleVersion}`); this._massUnwrap([moduleExports.prototype], ['query', 'queryBuilder', 'raw']); } }, }; } static ensureParentSpan(fallback) { const where = fallback; const span = api_1.trace.getSpan(api_1.context.active()) ?? where[_STORED_PARENT_SPAN]; if (span) { where[_STORED_PARENT_SPAN] = span; } return span; } // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/class-methods-use-this patchAddParentSpan = (original) => { return function (...params) { KnexInstrumentation.ensureParentSpan(this); return original.apply(this, params); }; }; patchQuery = (original) => { const self = this; return function (connection, query) { const span = self.createSpan(this, query); return original.call(this, connection, query).then((result) => { span.setStatus({ code: api_1.SpanStatusCode.OK }).end(); return result; }, (e) => { const err = e instanceof Error ? e : new Error(String(e), { cause: e }); span.recordException(err); span.setStatus({ code: api_1.SpanStatusCode.ERROR }).end(); throw err; }); }; }; createSpan(client, query) { const q = typeof query === 'string' ? { sql: query } : query; const parentSpan = KnexInstrumentation.ensureParentSpan(client); return this.tracer.startSpan(q.method ?? q.sql, { kind: api_1.SpanKind.CLIENT, attributes: { [semantic_conventions_1.ATTR_DB_SYSTEM_NAME]: client.driverName, ...new connectionattributes_1.ConnectionAttributes(client.connectionSettings).getAttributes(), [semantic_conventions_1.ATTR_DB_QUERY_TEXT]: q.bindings?.length ? `${q.sql}\nwith [${q.bindings}]` : q.sql, }, }, parentSpan ? api_1.trace.setSpan(api_1.context.active(), parentSpan) : undefined); } } exports.KnexInstrumentation = KnexInstrumentation;