UNPKG

opentelemetry-instrumentation-typeorm

Version:

open telemetry instrumentation for the `typeorm` module

341 lines 17.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeormInstrumentation = void 0; const api_1 = require("@opentelemetry/api"); const core_1 = require("@opentelemetry/core"); const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); const types_1 = require("./types"); const utils_1 = require("./utils"); const version_1 = require("./version"); const instrumentation_1 = require("@opentelemetry/instrumentation"); const is_promise_1 = __importDefault(require("is-promise")); const selectQueryBuilderExecuteMethods = [ 'getRawOne', 'getCount', 'getManyAndCount', 'stream', 'getMany', 'getOneOrFail', 'getOne', 'getRawAndEntities', 'getRawMany', ]; const rawQueryFuncName = 'query'; const functionsUsingEntityPersistExecutor = ['save', 'remove', 'softRemove', 'recover']; const functionsUsingQueryBuilder = [ 'insert', 'update', 'delete', 'softDelete', 'restore', 'count', 'find', 'findAndCount', 'findByIds', 'findOne', 'increment', 'decrement', ]; const entityManagerMethods = [ ...functionsUsingEntityPersistExecutor, ...functionsUsingQueryBuilder, ]; class TypeormInstrumentation extends instrumentation_1.InstrumentationBase { constructor(config = {}) { super('opentelemetry-instrumentation-typeorm', version_1.VERSION, Object.assign({}, config)); } init() { const selectQueryBuilder = new instrumentation_1.InstrumentationNodeModuleFile('typeorm/query-builder/SelectQueryBuilder.js', ['>0.2.28'], (moduleExports, moduleVersion) => { selectQueryBuilderExecuteMethods.map((method) => { var _a; if ((0, instrumentation_1.isWrapped)((_a = moduleExports.SelectQueryBuilder.prototype) === null || _a === void 0 ? void 0 : _a[method])) { this._unwrap(moduleExports.SelectQueryBuilder.prototype, method); } this._wrap(moduleExports.SelectQueryBuilder.prototype, method, this._patchQueryBuilder(moduleVersion)); }); return moduleExports; }, (moduleExports) => { selectQueryBuilderExecuteMethods.map((method) => { var _a; if ((0, instrumentation_1.isWrapped)((_a = moduleExports.SelectQueryBuilder.prototype) === null || _a === void 0 ? void 0 : _a[method])) { this._unwrap(moduleExports.SelectQueryBuilder.prototype, method); } }); return moduleExports; }); const connection = new instrumentation_1.InstrumentationNodeModuleFile('typeorm/connection/Connection.js', ['>0.2.28 <0.3.0'], (moduleExports, moduleVersion) => { var _a; if ((0, instrumentation_1.isWrapped)((_a = moduleExports.Connection.prototype) === null || _a === void 0 ? void 0 : _a[rawQueryFuncName])) { this._unwrap(moduleExports.Connection.prototype, rawQueryFuncName); } this._wrap(moduleExports.Connection.prototype, rawQueryFuncName, this._patchRawQuery(moduleVersion)); return moduleExports; }, (moduleExports) => { var _a; if ((0, instrumentation_1.isWrapped)((_a = moduleExports.Connection.prototype) === null || _a === void 0 ? void 0 : _a[rawQueryFuncName])) { this._unwrap(moduleExports.Connection.prototype, rawQueryFuncName); } return moduleExports; }); const dataSource = new instrumentation_1.InstrumentationNodeModuleFile('typeorm/data-source/DataSource.js', ['>=0.3.0'], (moduleExports, moduleVersion) => { var _a; if ((0, instrumentation_1.isWrapped)((_a = moduleExports.DataSource.prototype) === null || _a === void 0 ? void 0 : _a[rawQueryFuncName])) { this._unwrap(moduleExports.DataSource.prototype, rawQueryFuncName); } this._wrap(moduleExports.DataSource.prototype, rawQueryFuncName, this._patchRawQuery(moduleVersion)); return moduleExports; }, (moduleExports) => { var _a; if ((0, instrumentation_1.isWrapped)((_a = moduleExports.DataSource.prototype) === null || _a === void 0 ? void 0 : _a[rawQueryFuncName])) { this._unwrap(moduleExports.DataSource.prototype, rawQueryFuncName); } return moduleExports; }); const entityManager = new instrumentation_1.InstrumentationNodeModuleFile('typeorm/entity-manager/EntityManager.js', ['>0.2.28'], (moduleExports, moduleVersion) => { entityManagerMethods.map((method) => { var _a; if ((0, instrumentation_1.isWrapped)((_a = moduleExports.EntityManager.prototype) === null || _a === void 0 ? void 0 : _a[method])) { this._unwrap(moduleExports.EntityManager.prototype, method); } this._wrap(moduleExports.EntityManager.prototype, method, this._patchEntityManagerFunction(method, moduleVersion)); }); return moduleExports; }, (moduleExports) => { entityManagerMethods.map((method) => { var _a; if ((0, instrumentation_1.isWrapped)((_a = moduleExports.EntityManager.prototype) === null || _a === void 0 ? void 0 : _a[method])) { this._unwrap(moduleExports.EntityManager.prototype, method); } }); return moduleExports; }); const module = new instrumentation_1.InstrumentationNodeModuleDefinition('typeorm', ['>0.2.28'], null, null, [ selectQueryBuilder, entityManager, connection, dataSource, ]); return module; } _patchEntityManagerFunction(opName, moduleVersion) { const self = this; api_1.diag.debug(`typeorm instrumentation: patched EntityManager ${opName} prototype`); return (original) => { return function (...args) { var _a, _b, _c; if ((0, utils_1.isTypeormInternalTracingSuppressed)(api_1.context.active())) { return original.apply(this, arguments); } const connectionOptions = (_b = (_a = this === null || this === void 0 ? void 0 : this.connection) === null || _a === void 0 ? void 0 : _a.options) !== null && _b !== void 0 ? _b : {}; const attributes = { [semantic_conventions_1.SEMATTRS_DB_SYSTEM]: connectionOptions.type, [semantic_conventions_1.SEMATTRS_DB_USER]: connectionOptions.username, [semantic_conventions_1.SEMATTRS_NET_PEER_NAME]: connectionOptions.host, [semantic_conventions_1.SEMATTRS_NET_PEER_PORT]: connectionOptions.port, [semantic_conventions_1.SEMATTRS_DB_NAME]: connectionOptions.database, [semantic_conventions_1.SEMATTRS_DB_OPERATION]: opName, [semantic_conventions_1.SEMATTRS_DB_STATEMENT]: JSON.stringify(buildStatement(original, args)), }; if (self._config.moduleVersionAttributeName && moduleVersion) { attributes[self._config.moduleVersionAttributeName] = moduleVersion; } //ignore EntityMetadataNotFoundError try { if (this.metadata) { attributes[semantic_conventions_1.SEMATTRS_DB_SQL_TABLE] = this.metadata.tableName; } else { const entity = args[0]; const name = typeof entity === 'object' ? (_c = entity === null || entity === void 0 ? void 0 : entity.constructor) === null || _c === void 0 ? void 0 : _c.name : entity; const metadata = this.connection.getMetadata(name); if (metadata === null || metadata === void 0 ? void 0 : metadata.tableName) { attributes[semantic_conventions_1.SEMATTRS_DB_SQL_TABLE] = metadata.tableName; } } } catch (_d) { } Object.entries(attributes).forEach(([key, value]) => { if (value === undefined) delete attributes[key]; }); const span = self.tracer.startSpan(`TypeORM ${opName}`, { kind: api_1.SpanKind.CLIENT, attributes, }); const contextWithSpan = api_1.trace.setSpan(api_1.context.active(), span); const traceContext = self._config.enableInternalInstrumentation ? contextWithSpan : (0, utils_1.suppressTypeormInternalTracing)(contextWithSpan); const contextWithSuppressTracing = self._config.suppressInternalInstrumentation ? (0, core_1.suppressTracing)(traceContext) : traceContext; return api_1.context.with(contextWithSuppressTracing, () => self._endSpan(() => original.apply(this, arguments), span)); }; }; } _patchQueryBuilder(moduleVersion) { const self = this; return (original) => { return function () { var _a, _b; if ((0, utils_1.isTypeormInternalTracingSuppressed)(api_1.context.active())) { return original.apply(this, arguments); } const queryBuilder = this; const sql = queryBuilder.getQuery(); const parameters = queryBuilder.getParameters(); const mainTableName = this.getMainTableName(); const operation = queryBuilder.expressionMap.queryType; const connectionOptions = (_a = queryBuilder === null || queryBuilder === void 0 ? void 0 : queryBuilder.connection) === null || _a === void 0 ? void 0 : _a.options; const attributes = { [semantic_conventions_1.SEMATTRS_DB_SYSTEM]: connectionOptions.type, [semantic_conventions_1.SEMATTRS_DB_USER]: connectionOptions.username, [semantic_conventions_1.SEMATTRS_NET_PEER_NAME]: connectionOptions.host, [semantic_conventions_1.SEMATTRS_NET_PEER_PORT]: connectionOptions.port, [semantic_conventions_1.SEMATTRS_DB_NAME]: connectionOptions.database, [semantic_conventions_1.SEMATTRS_DB_OPERATION]: operation, [semantic_conventions_1.SEMATTRS_DB_STATEMENT]: sql, [semantic_conventions_1.SEMATTRS_DB_SQL_TABLE]: mainTableName, }; if (self._config.collectParameters) { try { attributes[types_1.ExtendedDatabaseAttribute.DB_STATEMENT_PARAMETERS] = JSON.stringify(parameters); } catch (err) { } } const span = self.tracer.startSpan(`TypeORM ${operation} ${mainTableName}`, { kind: api_1.SpanKind.CLIENT, attributes, }); const contextWithSpan = api_1.trace.setSpan(api_1.context.active(), span); const traceContext = self._config.enableInternalInstrumentation ? contextWithSpan : (0, utils_1.suppressTypeormInternalTracing)(contextWithSpan); const contextWithSuppressTracing = ((_b = self._config) === null || _b === void 0 ? void 0 : _b.suppressInternalInstrumentation) ? (0, core_1.suppressTracing)(traceContext) : traceContext; return api_1.context.with(contextWithSuppressTracing, () => self._endSpan(() => original.apply(this, arguments), span)); }; }; } getOperationName(statement) { let operation = 'raw query'; if (typeof statement === 'string') { statement = statement.trim(); try { operation = statement.split(' ')[0].toUpperCase(); } catch (e) { } } return operation; } _patchRawQuery(moduleVersion) { const self = this; return (original) => { return function () { var _a; if ((0, utils_1.isTypeormInternalTracingSuppressed)(api_1.context.active())) { return original.apply(this, arguments); } const conn = this; const sql = arguments[0]; const operation = self.getOperationName(sql); const connectionOptions = conn.options; const attributes = { [semantic_conventions_1.SEMATTRS_DB_SYSTEM]: connectionOptions.type, [semantic_conventions_1.SEMATTRS_DB_USER]: connectionOptions.username, [semantic_conventions_1.SEMATTRS_NET_PEER_NAME]: connectionOptions.host, [semantic_conventions_1.SEMATTRS_NET_PEER_PORT]: connectionOptions.port, [semantic_conventions_1.SEMATTRS_DB_NAME]: connectionOptions.database, [semantic_conventions_1.SEMATTRS_DB_OPERATION]: operation, [semantic_conventions_1.SEMATTRS_DB_STATEMENT]: sql, }; const span = self.tracer.startSpan(`TypeORM ${operation}`, { kind: api_1.SpanKind.CLIENT, attributes, }); const contextWithSpan = api_1.trace.setSpan(api_1.context.active(), span); const traceContext = self._config.enableInternalInstrumentation ? contextWithSpan : (0, utils_1.suppressTypeormInternalTracing)(contextWithSpan); const contextWithSuppressTracing = ((_a = self._config) === null || _a === void 0 ? void 0 : _a.suppressInternalInstrumentation) ? (0, core_1.suppressTracing)(traceContext) : traceContext; return api_1.context.with(contextWithSuppressTracing, () => self._endSpan(() => original.apply(this, arguments), span)); }; }; } _endSpan(traced, span) { const executeResponseHook = (response) => { var _a; if ((_a = this._config) === null || _a === void 0 ? void 0 : _a.responseHook) { (0, instrumentation_1.safeExecuteInTheMiddle)(() => this._config.responseHook(span, response), (e) => { if (e) api_1.diag.error('typeorm instrumentation: responseHook error', e); }, true); } return response; }; try { const response = traced(); if ((0, is_promise_1.default)(response)) { return Promise.resolve(response) .then((response) => executeResponseHook(response)) .catch((err) => { if (err) { if (typeof err === 'string') { span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err }); } else { span.recordException(err); span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err === null || err === void 0 ? void 0 : err.message }); } } throw err; }) .finally(() => span.end()); } else { span.end(); return executeResponseHook(response); } } catch (error) { span.recordException(error); span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error === null || error === void 0 ? void 0 : error.message }); span.end(); throw error; } } } exports.TypeormInstrumentation = TypeormInstrumentation; const buildStatement = (func, args) => { const paramNames = (0, utils_1.getParamNames)(func); const statement = {}; paramNames.forEach((pName, i) => { var _a; const value = args[i]; if (!value) return; try { const stringified = JSON.stringify(value); if (stringified) { statement[pName] = args[i]; return; } } catch (err) { } if (value === null || value === void 0 ? void 0 : value.name) { statement[pName] = value.name; return; } if ((_a = value === null || value === void 0 ? void 0 : value.constructor) === null || _a === void 0 ? void 0 : _a.name) { statement[pName] = value.constructor.name; } }); return statement; }; //# sourceMappingURL=typeorm.js.map