diff --git a/.circleci/config.yml b/.circleci/config.yml index 2e3d019f2e..95d4c61fa2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,10 +2,14 @@ version: 2 test_env: &test_env RUN_POSTGRES_TESTS: 1 + RUN_MONGODB_TESTS: 1 POSTGRES_USER: postgres POSTGRES_DB: circle_database POSTGRES_HOST: localhost POSTGRES_PORT: 5432 + MONGODB_HOST: localhost + MONGODB_PORT: 27017 + MONGODB_DB: opentelemetry-tests postgres_service: &postgres_service image: circleci/postgres:9.6-alpine @@ -13,6 +17,9 @@ postgres_service: &postgres_service POSTGRES_USER: postgres POSTGRES_DB: circle_database +mongo_service: &mongo_service + image: mongo + cache_1: &cache_1 key: npm-cache-01-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/checksums.txt" }}-1 paths: @@ -39,7 +46,7 @@ cache_2: &cache_2 - packages/opentelemetry-plugin-grpc/node_modules - packages/opentelemetry-plugin-http/node_modules - packages/opentelemetry-plugin-http2/node_modules - - packages/opentelemetry-plugin-mongodb/node_modules + - packages/opentelemetry-plugin-mongodb-core/node_modules - packages/opentelemetry-plugin-redis/node_modules - packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/node_modules - packages/opentelemetry-plugin-document-load/node_modules @@ -145,18 +152,21 @@ jobs: - image: node:8 environment: *test_env - *postgres_service + - *mongo_service <<: *node_unit_tests node10: docker: - image: node:10 environment: *test_env - *postgres_service + - *mongo_service <<: *node_unit_tests node12: docker: - image: node:12 environment: *test_env - *postgres_service + - *mongo_service <<: *node_unit_tests node12-browsers: docker: diff --git a/packages/opentelemetry-plugin-mongodb/.npmignore b/packages/opentelemetry-plugin-mongodb-core/.npmignore similarity index 100% rename from packages/opentelemetry-plugin-mongodb/.npmignore rename to packages/opentelemetry-plugin-mongodb-core/.npmignore diff --git a/packages/opentelemetry-plugin-mongodb/LICENSE b/packages/opentelemetry-plugin-mongodb-core/LICENSE similarity index 100% rename from packages/opentelemetry-plugin-mongodb/LICENSE rename to packages/opentelemetry-plugin-mongodb-core/LICENSE diff --git a/packages/opentelemetry-plugin-mongodb/README.md b/packages/opentelemetry-plugin-mongodb-core/README.md similarity index 77% rename from packages/opentelemetry-plugin-mongodb/README.md rename to packages/opentelemetry-plugin-mongodb-core/README.md index 6c05d1b0da..1c6ed4aac6 100644 --- a/packages/opentelemetry-plugin-mongodb/README.md +++ b/packages/opentelemetry-plugin-mongodb-core/README.md @@ -1,10 +1,10 @@ -# OpenTelemetry mongodb Instrumentation for Node.js +# OpenTelemetry mongodb-core Instrumentation for Node.js [![Gitter chat][gitter-image]][gitter-url] [![dependencies][dependencies-image]][dependencies-url] [![devDependencies][devDependencies-image]][devDependencies-url] [![Apache License][license-image]][license-image] -This module provides automatic instrumentation for [`mongodb`](https://github.com/mongodb/node-mongodb-native). +This module provides automatic instrumentation for [`mongodb-core`](https://github.com/mongodb-js/mongodb-core). For automatic instrumentation see the [@opentelemetry/node](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-node) package. @@ -12,13 +12,13 @@ For automatic instrumentation see the ## Installation ```bash -npm install --save @opentelemetry/plugin-mongodb +npm install --save @opentelemetry/plugin-mongodb-core ``` ## Usage ```js -const opentelemetry = require('@opentelemetry/plugin-mongodb'); +const opentelemetry = require('@opentelemetry/plugin-mongodb-core'); // TODO: DEMONSTRATE API ``` @@ -36,7 +36,7 @@ Apache 2.0 - See [LICENSE][license-url] for more information. [gitter-url]: https://gitter.im/open-telemetry/opentelemetry-node?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge [license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/master/LICENSE [license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat -[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-plugin-mongodb -[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-mongodb -[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-plugin-mongodb -[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-mongodb&type=dev +[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-plugin-mongodb-core +[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-mongodb-core +[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-plugin-mongodb-core +[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-mongodb-core&type=dev diff --git a/packages/opentelemetry-plugin-mongodb/package.json b/packages/opentelemetry-plugin-mongodb-core/package.json similarity index 72% rename from packages/opentelemetry-plugin-mongodb/package.json rename to packages/opentelemetry-plugin-mongodb-core/package.json index 6c8b02219d..f54d725add 100644 --- a/packages/opentelemetry-plugin-mongodb/package.json +++ b/packages/opentelemetry-plugin-mongodb-core/package.json @@ -1,7 +1,7 @@ { - "name": "@opentelemetry/plugin-mongodb", + "name": "@opentelemetry/plugin-mongodb-core", "version": "0.2.0", - "description": "OpenTelemetry mongodb automatic instrumentation package.", + "description": "OpenTelemetry mongodb-core automatic instrumentation package.", "private": true, "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -18,7 +18,7 @@ }, "keywords": [ "opentelemetry", - "mongodb", + "mongodb-core", "nodejs", "tracing", "profiling", @@ -40,22 +40,27 @@ "access": "public" }, "devDependencies": { + "@opentelemetry/node": "^0.1.1", + "@opentelemetry/tracing": "^0.1.1", "@types/mocha": "^5.2.7", - "@types/node": "^12.6.9", + "@types/mongodb": "^3.2.3", + "@types/node": "^12.7.2", + "@types/shimmer": "^1.0.1", "codecov": "^3.6.1", "gts": "^1.1.0", "mocha": "^6.2.0", + "mongodb": "^3.3.0", "nyc": "^14.1.1", "rimraf": "^3.0.0", "ts-mocha": "^6.0.0", "ts-node": "^8.3.0", - "tslint-consistent-codestyle": "^1.15.1", + "tslint-consistent-codestyle": "^1.16.0", "tslint-microsoft-contrib": "^6.2.0", "typescript": "3.7.2" }, "dependencies": { "@opentelemetry/core": "^0.2.0", - "@opentelemetry/node": "^0.2.0", - "@opentelemetry/types": "^0.2.0" + "@opentelemetry/types": "^0.2.0", + "shimmer": "^1.2.1" } } diff --git a/packages/opentelemetry-plugin-mongodb/src/index.ts b/packages/opentelemetry-plugin-mongodb-core/src/index.ts similarity index 95% rename from packages/opentelemetry-plugin-mongodb/src/index.ts rename to packages/opentelemetry-plugin-mongodb-core/src/index.ts index ae225f6b52..21a9d57333 100644 --- a/packages/opentelemetry-plugin-mongodb/src/index.ts +++ b/packages/opentelemetry-plugin-mongodb-core/src/index.ts @@ -13,3 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +export * from './mongodb'; diff --git a/packages/opentelemetry-plugin-mongodb-core/src/mongodb.ts b/packages/opentelemetry-plugin-mongodb-core/src/mongodb.ts new file mode 100644 index 0000000000..e3e179c7b2 --- /dev/null +++ b/packages/opentelemetry-plugin-mongodb-core/src/mongodb.ts @@ -0,0 +1,251 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// mongodb.Server type is deprecated so every use trigger a lint error +/* tslint:disable:deprecation */ + +import { BasePlugin } from '@opentelemetry/core'; +import { Span, SpanKind, CanonicalCode } from '@opentelemetry/types'; +import { + Func, + MongoInternalCommand, + MongoInternalTopology, + AttributeNames, + MongodbCommandType, +} from './types'; +import * as mongodb from 'mongodb'; +import * as shimmer from 'shimmer'; + +/** MongoDBCore instrumentation plugin for OpenTelemetry */ +export class MongoDBCorePlugin extends BasePlugin { + private readonly _SERVER_METHODS = ['insert', 'update', 'remove', 'command']; + private readonly _CURSOR_METHODS = ['_next', 'next']; + + private readonly _COMPONENT = 'mongodb-core'; + private readonly _DB_TYPE = 'mongodb'; + + readonly supportedVersions = ['>=2 <3']; + + constructor(readonly moduleName: string) { + super(); + } + + /** + * Patches MongoDB operations. + */ + protected patch() { + this._logger.debug('Patched MongoDB'); + + if (this._moduleExports.Server) { + for (const fn of this._SERVER_METHODS) { + this._logger.debug(`patching mongodb-core.Server.prototype.${fn}`); + shimmer.wrap( + this._moduleExports.Server.prototype, + // Forced to ignore due to incomplete typings + // tslint:disable-next-line:ban-ts-ignore + // @ts-ignore + fn, + this._getPatchCommand(fn) + ); + } + } + + if (this._moduleExports.Cursor) { + this._logger.debug( + 'patching mongodb-core.Cursor.prototype functions:', + this._CURSOR_METHODS + ); + shimmer.massWrap( + [this._moduleExports.Cursor.prototype], + this._CURSOR_METHODS as never[], + // tslint:disable-next-line:no-any + this._getPatchCursor() as any + ); + } + + return this._moduleExports; + } + + /** Unpatches all MongoDB patched functions. */ + unpatch(): void { + shimmer.massUnwrap( + [this._moduleExports.Server.prototype], + this._SERVER_METHODS as never[] + ); + shimmer.massUnwrap( + [this._moduleExports.Cursor.prototype], + this._CURSOR_METHODS as never[] + ); + } + + /** Creates spans for Command operations */ + private _getPatchCommand(operationName: string) { + const plugin = this; + return (original: Func) => { + return function patchedServerCommand( + this: mongodb.Server, + ns: string, + commands: MongoInternalCommand[] | MongoInternalCommand, + options: {} | Function, + callback: Function + ): mongodb.Server { + const currentSpan = plugin._tracer.getCurrentSpan(); + const resultHandler = + typeof options === 'function' ? options : callback; + if ( + currentSpan === null || + typeof resultHandler !== 'function' || + typeof commands !== 'object' + ) { + return original.apply(this, (arguments as unknown) as unknown[]); + } + const command = commands instanceof Array ? commands[0] : commands; + const commandType = plugin._getCommandType(command); + const type = + commandType === MongodbCommandType.UNKNOWN + ? operationName + : commandType; + const span = plugin._tracer.startSpan(`mongodb.${type}`, { + parent: currentSpan, + kind: SpanKind.CLIENT, + }); + plugin._populateAttributes( + span, + ns, + command, + this as MongoInternalTopology + ); + return original.call( + this, + ns, + commands, + plugin._patchEnd(span, resultHandler) + ); + }; + }; + } + + /** + * Get the mongodb command type from the object. + * @param command Internal mongodb command object + * @param defaulType the default type to return if we could not find a + * specific command. + */ + private _getCommandType(command: MongoInternalCommand): MongodbCommandType { + if (command.createIndexes !== undefined) { + return MongodbCommandType.CREATE_INDEXES; + } else if (command.findandmodify !== undefined) { + return MongodbCommandType.FIND_AND_MODIFY; + } else if (command.ismaster !== undefined) { + return MongodbCommandType.IS_MASTER; + } else if (command.count !== undefined) { + return MongodbCommandType.COUNT; + } else { + return MongodbCommandType.UNKNOWN; + } + } + + /** + * Populate span's attributes by fetching related metadata from the context + * @param span span to add attributes to + * @param ns mongodb namespace + * @param command mongodb internal representation of a command + * @param topology mongodb internal representation of the network topology + */ + private _populateAttributes( + span: Span, + ns: string, + command: MongoInternalCommand, + topology: MongoInternalTopology + ) { + // add network attributes to determine the remote server + if (topology && topology.s && topology.s.options) { + span.setAttributes({ + [AttributeNames.PEER_HOSTNAME]: `${topology.s.options.host}`, + [AttributeNames.PEER_PORT]: `${topology.s.options.port}`, + }); + } + // add database related attributes + span.setAttributes({ + [AttributeNames.DB_INSTANCE]: `${ns}`, + [AttributeNames.DB_TYPE]: this._DB_TYPE, + [AttributeNames.COMPONENT]: this._COMPONENT, + }); + + if (command === undefined) return; + const query = Object.keys(command.query || command.q || {}).reduce( + (obj, key) => { + obj[key] = '?'; + return obj; + }, + {} as { [key: string]: string } + ); + span.setAttribute('db.statement', JSON.stringify(query)); + } + + /** Creates spans for Cursor operations */ + private _getPatchCursor() { + const plugin = this; + return (original: Func) => { + return function patchedCursorCommand( + this: { + ns: string; + cmd: MongoInternalCommand; + topology: MongoInternalTopology; + }, + ...args: unknown[] + ): mongodb.Cursor { + const currentSpan = plugin._tracer.getCurrentSpan(); + const resultHandler = args[0]; + if (currentSpan === null || typeof resultHandler !== 'function') { + return original.apply(this, args); + } + const span = plugin._tracer.startSpan(`mongodb.query`, { + parent: currentSpan, + kind: SpanKind.CLIENT, + }); + plugin._populateAttributes(span, this.ns, this.cmd, this.topology); + + return original.call(this, plugin._patchEnd(span, resultHandler)); + }; + }; + } + + /** + * Ends a created span. + * @param span The created span to end. + * @param resultHandler A callback function. + */ + private _patchEnd(span: Span, resultHandler: Function): Function { + return function patchedEnd(this: {}, ...args: unknown[]) { + const error = args[0]; + if (error instanceof Error) { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: error.message, + }); + } else { + span.setStatus({ + code: CanonicalCode.OK, + }); + } + span.end(); + return resultHandler.apply(this, args); + }; + } +} + +export const plugin = new MongoDBCorePlugin('mongodb-core'); diff --git a/packages/opentelemetry-plugin-mongodb-core/src/types.ts b/packages/opentelemetry-plugin-mongodb-core/src/types.ts new file mode 100644 index 0000000000..3c8b33b542 --- /dev/null +++ b/packages/opentelemetry-plugin-mongodb-core/src/types.ts @@ -0,0 +1,59 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type Func = (...args: unknown[]) => T; +export type MongoInternalCommand = { + findandmodify: boolean; + createIndexes: boolean; + count: boolean; + ismaster: boolean; + query?: { [key: string]: unknown }; + q?: { [key: string]: unknown }; +}; +// +// https://github.com/mongodb-js/mongodb-core/blob/master/lib/topologies/server.js#L117 +export type MongoInternalTopology = { + s?: { + options?: { + host?: string; + port?: number; + servername?: string; + }; + }; +}; + +export enum AttributeNames { + // required by https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md#databases-client-calls + COMPONENT = 'component', + DB_TYPE = 'db.type', + DB_INSTANCE = 'db.instance', + DB_STATEMENT = 'db.statement', + PEER_ADDRESS = 'peer.address', + PEER_HOSTNAME = 'peer.host', + + PEER_PORT = 'peer.port', + PEER_IPV4 = 'peer.ipv4', + PEER_IPV6 = 'peer.ipv6', + PEER_SERVICE = 'peer.service', +} + +export enum MongodbCommandType { + CREATE_INDEXES = 'createIndexes', + FIND_AND_MODIFY = 'findAndModify', + IS_MASTER = 'isMaster', + COUNT = 'count', + UNKNOWN = 'unknown', +} diff --git a/packages/opentelemetry-plugin-mongodb-core/test/mongodb.test.ts b/packages/opentelemetry-plugin-mongodb-core/test/mongodb.test.ts new file mode 100644 index 0000000000..b0f4ee15fc --- /dev/null +++ b/packages/opentelemetry-plugin-mongodb-core/test/mongodb.test.ts @@ -0,0 +1,284 @@ +/*! + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NodeTracer } from '@opentelemetry/node'; +import * as assert from 'assert'; +import * as mongodb from 'mongodb'; +import { plugin } from '../src'; +import { SpanKind, CanonicalCode } from '@opentelemetry/types'; +import { NoopLogger } from '@opentelemetry/core'; +import { AttributeNames } from '../src/types'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, + ReadableSpan, +} from '@opentelemetry/tracing'; + +interface MongoDBAccess { + client: mongodb.MongoClient; + collection: mongodb.Collection; +} + +/** + * Access the mongodb collection. + * @param url The mongodb URL to access. + * @param dbName The mongodb database name. + * @param collectionName The mongodb collection name. + */ +function accessCollection( + url: string, + dbName: string, + collectionName: string +): Promise { + return new Promise((resolve, reject) => { + mongodb.MongoClient.connect(url, function connectedClient(err, client) { + if (err) { + reject(err); + return; + } + const db = client.db(dbName); + const collection = db.collection(collectionName); + resolve({ client, collection }); + }); + }); +} + +/** + * Asserts root spans attributes. + * @param spans Readable spans that we need to asert. + * @param expectedName The expected name of the first root span. + * @param expectedKind The expected kind of the first root span. + */ +function assertSpans( + spans: ReadableSpan[], + expectedName: string, + expectedKind: SpanKind +) { + assert.strictEqual(spans.length, 2); + spans.forEach(span => { + assert(span.endTime instanceof Array); + assert(span.endTime.length === 2); + }); + const [mongoSpan] = spans; + assert.strictEqual(mongoSpan.name, expectedName); + assert.strictEqual(mongoSpan.kind, expectedKind); + assert.strictEqual( + mongoSpan.attributes[AttributeNames.COMPONENT], + 'mongodb-core' + ); + assert.strictEqual( + mongoSpan.attributes[AttributeNames.PEER_HOSTNAME], + process.env.MONGODB_HOST || 'localhost' + ); + assert.strictEqual(mongoSpan.status.code, CanonicalCode.OK); +} + +describe('MongoDBPlugin', () => { + // For these tests, mongo must be running. Add RUN_MONGODB_TESTS to run + // these tests. + const RUN_MONGODB_TESTS = process.env.RUN_MONGODB_TESTS as string; + let shouldTest = true; + if (!RUN_MONGODB_TESTS) { + console.log('Skipping test-mongodb. Run MongoDB to test'); + shouldTest = false; + } + + const URL = `mongodb://${process.env.MONGODB_HOST || 'localhost'}:${process + .env.MONGODB_PORT || '27017'}`; + const DB_NAME = process.env.MONGODB_DB || 'opentelemetry-tests'; + const COLLECTION_NAME = 'test'; + + let client: mongodb.MongoClient; + let collection: mongodb.Collection; + const logger = new NoopLogger(); + const tracer = new NodeTracer(); + const memoryExporter = new InMemorySpanExporter(); + tracer.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); + + before(done => { + plugin.enable(mongodb, tracer, logger); + accessCollection(URL, DB_NAME, COLLECTION_NAME) + .then(result => { + client = result.client; + collection = result.collection; + done(); + }) + .catch((err: Error) => { + console.log( + 'Skipping test-mongodb. Could not connect. Run MongoDB to test' + ); + shouldTest = false; + done(); + }); + }); + + beforeEach(function mongoBeforeEach(done) { + // Skiping all tests in beforeEach() is a workarround. Mocha does not work + // properly when skiping tests in before() on nested describe() calls. + // https://github.com/mochajs/mocha/issues/2819 + if (!shouldTest) { + this.skip(); + } + memoryExporter.reset(); + // Non traced insertion of basic data to perform tests + const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }]; + collection.insertMany(insertData, (err, result) => { + done(); + }); + }); + + afterEach(done => { + collection.deleteOne({}, done); + }); + + after(() => { + if (client) { + client.close(); + } + }); + + /** Should intercept query */ + describe('Instrumenting query operations', () => { + it('should create a child span for insert', done => { + const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }]; + + const span = tracer.startSpan(`insertRootSpan`); + tracer.withSpan(span, () => { + collection.insertMany(insertData, (err, result) => { + span.end(); + assert.ifError(err); + assertSpans( + memoryExporter.getFinishedSpans(), + `mongodb.insert`, + SpanKind.CLIENT + ); + done(); + }); + }); + }); + + it('should create a child span for update', done => { + const span = tracer.startSpan('updateRootSpan'); + tracer.withSpan(span, () => { + collection.updateOne({ a: 2 }, { $set: { b: 1 } }, (err, result) => { + span.end(); + assert.ifError(err); + assertSpans( + memoryExporter.getFinishedSpans(), + `mongodb.update`, + SpanKind.CLIENT + ); + done(); + }); + }); + }); + + it('should create a child span for remove', done => { + const span = tracer.startSpan('removeRootSpan'); + tracer.withSpan(span, () => { + collection.deleteOne({ a: 3 }, (err, result) => { + span.end(); + assert.ifError(err); + assertSpans( + memoryExporter.getFinishedSpans(), + `mongodb.remove`, + SpanKind.CLIENT + ); + done(); + }); + }); + }); + }); + + /** Should intercept cursor */ + describe('Instrumenting cursor operations', () => { + it('should create a child span for find', done => { + const span = tracer.startSpan('findRootSpan'); + tracer.withSpan(span, () => { + collection.find({}).toArray((err, result) => { + span.end(); + assert.ifError(err); + assertSpans( + memoryExporter.getFinishedSpans(), + `mongodb.query`, + SpanKind.CLIENT + ); + done(); + }); + }); + }); + }); + + /** Should intercept command */ + describe('Instrumenting command operations', () => { + it('should create a child span for create index', done => { + const span = tracer.startSpan('indexRootSpan'); + tracer.withSpan(span, () => { + collection.createIndex({ a: 1 }, (err, result) => { + span.end(); + assert.ifError(err); + assertSpans( + memoryExporter.getFinishedSpans(), + `mongodb.createIndexes`, + SpanKind.CLIENT + ); + done(); + }); + }); + }); + }); + + /** Should intercept command */ + describe('Removing Instrumentation', () => { + it('should unpatch plugin', () => { + assert.doesNotThrow(() => { + plugin.unpatch(); + }); + }); + + it('should not create a child span for query', done => { + const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }]; + + const span = tracer.startSpan('insertRootSpan'); + collection.insertMany(insertData, (err, result) => { + span.end(); + assert.ifError(err); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + done(); + }); + }); + + it('should not create a child span for cursor', done => { + const span = tracer.startSpan('findRootSpan'); + collection.find({}).toArray((err, result) => { + span.end(); + assert.ifError(err); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + done(); + }); + }); + + it('should not create a child span for command', done => { + const span = tracer.startSpan('indexRootSpan'); + collection.createIndex({ a: 1 }, (err, result) => { + span.end(); + assert.ifError(err); + assert.strictEqual(memoryExporter.getFinishedSpans().length, 1); + done(); + }); + }); + }); +}); diff --git a/packages/opentelemetry-plugin-mongodb/tsconfig.json b/packages/opentelemetry-plugin-mongodb-core/tsconfig.json similarity index 76% rename from packages/opentelemetry-plugin-mongodb/tsconfig.json rename to packages/opentelemetry-plugin-mongodb-core/tsconfig.json index a2042cd68b..3e83278f6c 100644 --- a/packages/opentelemetry-plugin-mongodb/tsconfig.json +++ b/packages/opentelemetry-plugin-mongodb-core/tsconfig.json @@ -5,7 +5,6 @@ "outDir": "build" }, "include": [ - "src/**/*.ts", - "test/**/*.ts" + "src/**/*.ts" ] } diff --git a/packages/opentelemetry-plugin-mongodb/tslint.json b/packages/opentelemetry-plugin-mongodb-core/tslint.json similarity index 100% rename from packages/opentelemetry-plugin-mongodb/tslint.json rename to packages/opentelemetry-plugin-mongodb-core/tslint.json