123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- var {
- _optionalChain
- } = require('@sentry/utils');
- Object.defineProperty(exports, '__esModule', { value: true });
- const utils = require('@sentry/utils');
- const debugBuild = require('../../common/debug-build.js');
- const nodeUtils = require('./utils/node-utils.js');
- // This allows us to use the same array for both defaults options and the type itself.
- // (note `as const` at the end to make it a union of string literal types (i.e. "a" | "b" | ... )
- // and not just a string[])
- const OPERATIONS = [
- 'aggregate', // aggregate(pipeline, options, callback)
- 'bulkWrite', // bulkWrite(operations, options, callback)
- 'countDocuments', // countDocuments(query, options, callback)
- 'createIndex', // createIndex(fieldOrSpec, options, callback)
- 'createIndexes', // createIndexes(indexSpecs, options, callback)
- 'deleteMany', // deleteMany(filter, options, callback)
- 'deleteOne', // deleteOne(filter, options, callback)
- 'distinct', // distinct(key, query, options, callback)
- 'drop', // drop(options, callback)
- 'dropIndex', // dropIndex(indexName, options, callback)
- 'dropIndexes', // dropIndexes(options, callback)
- 'estimatedDocumentCount', // estimatedDocumentCount(options, callback)
- 'find', // find(query, options, callback)
- 'findOne', // findOne(query, options, callback)
- 'findOneAndDelete', // findOneAndDelete(filter, options, callback)
- 'findOneAndReplace', // findOneAndReplace(filter, replacement, options, callback)
- 'findOneAndUpdate', // findOneAndUpdate(filter, update, options, callback)
- 'indexes', // indexes(options, callback)
- 'indexExists', // indexExists(indexes, options, callback)
- 'indexInformation', // indexInformation(options, callback)
- 'initializeOrderedBulkOp', // initializeOrderedBulkOp(options, callback)
- 'insertMany', // insertMany(docs, options, callback)
- 'insertOne', // insertOne(doc, options, callback)
- 'isCapped', // isCapped(options, callback)
- 'mapReduce', // mapReduce(map, reduce, options, callback)
- 'options', // options(options, callback)
- 'parallelCollectionScan', // parallelCollectionScan(options, callback)
- 'rename', // rename(newName, options, callback)
- 'replaceOne', // replaceOne(filter, doc, options, callback)
- 'stats', // stats(options, callback)
- 'updateMany', // updateMany(filter, update, options, callback)
- 'updateOne', // updateOne(filter, update, options, callback)
- ] ;
- // All of the operations above take `options` and `callback` as their final parameters, but some of them
- // take additional parameters as well. For those operations, this is a map of
- // { <operation name>: [<names of additional parameters>] }, as a way to know what to call the operation's
- // positional arguments when we add them to the span's `data` object later
- const OPERATION_SIGNATURES
- = {
- // aggregate intentionally not included because `pipeline` arguments are too complex to serialize well
- // see https://github.com/getsentry/sentry-javascript/pull/3102
- bulkWrite: ['operations'],
- countDocuments: ['query'],
- createIndex: ['fieldOrSpec'],
- createIndexes: ['indexSpecs'],
- deleteMany: ['filter'],
- deleteOne: ['filter'],
- distinct: ['key', 'query'],
- dropIndex: ['indexName'],
- find: ['query'],
- findOne: ['query'],
- findOneAndDelete: ['filter'],
- findOneAndReplace: ['filter', 'replacement'],
- findOneAndUpdate: ['filter', 'update'],
- indexExists: ['indexes'],
- insertMany: ['docs'],
- insertOne: ['doc'],
- mapReduce: ['map', 'reduce'],
- rename: ['newName'],
- replaceOne: ['filter', 'doc'],
- updateMany: ['filter', 'update'],
- updateOne: ['filter', 'update'],
- };
- function isCursor(maybeCursor) {
- return maybeCursor && typeof maybeCursor === 'object' && maybeCursor.once && typeof maybeCursor.once === 'function';
- }
- /** Tracing integration for mongo package */
- class Mongo {
- /**
- * @inheritDoc
- */
- static __initStatic() {this.id = 'Mongo';}
- /**
- * @inheritDoc
- */
- /**
- * @inheritDoc
- */
- constructor(options = {}) {
- this.name = Mongo.id;
- this._operations = Array.isArray(options.operations) ? options.operations : (OPERATIONS );
- this._describeOperations = 'describeOperations' in options ? options.describeOperations : true;
- this._useMongoose = !!options.useMongoose;
- }
- /** @inheritdoc */
- loadDependency() {
- const moduleName = this._useMongoose ? 'mongoose' : 'mongodb';
- return (this._module = this._module || utils.loadModule(moduleName));
- }
- /**
- * @inheritDoc
- */
- setupOnce(_, getCurrentHub) {
- if (nodeUtils.shouldDisableAutoInstrumentation(getCurrentHub)) {
- debugBuild.DEBUG_BUILD && utils.logger.log('Mongo Integration is skipped because of instrumenter configuration.');
- return;
- }
- const pkg = this.loadDependency();
- if (!pkg) {
- const moduleName = this._useMongoose ? 'mongoose' : 'mongodb';
- debugBuild.DEBUG_BUILD && utils.logger.error(`Mongo Integration was unable to require \`${moduleName}\` package.`);
- return;
- }
- this._instrumentOperations(pkg.Collection, this._operations, getCurrentHub);
- }
- /**
- * Patches original collection methods
- */
- _instrumentOperations(collection, operations, getCurrentHub) {
- operations.forEach((operation) => this._patchOperation(collection, operation, getCurrentHub));
- }
- /**
- * Patches original collection to utilize our tracing functionality
- */
- _patchOperation(collection, operation, getCurrentHub) {
- if (!(operation in collection.prototype)) return;
- const getSpanContext = this._getSpanContextFromOperationArguments.bind(this);
- utils.fill(collection.prototype, operation, function (orig) {
- return function ( ...args) {
- const lastArg = args[args.length - 1];
- // eslint-disable-next-line deprecation/deprecation
- const hub = getCurrentHub();
- // eslint-disable-next-line deprecation/deprecation
- const scope = hub.getScope();
- // eslint-disable-next-line deprecation/deprecation
- const client = hub.getClient();
- // eslint-disable-next-line deprecation/deprecation
- const parentSpan = scope.getSpan();
- const sendDefaultPii = _optionalChain([client, 'optionalAccess', _2 => _2.getOptions, 'call', _3 => _3(), 'access', _4 => _4.sendDefaultPii]);
- // Check if the operation was passed a callback. (mapReduce requires a different check, as
- // its (non-callback) arguments can also be functions.)
- if (typeof lastArg !== 'function' || (operation === 'mapReduce' && args.length === 2)) {
- // eslint-disable-next-line deprecation/deprecation
- const span = _optionalChain([parentSpan, 'optionalAccess', _5 => _5.startChild, 'call', _6 => _6(getSpanContext(this, operation, args, sendDefaultPii))]);
- const maybePromiseOrCursor = orig.call(this, ...args);
- if (utils.isThenable(maybePromiseOrCursor)) {
- return maybePromiseOrCursor.then((res) => {
- _optionalChain([span, 'optionalAccess', _7 => _7.end, 'call', _8 => _8()]);
- return res;
- });
- }
- // If the operation returns a Cursor
- // we need to attach a listener to it to finish the span when the cursor is closed.
- else if (isCursor(maybePromiseOrCursor)) {
- const cursor = maybePromiseOrCursor ;
- try {
- cursor.once('close', () => {
- _optionalChain([span, 'optionalAccess', _9 => _9.end, 'call', _10 => _10()]);
- });
- } catch (e) {
- // If the cursor is already closed, `once` will throw an error. In that case, we can
- // finish the span immediately.
- _optionalChain([span, 'optionalAccess', _11 => _11.end, 'call', _12 => _12()]);
- }
- return cursor;
- } else {
- _optionalChain([span, 'optionalAccess', _13 => _13.end, 'call', _14 => _14()]);
- return maybePromiseOrCursor;
- }
- }
- // eslint-disable-next-line deprecation/deprecation
- const span = _optionalChain([parentSpan, 'optionalAccess', _15 => _15.startChild, 'call', _16 => _16(getSpanContext(this, operation, args.slice(0, -1)))]);
- return orig.call(this, ...args.slice(0, -1), function (err, result) {
- _optionalChain([span, 'optionalAccess', _17 => _17.end, 'call', _18 => _18()]);
- lastArg(err, result);
- });
- };
- });
- }
- /**
- * Form a SpanContext based on the user input to a given operation.
- */
- _getSpanContextFromOperationArguments(
- collection,
- operation,
- args,
- sendDefaultPii = false,
- ) {
- const data = {
- 'db.system': 'mongodb',
- 'db.name': collection.dbName,
- 'db.operation': operation,
- 'db.mongodb.collection': collection.collectionName,
- };
- const spanContext = {
- op: 'db',
- // TODO v8: Use `${collection.collectionName}.${operation}`
- origin: 'auto.db.mongo',
- description: operation,
- data,
- };
- // If the operation takes no arguments besides `options` and `callback`, or if argument
- // collection is disabled for this operation, just return early.
- const signature = OPERATION_SIGNATURES[operation];
- const shouldDescribe = Array.isArray(this._describeOperations)
- ? this._describeOperations.includes(operation)
- : this._describeOperations;
- if (!signature || !shouldDescribe || !sendDefaultPii) {
- return spanContext;
- }
- try {
- // Special case for `mapReduce`, as the only one accepting functions as arguments.
- if (operation === 'mapReduce') {
- const [map, reduce] = args ;
- data[signature[0]] = typeof map === 'string' ? map : map.name || '<anonymous>';
- data[signature[1]] = typeof reduce === 'string' ? reduce : reduce.name || '<anonymous>';
- } else {
- for (let i = 0; i < signature.length; i++) {
- data[`db.mongodb.${signature[i]}`] = JSON.stringify(args[i]);
- }
- }
- } catch (_oO) {
- // no-empty
- }
- return spanContext;
- }
- }Mongo.__initStatic();
- exports.Mongo = Mongo;
- //# sourceMappingURL=mongo.js.map
|