Repository URL to install this package:
|
Version:
0.9.2-rc0 ▾
|
/*
* (c) Copyright IBM Corp. 2021
* (c) Copyright Instana Inc. and contributors 2016
*/
'use strict';
/**
* @typedef {import('../../clsHooked/context').InstanaCLSContext} InstanaCLSContext
* @typedef {import('@supertenant/superbrain/lib/binding.cjs').JSOpenSpanResult} JSOpenSpanResult
* @typedef {import('@supertenant/superbrain/types/binding.cjs').SpanLabels} SpanLabels
* @typedef {Error & { ok?: number, code?: number, codeName?: string, message?: string }} MongodbError
* @typedef {{
* Connection?: {
* prototype: any
* }
* }} CmapConnection
*/
const shimmer = require('shimmer');
const requireHook = require('../../../util/requireHook');
const tracingUtil = require('../../tracingUtil');
const constants = require('../../constants');
const cls = require('../../cls');
const { superbrain } = require('@supertenant/superbrain');
const superconsts = require('@supertenant/superconsts');
const { getOrCreateTask } = require('../../taskManager');
const { getWaitPollInterval, getMongodbRejectActionResult, getDelayDuration } = require('../../actions');
const { dedupReportError } = require('../../../logger');
// Create a mapping from code to codeName to mimic mongodb errors.
const DefaultErrorConstants = {
11601: 'Interrupted',
262: 'ExceededTimeLimit'
};
let ErrorConstants = {};
Object.assign(ErrorConstants, DefaultErrorConstants);
let isActive = true;
const commands = [
//
'aggregate',
'count',
'delete',
'distinct',
'find',
'findAndModify',
'findandmodify',
'getMore',
'getmore',
'insert',
'update'
];
exports.spanName = 'mongo';
exports.batchable = true;
exports.init = function init() {
// unified topology layer
requireHook.onFileLoad(/\/mongodb\/lib\/cmap\/connection.js/, instrumentCmapConnection);
// mongodb >= 3.3.x, legacy topology layer
requireHook.onFileLoad(/\/mongodb\/lib\/core\/connection\/pool.js/, instrumentLegacyTopologyPool);
// mongodb < 3.3.x, legacy topology layer
requireHook.onFileLoad(/\/mongodb-core\/lib\/connection\/pool.js/, instrumentLegacyTopologyPool);
};
/**
* @param {CmapConnection} connection
*/
function instrumentCmapConnection(connection) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumenting mongodb cmap connection');
if (connection.Connection && connection.Connection.prototype) {
// collection.findOne, collection.find et al.
shimmer.wrap(connection.Connection.prototype, 'query', shimCmapQuery);
// collection.count et al.
shimmer.wrap(connection.Connection.prototype, 'command', shimCmapCommand);
[
'insert', // collection.insertOne et al.
'update', // collection.replaceOne et al.
'remove' // collection.delete et al.
].forEach(fnName => {
if (connection.Connection.prototype[fnName]) {
shimmer.wrap(connection.Connection.prototype, fnName, shimCmapMethod);
} else {
superbrain.log(superconsts.BrainLogLevel.Debug, `instrumentCmapConnection: no ${fnName} found`);
}
});
shimmer.wrap(connection.Connection.prototype, 'getMore', shimCmapGetMore);
} else {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentCmapConnection: no Connection.prototype found');
}
}
function shimCmapQuery(original) {
return function tmp() {
if (cls.skipExitTracing({ isActive })) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'shimCmapQuery: skipping exit tracing');
return original.apply(this, arguments);
}
const originalArgs = new Array(arguments.length);
for (let i = 0; i < arguments.length; i++) {
originalArgs[i] = arguments[i];
}
return instrumentedCmapQuery(this, original, originalArgs);
};
}
function shimCmapCommand(original) {
return function () {
if (cls.skipExitTracing({ isActive })) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'shimCmapCommand: skipping exit tracing');
return original.apply(this, arguments);
}
const command = arguments[1] && commands.find(c => arguments[1][c]);
if (!command) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'shimCmapCommand: no command found');
return original.apply(this, arguments);
}
const originalArgs = new Array(arguments.length);
for (let i = 0; i < arguments.length; i++) {
originalArgs[i] = arguments[i];
}
return instrumentedCmapMethod(this, original, originalArgs, command);
};
}
function shimCmapMethod(original) {
return function () {
if (cls.skipExitTracing({ isActive })) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'shimCmapMethod: skipping exit tracing');
return original.apply(this, arguments);
}
const originalArgs = new Array(arguments.length);
for (let i = 0; i < arguments.length; i++) {
originalArgs[i] = arguments[i];
}
return instrumentedCmapMethod(this, original, originalArgs, original.name);
};
}
function shimCmapGetMore(original) {
return function () {
if (cls.skipExitTracing({ isActive })) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'shimCmapGetMore: skipping exit tracing');
return original.apply(this, arguments);
}
const originalArgs = new Array(arguments.length);
for (let i = 0; i < arguments.length; i++) {
originalArgs[i] = arguments[i];
}
return instrumentedCmapGetMore(this, original, originalArgs);
};
}
/**
* @param {InstanaCLSContext} ctx
*/
function instrumentedCmapQuery(ctx, originalQuery, originalArgs) {
const { originalCallback, callbackIndex } = findCallback(originalArgs);
if (callbackIndex < 0) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapQuery: no callback found');
return originalQuery.apply(ctx, originalArgs);
}
return cls.ns.runAndReturn(() => {
const span = cls.startSpan(exports.spanName, constants.EXIT);
const namespace = originalArgs[0];
const splitNamespace = dbAndCollectionFromNamespace(namespace);
const cmd = originalArgs[1];
/** @type{string?} */
let command;
if (cmd) {
command = findCommand(cmd);
}
let { service, hostnameAndPort } = getServiceHostnameAndPortFromCtx(ctx);
let spanData = {
command,
service,
namespace
};
span.data.peer = hostnameAndPort;
span.data.mongo = spanData;
const jsonAndFilter = readJsonOrFilter(cmd, span);
const json = jsonAndFilter.stringJson;
const filter = jsonAndFilter.filter;
/** @type {JSOpenSpanResult} */
let openSpanResult = null;
const stTaskId = getOrCreateTask();
if (stTaskId !== 0) {
/** @type{import('@supertenant/superbrain/types/binding.cjs').SpanLabels} */
const stSpanData = {};
stSpanData[superconsts.Label.SupertenantResourceType] = superconsts.ResourceType.Mongodb;
stSpanData[superconsts.Label.IntegrationModuleResourceId] = hostnameAndPort.hostname;
stSpanData[superconsts.Label.DbDatabase] = splitNamespace.db;
stSpanData[superconsts.Label.DbMongodbCollection] = splitNamespace.collection;
stSpanData[superconsts.Label.DbHost] = hostnameAndPort.hostname;
stSpanData[superconsts.Label.DbPort] = hostnameAndPort.port.toString();
stSpanData[superconsts.Label.DbCommand] = command;
stSpanData[superconsts.Label.DbMongodbCommandJson] = json;
stSpanData[superconsts.Label.DbMongodbFilter] = filter;
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapQuery: openSpan');
openSpanResult = superbrain.openSpan(stTaskId, superconsts.SpanType.ClientRequest, stSpanData);
} else {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapQuery: no stTaskId');
}
const stCallback = createWrappedCallback(span, openSpanResult, originalCallback);
originalArgs[callbackIndex] = stCallback;
if (openSpanResult) {
callbackHandleAction(openSpanResult, ctx, originalQuery, originalArgs, stCallback);
} else {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapQuery: no openSpanResult');
return originalQuery.apply(ctx, originalArgs);
}
});
}
/**
*
* @param {InstanaCLSContext} ctx
* @param {string} command
*/
function instrumentedCmapMethod(ctx, originalMethod, originalArgs, command) {
const { originalCallback, callbackIndex } = findCallback(originalArgs);
if (callbackIndex < 0) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapMethod: no callback found');
return originalMethod.apply(ctx, originalArgs);
}
return cls.ns.runAndReturn(() => {
const span = cls.startSpan(exports.spanName, constants.EXIT);
const namespace = originalArgs[0];
const splitNamespace = dbAndCollectionFromNamespace(namespace);
let { service, hostnameAndPort } = getServiceHostnameAndPortFromCtx(ctx);
let spanData = {
command,
service,
namespace
};
span.data.peer = hostnameAndPort;
span.data.mongo = spanData;
let json;
let filter;
// TODO: remove the not-insert check?
if (command && command.indexOf('insert') < 0) {
// we do not capture the document for insert commands
const jsonAndFilter = readJsonOrFilter(originalArgs[1], span);
json = jsonAndFilter.stringJson;
filter = jsonAndFilter.filter;
}
/** @type {JSOpenSpanResult} */
let openSpanResult = null;
const stTaskId = getOrCreateTask();
if (stTaskId !== 0) {
/** @type{SpanLabels} */
const stSpanData = {};
stSpanData[superconsts.Label.SupertenantResourceType] = superconsts.ResourceType.Mongodb;
stSpanData[superconsts.Label.IntegrationModuleResourceId] = hostnameAndPort.hostname;
stSpanData[superconsts.Label.DbDatabase] = splitNamespace.db;
stSpanData[superconsts.Label.DbMongodbCollection] = splitNamespace.collection;
stSpanData[superconsts.Label.DbHost] = hostnameAndPort.hostname;
stSpanData[superconsts.Label.DbPort] = hostnameAndPort.port.toString();
stSpanData[superconsts.Label.DbCommand] = command;
stSpanData[superconsts.Label.DbMongodbCommandJson] = json;
stSpanData[superconsts.Label.DbMongodbFilter] = filter;
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapMethod: openSpan');
openSpanResult = superbrain.openSpan(stTaskId, superconsts.SpanType.ClientRequest, stSpanData);
} else {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapMethod: no stTaskId');
}
const stCallback = createWrappedCallback(span, openSpanResult, originalCallback);
originalArgs[callbackIndex] = stCallback;
if (openSpanResult) {
callbackHandleAction(openSpanResult, ctx, originalMethod, originalArgs, stCallback);
} else {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapMethod: no openSpanResult');
return originalMethod.apply(ctx, originalArgs);
}
});
}
function instrumentedCmapGetMore(ctx, originalMethod, originalArgs) {
const { originalCallback, callbackIndex } = findCallback(originalArgs);
if (callbackIndex < 0) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapGetMore: no callback found');
return originalMethod.apply(ctx, originalArgs);
}
return cls.ns.runAndReturn(() => {
const span = cls.startSpan(exports.spanName, constants.EXIT);
const namespace = originalArgs[0];
const splitNamespace = dbAndCollectionFromNamespace(namespace);
let { service, hostnameAndPort } = getServiceHostnameAndPortFromCtx(ctx);
let spanData = {
command: 'getMore',
service,
namespace
};
span.data.peer = hostnameAndPort;
span.data.mongo = spanData;
/** @type {JSOpenSpanResult} */
let openSpanResult = null;
const stTaskId = getOrCreateTask();
if (stTaskId !== 0) {
/** @type{SpanLabels} */
const stSpanData = {};
stSpanData[superconsts.Label.SupertenantResourceType] = superconsts.ResourceType.Mongodb;
stSpanData[superconsts.Label.IntegrationModuleResourceId] = hostnameAndPort.hostname;
stSpanData[superconsts.Label.DbDatabase] = splitNamespace.db;
stSpanData[superconsts.Label.DbMongodbCollection] = splitNamespace.collection;
stSpanData[superconsts.Label.DbHost] = hostnameAndPort.hostname;
stSpanData[superconsts.Label.DbPort] = hostnameAndPort.port;
stSpanData[superconsts.Label.DbCommand] = 'getMore';
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapGetMore: openSpan');
openSpanResult = superbrain.openSpan(stTaskId, superconsts.SpanType.ClientRequest, stSpanData);
} else {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapGetMore: no stTaskId');
}
const stCallback = createWrappedCallback(span, openSpanResult, originalCallback);
originalArgs[callbackIndex] = stCallback;
if (openSpanResult) {
callbackHandleAction(openSpanResult, ctx, originalMethod, originalArgs, stCallback);
} else {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedCmapGetMore: no openSpanResult');
return originalMethod.apply(ctx, originalArgs);
}
});
}
function instrumentLegacyTopologyPool(Pool) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumenting mongodb legacy topology pool');
shimmer.wrap(Pool.prototype, 'write', shimLegacyWrite);
}
function shimLegacyWrite(original) {
return function () {
if (cls.skipExitTracing({ isActive })) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'shimLegacyWrite: skipping exit tracing');
return original.apply(this, arguments);
}
const originalArgs = new Array(arguments.length);
for (let i = 0; i < arguments.length; i++) {
originalArgs[i] = arguments[i];
}
return instrumentedLegacyWrite(this, original, originalArgs);
};
}
function instrumentedLegacyWrite(ctx, originalWrite, originalArgs) {
// pool.js#write throws a sync error if there is no callback, so we can safely assume there is one. If there was no
// callback, we wouldn't be able to finish the span, so we won't start one.
const { originalCallback, callbackIndex } = findCallback(originalArgs);
if (callbackIndex < 0) {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedLegacyWrite: no callback found');
return originalWrite.apply(ctx, originalArgs);
}
return cls.ns.runAndReturn(() => {
const span = cls.startSpan(exports.spanName, constants.EXIT);
let hostname;
let port;
let service;
let command;
let database;
let collection;
let namespace;
const message = originalArgs[0];
if (message && typeof message === 'object') {
if (
message.options &&
message.options.session &&
message.options.session.topology &&
message.options.session.topology.s &&
message.options.session.topology.s
) {
hostname = message.options.session.topology.s.host;
port = message.options.session.topology.s.port;
}
if ((!hostname || !port) && ctx.options) {
// fallback for older versions of mongodb package
if (!hostname) {
hostname = ctx.options.host;
}
if (!port) {
port = ctx.options.port;
}
}
let cmdObj = message.command;
if (!cmdObj) {
// fallback for older mongodb versions
cmdObj = message.query;
}
if (cmdObj) {
if (cmdObj.collection) {
// only getMore commands have the collection attribute
collection = cmdObj.collection;
}
if (!collection) {
collection = findCollection(cmdObj);
}
command = findCommand(cmdObj);
database = cmdObj.$db;
}
if (!database && typeof message.ns === 'string') {
// fallback for older mongodb versions
database = message.ns.split('.')[0];
}
}
if (database && collection) {
namespace = `${database}.${collection}`;
} else if (database) {
namespace = `${database}.?`;
} else if (collection) {
namespace = `?.${collection}`;
}
if (hostname || port) {
span.data.peer = {
hostname,
port
};
}
if (hostname && port) {
service = `${hostname}:${port}`;
} else if (hostname) {
service = `${hostname}:27017`;
} else if (port) {
service = '?:27017';
}
let spanData = {
command,
service,
namespace
};
span.data.mongo = spanData;
let json;
let filter;
const jsonAndFilter = readJsonOrFilterFromMessage(message, span);
if (jsonAndFilter) {
json = jsonAndFilter.stringJson;
filter = jsonAndFilter.filter;
}
/** @type {JSOpenSpanResult} */
let openSpanResult = null;
const stTaskId = getOrCreateTask();
if (stTaskId !== 0) {
/** @type{SpanLabels} */
const stSpanData = {};
stSpanData[superconsts.Label.SupertenantResourceType] = superconsts.ResourceType.Mongodb;
stSpanData[superconsts.Label.IntegrationModuleResourceId] = hostname;
stSpanData[superconsts.Label.DbDatabase] = database;
stSpanData[superconsts.Label.DbMongodbCollection] = collection;
stSpanData[superconsts.Label.DbHost] = hostname;
stSpanData[superconsts.Label.DbPort] = port;
stSpanData[superconsts.Label.DbCommand] = command;
stSpanData[superconsts.Label.DbMongodbCommandJson] = json;
stSpanData[superconsts.Label.DbMongodbFilter] = filter;
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedLegacyWrite: openSpan');
openSpanResult = superbrain.openSpan(stTaskId, superconsts.SpanType.ClientRequest, stSpanData);
} else {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedLegacyWrite: no stTaskId');
}
const stCallback = createWrappedCallback(span, openSpanResult, originalCallback);
originalArgs[callbackIndex] = stCallback;
if (openSpanResult) {
callbackHandleAction(openSpanResult, ctx, originalWrite, originalArgs, stCallback);
} else {
superbrain.log(superconsts.BrainLogLevel.Debug, 'instrumentedLegacyWrite: no openSpanResult');
return originalWrite.apply(ctx, originalArgs);
}
});
}
/**
* @param {JSOpenSpanResult} openSpanResult
*/
function callbackHandleAction(openSpanResult, ctx, originalFunction, originalArgs, resultCallback, firstCall = true) {
let actionRef;
if (firstCall) {
actionRef = superbrain.getAction(openSpanResult.action);
} else {
actionRef = superbrain.pollSpanAction(openSpanResult.openSpanResult);
}
if (actionRef.Action === superconsts.Action.Execute) {
if (!openSpanResult.canceled) {
openSpanResult.executed = true;
originalFunction.apply(ctx, originalArgs);
}
return;
}
if (actionRef.Action === superconsts.Action.Wait) {
let pollInterval = getWaitPollInterval(actionRef);
setTimeout(cls.ns.bind(function () {
callbackHandleAction(openSpanResult, ctx, originalFunction, originalArgs, resultCallback, false);
}), pollInterval).unref();
return;
}
if (actionRef.Action === superconsts.Action.Delay) {
const delayDuration = getDelayDuration(actionRef);
return new Promise((resolve) => setTimeout(resolve, delayDuration).unref()).then(cls.ns.bind(() => {
return function () {
if (!openSpanResult.canceled) {
openSpanResult.executed = true;
originalFunction.apply(ctx, originalArgs);
}
}();
}));
}
if (actionRef.Action === superconsts.Action.Reject) {
resultCallback(rejectToError(getMongodbRejectActionResult(actionRef)));
} else {
dedupReportError('httpServer:UNKNOWN_ACTION', 'received unknown action ID', actionRef);
originalFunction.apply(ctx, originalArgs);
}
}
/**
*
* @param {import('../../actions').MongodbRejectActionResult} rejectActionResult
* @returns {MongodbError}
*/
function rejectToError(rejectActionResult) {
/** @type{MongodbError} */
let err = new Error(rejectActionResult.error_message);
err.ok = 0;
err.code = rejectActionResult.code;
err.codeName = ErrorConstants[rejectActionResult.code];
err.message = rejectActionResult.error_message;
return err;
}
function findCallback(originalArgs) {
let originalCallback;
let callbackIndex = -1;
for (let i = 1; i < originalArgs.length; i++) {
if (typeof originalArgs[i] === 'function') {
originalCallback = originalArgs[i];
callbackIndex = i;
break;
}
}
return {
originalCallback,
callbackIndex
};
}
function findCollection(cmdObj) {
for (let j = 0; j < commands.length; j++) {
if (cmdObj[commands[j]] && typeof cmdObj[commands[j]] === 'string') {
// most commands (except for getMore) add the collection as the value for the command-specific key
return cmdObj[commands[j]];
}
}
}
function findCommand(cmdObj) {
for (let j = 0; j < commands.length; j++) {
if (cmdObj[commands[j]]) {
return commands[j];
}
}
}
/**
* @param {InstanaCLSContext} ctx
*/
function getServiceHostnameAndPortFromCtx(ctx) {
let address = '';
let hostname = '';
let port = '';
if (ctx.host) {
address = ctx.port ? `${ctx.host}:${ctx.port}` : ctx.host;
hostname = ctx.host;
port = ctx.port || '';
} else if (ctx.address) {
const hostnameAndPort = splitIntoHostAndPort(ctx.address);
return { address: ctx.address, hostnameAndPort };
}
return { address, hostnameAndPort: { hostname, port } };
}
function splitIntoHostAndPort(address) {
let hostname = '';
let port = '';
if (typeof address === 'string') {
// address can be IPv6, which may contain a lot of colons.
// Unfortunately, there's no good way to tell if we're using IPv4/DNS or IPv6.
// when ports are also involved, so we use the following heuristic:
// 1. If we have [ then it should be [ipv6]:port format.
// 2. If we have more than one colon then it's ipv6 w/o port.
// 3. IPv4/DNS:
// 3.1. If we have a colon then it's ipv4/DNS w/ port.
// 3.2. IPv4/DNS w/o port.
hostname = address;
if (address[0] === '[') {
// case 1: IPv6 []:port
const closingBracketIdx = address.lastIndexOf(']');
if (closingBracketIdx >= 0) {
const lastColon = address.lastIndexOf(':');
if (lastColon > closingBracketIdx) {
port = address.substring(lastColon + 1);
}
hostname = address.substring(1, closingBracketIdx);
} else {
// this is a bug in the mongodb driver (address: "[xxxx"), so we just copy
// address as the hostname (already set).
}
} else {
const idx = address.indexOf(':');
if (idx >= 0) {
const idx2 = address.indexOf(':', idx + 1);
if (idx2 < 0) {
// case 3.1: IPv4/DNS + port
hostname = address.substring(0, idx);
port = address.substring(idx + 1);
} // else case 2: IPv6 w/o port (hostname is already set to address)
} // else case 3.2: IPv4/DNS w/o port (hostname is already set to address)
}
} else {
dedupReportError('mongodb:splitIntoHostAndPort', 'address is not a string', address);
}
return { hostname, port };
}
function readJsonOrFilterFromMessage(message, span) {
if (!message) {
return;
}
let cmdObj = message.command;
if (!cmdObj) {
cmdObj = message.query;
}
if (!cmdObj) {
return;
}
return readJsonOrFilter(cmdObj, span);
}
function readJsonOrFilter(cmdObj, span) {
let json;
if (Array.isArray(cmdObj) && cmdObj.length >= 1) {
json = cmdObj;
} else if (Array.isArray(cmdObj.updates) && cmdObj.updates.length >= 1) {
json = cmdObj.updates;
} else if (Array.isArray(cmdObj.deletes) && cmdObj.deletes.length >= 1) {
json = cmdObj.deletes;
} else if (Array.isArray(cmdObj.pipeline) && cmdObj.pipeline.length >= 1) {
json = cmdObj.pipeline;
}
// The back end will process exactly one of json, query, or filter, so it does not matter too much which one we
// provide.
let stringJson = '';
if (json) {
stringJson = stringifyWhenNecessary(json);
span.data.mongo.json = stringJson;
} else if (cmdObj.filter || cmdObj.query) {
span.data.mongo.filter = stringifyWhenNecessary(cmdObj.filter || cmdObj.query);
}
let filter = '';
if (cmdObj.filter || cmdObj.query) {
filter = stringifyWhenNecessary(cmdObj.filter || cmdObj.query);
}
return { stringJson, filter };
}
function stringifyWhenNecessary(obj) {
if (obj == null) {
return undefined;
} else if (typeof obj === 'string') {
return tracingUtil.shortenDatabaseStatement(obj);
}
return tracingUtil.shortenDatabaseStatement(JSON.stringify(obj));
}
function createWrappedCallback(span, openSpanResult, originalCallback) {
return cls.ns.bind(function (error) {
/** @type{SpanLabels} */
const stSpanData = {};
if (error) {
span.ec = 1;
span.data.mongo.error = tracingUtil.getErrorDetails(error);
stSpanData[superconsts.Label.SupertenantError] = 'true';
}
span.d = Date.now() - span.ts;
span.transmit();
if (openSpanResult != null) {
if (!openSpanResult.executed) {
// #860q6qe8z mark the request as canceled.
// TODO: Is this needed without promises?
openSpanResult.canceled = true;
stSpanData[superconsts.Label.SupertenantCanceled] = 'true';
}
superbrain.closeSpan(openSpanResult.spanId, stSpanData);
}
return originalCallback.apply(this, arguments);
});
}
function dbAndCollectionFromNamespace(namespace) {
let db;
let collection;
if (namespace != null) {
if (typeof namespace === 'string') {
const idx = namespace.indexOf('.');
if (idx >= 0) {
db = namespace.substring(0, idx);
collection = namespace.substring(idx + 1);
} else {
db = namespace;
}
} else {
db = namespace.db;
collection = namespace.collection;
}
}
return {
db,
collection
};
}
exports.activate = function activate() {
isActive = true;
};
exports.deactivate = function deactivate() {
isActive = false;
};