Skip to content

Commit 3a0dd0d

Browse files
obecnyvmarchaud
andauthored
chore: fixing context propagation on mongo callback (#403)
Co-authored-by: Valentin Marchaud <thisismac47@gmail.com>
1 parent 3a3220c commit 3a0dd0d

6 files changed

Lines changed: 74 additions & 12 deletions

File tree

examples/mongodb/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
"@opentelemetry/exporter-zipkin": "^0.18.0",
3434
"@opentelemetry/instrumentation": "^0.18.0",
3535
"@opentelemetry/instrumentation-http": "^0.18.0",
36+
"@opentelemetry/instrumentation-mongodb": "^0.15.0",
3637
"@opentelemetry/node": "^0.18.0",
37-
"@opentelemetry/plugin-mongodb": "^0.14.0",
3838
"@opentelemetry/tracing": "^0.18.0",
3939
"mongodb": "^3.5.7"
4040
},

examples/mongodb/server.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,17 @@ startServer(8080);
6060

6161
function handleInsertQuery(response) {
6262
const obj = { name: 'John', age: '20' };
63-
db.collection('users').insertOne(obj, (err) => {
63+
const collection = db.collection('users');
64+
collection.insertOne(obj, (err) => {
6465
if (err) {
6566
console.log('Error code:', err.code);
6667
response.end(err.message);
6768
} else {
6869
console.log('1 document inserted');
69-
response.end();
70+
// find document to test context propagation using callback
71+
collection.findOne({}, function () {
72+
response.end();
73+
});
7074
}
7175
});
7276
}

examples/mongodb/tracer.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
77
const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin');
88
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
99
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
10+
const { MongoDBInstrumentation } = require('@opentelemetry/instrumentation-mongodb');
1011

1112
module.exports = (serviceName) => {
1213
const provider = new NodeTracerProvider();
@@ -24,13 +25,12 @@ module.exports = (serviceName) => {
2425
registerInstrumentations({
2526
instrumentations: [
2627
new HttpInstrumentation(),
28+
new MongoDBInstrumentation({
29+
enhancedDatabaseReporting: true,
30+
}),
2731
{
2832
plugins: {
29-
mongodb: {
30-
enabled: true,
31-
path: '@opentelemetry/plugin-mongodb',
32-
enhancedDatabaseReporting: true,
33-
},
33+
mongodb: { enabled: false },
3434
},
3535
},
3636
],

plugins/node/opentelemetry-instrumentation-mongodb/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"types": "build/src/index.d.ts",
77
"repository": "open-telemetry/opentelemetry-js-contrib",
88
"scripts": {
9+
"docker:start": "docker run -e MONGODB_DB=opentelemetry-tests -e MONGODB_PORT=27017 -e MONGODB_HOST=localhost -p 27017:27017 --rm mongo",
910
"test": "nyc ts-mocha --parallel -p tsconfig.json 'test/**/*.test.ts'",
1011
"codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../",
1112
"tdd": "npm run test -- --watch-extensions ts --watch",
@@ -15,7 +16,8 @@
1516
"precompile": "tsc --version",
1617
"version:update": "node ../../../scripts/version-update.js",
1718
"compile": "npm run version:update && tsc -p .",
18-
"prepare": "npm run compile"
19+
"prepare": "npm run compile",
20+
"watch": "tsc -w"
1921
},
2022
"keywords": [
2123
"opentelemetry",

plugins/node/opentelemetry-instrumentation-mongodb/src/mongodb.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,9 @@ export class MongoDBInstrumentation extends InstrumentationBase<
397397
* @param resultHandler A callback function.
398398
*/
399399
private _patchEnd(span: Span, resultHandler: Function): Function {
400+
// mongodb is using "tick" when calling a callback, this way the context
401+
// in final callback (resultHandler) is lost
402+
const activeContext = context.active();
400403
return function patchedEnd(this: {}, ...args: unknown[]) {
401404
const error = args[0];
402405
if (error instanceof Error) {
@@ -406,7 +409,10 @@ export class MongoDBInstrumentation extends InstrumentationBase<
406409
});
407410
}
408411
span.end();
409-
return resultHandler.apply(this, args);
412+
413+
return context.with(activeContext, () => {
414+
return resultHandler.apply(this, args);
415+
});
410416
};
411417
}
412418
}

plugins/node/opentelemetry-instrumentation-mongodb/test/mongodb.test.ts

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
// for testing locally use this command to run docker
18-
// docker run -e MONGODB_DB=opentelemetry-tests -e MONGODB_PORT=27017 -e MONGODB_HOST=localhost -p 27017:27017 --name otmongo mongo
17+
// for testing locally "npm run docker:start"
1918

2019
import { context, setSpan, SpanKind } from '@opentelemetry/api';
2120
import { BasicTracerProvider } from '@opentelemetry/tracing';
@@ -99,6 +98,7 @@ describe('MongoDBInstrumentation', () => {
9998

10099
afterEach(done => {
101100
collection.deleteMany({}, done);
101+
memoryExporter.reset();
102102
});
103103

104104
after(() => {
@@ -109,6 +109,9 @@ describe('MongoDBInstrumentation', () => {
109109

110110
/** Should intercept query */
111111
describe('Instrumenting query operations', () => {
112+
beforeEach(() => {
113+
memoryExporter.reset();
114+
});
112115
it('should create a child span for insert', done => {
113116
const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }];
114117
const span = provider.getTracer('default').startSpan('insertRootSpan');
@@ -161,6 +164,10 @@ describe('MongoDBInstrumentation', () => {
161164

162165
/** Should intercept cursor */
163166
describe('Instrumenting cursor operations', () => {
167+
beforeEach(() => {
168+
memoryExporter.reset();
169+
});
170+
164171
it('should create a child span for find', done => {
165172
const span = provider.getTracer('default').startSpan('findRootSpan');
166173
context.with(setSpan(context.active(), span), () => {
@@ -212,6 +219,10 @@ describe('MongoDBInstrumentation', () => {
212219

213220
/** Should intercept command */
214221
describe('Instrumenting command operations', () => {
222+
beforeEach(() => {
223+
memoryExporter.reset();
224+
});
225+
215226
it('should create a child span for create index', done => {
216227
const span = provider.getTracer('default').startSpan('indexRootSpan');
217228
context.with(setSpan(context.active(), span), () => {
@@ -229,8 +240,47 @@ describe('MongoDBInstrumentation', () => {
229240
});
230241
});
231242

243+
describe('Mixed operations with callback', () => {
244+
beforeEach(() => {
245+
memoryExporter.reset();
246+
});
247+
248+
it('should create a span for find after callback insert', done => {
249+
const insertData = [{ a: 1 }, { a: 2 }, { a: 3 }];
250+
const span = provider.getTracer('default').startSpan('insertRootSpan');
251+
context.with(setSpan(context.active(), span), () => {
252+
collection.insertMany(insertData, (err, result) => {
253+
span.end();
254+
assert.ifError(err);
255+
const spans = memoryExporter.getFinishedSpans();
256+
const mainSpan = spans[spans.length - 1];
257+
assertSpans(spans, 'mongodb.insert', SpanKind.CLIENT);
258+
memoryExporter.reset();
259+
260+
collection.find({ a: 1 }).toArray((err, result) => {
261+
const spans2 = memoryExporter.getFinishedSpans();
262+
spans2.push(mainSpan);
263+
264+
assert.ifError(err);
265+
assertSpans(spans2, 'mongodb.find', SpanKind.CLIENT);
266+
assert.strictEqual(
267+
mainSpan.spanContext.spanId,
268+
spans2[0].parentSpanId
269+
);
270+
memoryExporter.reset();
271+
done();
272+
});
273+
});
274+
});
275+
});
276+
});
277+
232278
/** Should intercept command */
233279
describe('Removing Instrumentation', () => {
280+
beforeEach(() => {
281+
memoryExporter.reset();
282+
});
283+
234284
it('should unpatch plugin', () => {
235285
assert.doesNotThrow(() => {
236286
instrumentation.disable();

0 commit comments

Comments
 (0)