287 lines
8.6 KiB
TypeScript
287 lines
8.6 KiB
TypeScript
/*!
|
|
* 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 { NodeTracerRegistry } 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<MongoDBAccess> {
|
|
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 assert.
|
|
* @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,
|
|
log = false
|
|
) {
|
|
if (log) {
|
|
console.log(spans);
|
|
}
|
|
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');
|
|
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 registry = new NodeTracerRegistry();
|
|
const memoryExporter = new InMemorySpanExporter();
|
|
const spanProcessor = new SimpleSpanProcessor(memoryExporter);
|
|
registry.addSpanProcessor(spanProcessor);
|
|
|
|
before(done => {
|
|
plugin.enable(mongodb, registry, 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) {
|
|
// Skipping all tests in beforeEach() is a workaround. Mocha does not work
|
|
// properly when skipping 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 = registry.getTracer('default').startSpan(`insertRootSpan`);
|
|
registry.getTracer('default').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 = registry.getTracer('default').startSpan('updateRootSpan');
|
|
registry.getTracer('default').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 = registry.getTracer('default').startSpan('removeRootSpan');
|
|
registry.getTracer('default').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 = registry.getTracer('default').startSpan('findRootSpan');
|
|
registry.getTracer('default').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 = registry.getTracer('default').startSpan('indexRootSpan');
|
|
registry.getTracer('default').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 = registry.getTracer('default').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 = registry.getTracer('default').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 = registry.getTracer('default').startSpan('indexRootSpan');
|
|
collection.createIndex({ a: 1 }, (err, result) => {
|
|
span.end();
|
|
assert.ifError(err);
|
|
assert.strictEqual(memoryExporter.getFinishedSpans().length, 1);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|