lib/instrumentation/modules/mongodb-core.js (174 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');
const { getDBDestination } = require('../context');
var shimmer = require('../shimmer');
var SERVER_FNS = ['insert', 'update', 'remove', 'auth'];
var CURSOR_FNS_FIRST = ['next', '_getmore'];
const firstSpan = Symbol('first-span');
module.exports = function (mongodb, agent, { version, enabled }) {
if (!enabled) return mongodb;
if (!semver.satisfies(version, '>=1.2.19 <4')) {
agent.logger.debug(
'mongodb-core version %s not supported - aborting...',
version,
);
return mongodb;
}
const ins = agent._instrumentation;
if (mongodb.Server) {
agent.logger.debug('shimming mongodb-core.Server.prototype.command');
shimmer.wrap(mongodb.Server.prototype, 'command', wrapCommand);
agent.logger.debug(
'shimming mongodb-core.Server.prototype functions: %j',
SERVER_FNS,
);
shimmer.massWrap(mongodb.Server.prototype, SERVER_FNS, wrapQuery);
}
if (mongodb.Cursor) {
agent.logger.debug(
'shimming mongodb-core.Cursor.prototype functions: %j',
CURSOR_FNS_FIRST,
);
shimmer.massWrap(mongodb.Cursor.prototype, CURSOR_FNS_FIRST, wrapCursor);
}
return mongodb;
function wrapCommand(orig) {
return function wrappedFunction(ns, cmd) {
var trans = agent._instrumentation.currTransaction();
var id = trans && trans.id;
var span;
agent.logger.debug(
'intercepted call to mongodb-core.Server.prototype.command %o',
{ id, ns },
);
if (trans && arguments.length > 0) {
var index = arguments.length - 1;
var cb = arguments[index];
if (typeof cb === 'function') {
var type;
if (cmd.findAndModify) type = 'findAndModify';
else if (cmd.createIndexes) type = 'createIndexes';
else if (cmd.ismaster) type = 'ismaster';
else if (cmd.count) type = 'count';
else type = 'command';
span = ins.createSpan(ns + '.' + type, 'db', 'mongodb', 'query', {
exitSpan: true,
});
if (span) {
span.setDbContext({ type: 'mongodb', instance: ns });
arguments[index] = ins.bindFunctionToRunContext(
ins.currRunContext(),
wrappedCallback,
);
}
}
}
return orig.apply(this, arguments);
function wrappedCallback(_err, commandResult) {
agent.logger.debug(
'intercepted mongodb-core.Server.prototype.command callback %o',
{ id },
);
if (commandResult && commandResult.connection) {
span._setDestinationContext(
getDBDestination(
commandResult.connection.host,
commandResult.connection.port,
),
);
}
span.end();
return cb.apply(this, arguments);
}
};
}
function wrapQuery(orig, name) {
return function wrappedFunction(ns) {
var trans = agent._instrumentation.currTransaction();
var id = trans && trans.id;
var span;
agent.logger.debug(
'intercepted call to mongodb-core.Server.prototype.%s %o',
name,
{ id, ns },
);
if (trans && arguments.length > 0) {
var index = arguments.length - 1;
var cb = arguments[index];
if (typeof cb === 'function') {
span = ins.createSpan(ns + '.' + name, 'db', 'mongodb', 'query', {
exitSpan: true,
});
if (span) {
span.setDbContext({ type: 'mongodb', instance: ns });
arguments[index] = ins.bindFunctionToRunContext(
ins.currRunContext(),
wrappedCallback,
);
}
}
}
return orig.apply(this, arguments);
function wrappedCallback(_err, commandResult) {
agent.logger.debug(
'intercepted mongodb-core.Server.prototype.%s callback %o',
name,
{ id },
);
if (commandResult && commandResult.connection) {
span._setDestinationContext(
getDBDestination(
commandResult.connection.host,
commandResult.connection.port,
),
);
}
span.end();
return cb.apply(this, arguments);
}
};
}
function wrapCursor(orig, name) {
return function wrappedFunction() {
var trans = agent._instrumentation.currTransaction();
var id = trans && trans.id;
var span;
agent.logger.debug(
'intercepted call to mongodb-core.Cursor.prototype.%s %o',
name,
{ id },
);
if (trans && arguments.length > 0) {
var cb = arguments[0];
if (typeof cb === 'function') {
if (name !== 'next' || !this[firstSpan]) {
var spanName = `${this.ns}.${this.cmd.find ? 'find' : name}`;
span = ins.createSpan(spanName, 'db', 'mongodb', 'query', {
exitSpan: true,
});
}
if (span) {
span.setDbContext({ type: 'mongodb', instance: this.ns });
// Limitation: Currently not getting destination address/port for
// cursor calls.
arguments[0] = ins.bindFunctionToRunContext(
ins.currRunContext(),
wrappedCallback,
);
if (name === 'next') {
this[firstSpan] = true;
}
}
}
}
return orig.apply(this, arguments);
function wrappedCallback() {
agent.logger.debug(
'intercepted mongodb-core.Cursor.prototype.%s callback %o',
name,
{ id },
);
span.end();
return cb.apply(this, arguments);
}
};
}
};