lib/instrumentation/modules/tedious.js (130 lines of code) (raw):
/*
* Copyright Elasticsearch B.V. and other contributors where applicable.
* Licensed under the BSD 2-Clause License; you may not use this file except in
* compliance with the BSD 2-Clause License.
*/
'use strict';
var semver = require('semver');
var clone = require('shallow-clone-shim');
var sqlSummary = require('sql-summary');
var { getDBDestination } = require('../context');
module.exports = function (tedious, agent, { version, enabled }) {
if (!enabled) return tedious;
if (
semver.satisfies(version, '>=19') &&
!semver.satisfies(process.version, '>=18.17')
) {
agent.logger.debug(
'tedious version %s not supported for node %s - aborting...',
version,
process.version,
);
return tedious;
}
if (version === '4.0.0' || !semver.satisfies(version, '>=1.9.0 <20')) {
agent.logger.debug(
'tedious version %s not supported - aborting...',
version,
);
return tedious;
}
const ins = agent._instrumentation;
return clone({}, tedious, {
Connection(descriptor) {
const getter = descriptor.get;
if (getter) {
// tedious v6.5.0+
descriptor.get = function get() {
return wrapConnection(getter());
};
} else if (typeof descriptor.value === 'function') {
descriptor.value = wrapConnection(descriptor.value);
} else {
agent.logger.debug(
'could not patch `tedious.Connection` property for tedious version %s - aborting...',
version,
);
}
return descriptor;
},
Request(descriptor) {
const getter = descriptor.get;
if (getter) {
// tedious v6.5.0+
descriptor.get = function get() {
return wrapRequest(getter());
};
} else if (typeof descriptor.value === 'function') {
descriptor.value = wrapRequest(descriptor.value);
} else {
agent.logger.debug(
'could not patch `tedious.Request` property for tedious version %s - aborting...',
version,
);
}
return descriptor;
},
});
function wrapRequest(OriginalRequest) {
class Request extends OriginalRequest {
constructor() {
super(...arguments);
ins.bindEmitter(this);
}
}
return Request;
}
function wrapConnection(OriginalConnection) {
class Connection extends OriginalConnection {
constructor() {
super(...arguments);
ins.bindEmitter(this);
}
makeRequest(request, _packetType, payload) {
// if not a Request object (i.e. a BulkLoad), then bail
if (!request.parametersByName) {
return super.makeRequest(...arguments);
}
const span = ins.createSpan(null, 'db', 'mssql', 'query', {
exitSpan: true,
});
if (!span) {
return super.makeRequest(...arguments);
}
let host, port, instanceName;
if (typeof this.config === 'object') {
// http://tediousjs.github.io/tedious/api-connection.html#function_newConnection
host = this.config.server;
if (this.config.options) {
port = this.config.options.port;
instanceName = this.config.options.instanceName;
}
}
span._setDestinationContext(getDBDestination(host, port));
let sql;
let preparing;
if (payload.parameters !== undefined) {
// This looks for tedious instance with `RpcRequestPayload` started
// since version >=v11.0.10, when RPC parameter handling was refactored
// (https://github.com/tediousjs/tedious/pull/1275).
preparing =
typeof payload.procedure === 'number'
? // tedious@16.2.0 starts using stored procedure *IDs*
// (https://github.com/tediousjs/tedious/pull/1327)
payload.procedure === 11
: payload.procedure === 'sp_prepare';
const stmtParam =
payload.parameters.find(({ name }) => name === 'statement') ||
payload.parameters.find(({ name }) => name === 'stmt');
sql = stmtParam ? stmtParam.value : request.sqlTextOrProcedure;
} else {
preparing = request.sqlTextOrProcedure === 'sp_prepare';
const params = request.parametersByName;
sql = (params.statement || params.stmt || {}).value;
}
span.name = sqlSummary(sql) + (preparing ? ' (prepare)' : '');
const dbContext = { type: 'sql', statement: sql };
if (instanceName) {
dbContext.instance = instanceName;
}
span.setDbContext(dbContext);
const origCallback = request.userCallback;
request.userCallback = ins.bindFunction(function tracedCallback() {
// TODO: captureError and setOutcome on err first arg here
span.end();
if (origCallback) {
return origCallback.apply(this, arguments);
}
});
return super.makeRequest(...arguments);
}
}
return Connection;
}
};