Skip to content

Commit 2989b94

Browse files
authored
feat(instrumentation-mongodb): Add requireParentSpan config option. (open-telemetry#2658)
1 parent bc349a2 commit 2989b94

File tree

6 files changed

+180
-13
lines changed

6 files changed

+180
-13
lines changed

plugins/node/opentelemetry-instrumentation-mongodb/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Mongodb instrumentation has few options available to choose from. You can set th
5454
| [`enhancedDatabaseReporting`](./src/types.ts#L32) | `string` | If true, additional information about query parameters and results will be attached (as `attributes`) to spans representing database operations |
5555
| `responseHook` | `MongoDBInstrumentationExecutionResponseHook` (function) | Function for adding custom attributes from db response |
5656
| `dbStatementSerializer` | `DbStatementSerializer` (function) | Custom serializer function for the db.statement tag |
57+
| `requireParentSpan` | `boolean` | Require a parent span in order to create mongodb spans, default when unset is `true` |
5758

5859
## Semantic Conventions
5960

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

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,21 @@ import { V4Connect, V4Session } from './internal-types';
5555
import { PACKAGE_NAME, PACKAGE_VERSION } from './version';
5656
import { UpDownCounter } from '@opentelemetry/api';
5757

58+
const DEFAULT_CONFIG: MongoDBInstrumentationConfig = {
59+
requireParentSpan: true,
60+
};
61+
5862
/** mongodb instrumentation plugin for OpenTelemetry */
5963
export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumentationConfig> {
6064
private _connectionsUsage!: UpDownCounter;
6165
private _poolName!: string;
6266

6367
constructor(config: MongoDBInstrumentationConfig = {}) {
64-
super(PACKAGE_NAME, PACKAGE_VERSION, config);
68+
super(PACKAGE_NAME, PACKAGE_VERSION, { ...DEFAULT_CONFIG, ...config });
69+
}
70+
71+
override setConfig(config: MongoDBInstrumentationConfig = {}) {
72+
super.setConfig({ ...DEFAULT_CONFIG, ...config });
6573
}
6674

6775
override _updateMetricInstruments() {
@@ -438,10 +446,13 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
438446
callback?: Function
439447
) {
440448
const currentSpan = trace.getSpan(context.active());
449+
const skipInstrumentation =
450+
instrumentation._checkSkipInstrumentation(currentSpan);
451+
441452
const resultHandler =
442453
typeof options === 'function' ? options : callback;
443454
if (
444-
!currentSpan ||
455+
skipInstrumentation ||
445456
typeof resultHandler !== 'function' ||
446457
typeof ops !== 'object'
447458
) {
@@ -451,6 +462,7 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
451462
return original.call(this, server, ns, ops, options, callback);
452463
}
453464
}
465+
454466
const span = instrumentation.tracer.startSpan(
455467
`mongodb.${operationName}`,
456468
{
@@ -490,10 +502,14 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
490502
callback?: Function
491503
) {
492504
const currentSpan = trace.getSpan(context.active());
505+
const skipInstrumentation =
506+
instrumentation._checkSkipInstrumentation(currentSpan);
507+
493508
const resultHandler =
494509
typeof options === 'function' ? options : callback;
510+
495511
if (
496-
!currentSpan ||
512+
skipInstrumentation ||
497513
typeof resultHandler !== 'function' ||
498514
typeof cmd !== 'object'
499515
) {
@@ -503,6 +519,7 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
503519
return original.call(this, server, ns, cmd, options, callback);
504520
}
505521
}
522+
506523
const commandType = MongoDBInstrumentation._getCommandType(cmd);
507524
const type =
508525
commandType === MongodbCommandType.UNKNOWN ? 'command' : commandType;
@@ -535,20 +552,17 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
535552
callback: any
536553
) {
537554
const currentSpan = trace.getSpan(context.active());
555+
const skipInstrumentation =
556+
instrumentation._checkSkipInstrumentation(currentSpan);
538557
const resultHandler = callback;
539558
const commandType = Object.keys(cmd)[0];
540559

541-
if (
542-
typeof resultHandler !== 'function' ||
543-
typeof cmd !== 'object' ||
544-
cmd.ismaster ||
545-
cmd.hello
546-
) {
560+
if (typeof cmd !== 'object' || cmd.ismaster || cmd.hello) {
547561
return original.call(this, ns, cmd, options, callback);
548562
}
549563

550564
let span = undefined;
551-
if (currentSpan) {
565+
if (!skipInstrumentation) {
552566
span = instrumentation.tracer.startSpan(`mongodb.${commandType}`, {
553567
kind: SpanKind.CLIENT,
554568
});
@@ -581,6 +595,9 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
581595
) {
582596
const [ns, cmd] = args;
583597
const currentSpan = trace.getSpan(context.active());
598+
const skipInstrumentation =
599+
instrumentation._checkSkipInstrumentation(currentSpan);
600+
584601
const commandType = Object.keys(cmd)[0];
585602
const resultHandler = () => undefined;
586603

@@ -589,7 +606,7 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
589606
}
590607

591608
let span = undefined;
592-
if (currentSpan) {
609+
if (!skipInstrumentation) {
593610
span = instrumentation.tracer.startSpan(`mongodb.${commandType}`, {
594611
kind: SpanKind.CLIENT,
595612
});
@@ -634,10 +651,13 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
634651
callback?: Function
635652
) {
636653
const currentSpan = trace.getSpan(context.active());
654+
const skipInstrumentation =
655+
instrumentation._checkSkipInstrumentation(currentSpan);
637656
const resultHandler =
638657
typeof options === 'function' ? options : callback;
658+
639659
if (
640-
!currentSpan ||
660+
skipInstrumentation ||
641661
typeof resultHandler !== 'function' ||
642662
typeof cmd !== 'object'
643663
) {
@@ -655,6 +675,7 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
655675
);
656676
}
657677
}
678+
658679
const span = instrumentation.tracer.startSpan('mongodb.find', {
659680
kind: SpanKind.CLIENT,
660681
});
@@ -699,9 +720,13 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
699720
callback?: Function
700721
) {
701722
const currentSpan = trace.getSpan(context.active());
723+
const skipInstrumentation =
724+
instrumentation._checkSkipInstrumentation(currentSpan);
725+
702726
const resultHandler =
703727
typeof options === 'function' ? options : callback;
704-
if (!currentSpan || typeof resultHandler !== 'function') {
728+
729+
if (skipInstrumentation || typeof resultHandler !== 'function') {
705730
if (typeof options === 'function') {
706731
return original.call(
707732
this,
@@ -723,6 +748,7 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
723748
);
724749
}
725750
}
751+
726752
const span = instrumentation.tracer.startSpan('mongodb.getMore', {
727753
kind: SpanKind.CLIENT,
728754
});
@@ -1021,4 +1047,10 @@ export class MongoDBInstrumentation extends InstrumentationBase<MongoDBInstrumen
10211047
const poolName = `mongodb://${host}:${port}/${database}`;
10221048
this._poolName = poolName;
10231049
}
1050+
1051+
private _checkSkipInstrumentation(currentSpan: Span | undefined) {
1052+
const requireParentSpan = this.getConfig().requireParentSpan;
1053+
const hasNoParentSpan = currentSpan === undefined;
1054+
return requireParentSpan === true && hasNoParentSpan;
1055+
}
10241056
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ export interface MongoDBInstrumentationConfig extends InstrumentationConfig {
4949
* Custom serializer function for the db.statement tag
5050
*/
5151
dbStatementSerializer?: DbStatementSerializer;
52+
53+
/**
54+
* Require parent to create mongodb span, default when unset is true
55+
*/
56+
requireParentSpan?: boolean;
5257
}
5358

5459
export interface MongoResponseHookInformation {

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,49 @@ describe('MongoDBInstrumentation-Tracing-v3', () => {
671671
});
672672
});
673673

674+
describe('requireParentSpan', () => {
675+
// Resetting the behavior to default to avoid flakes in other tests
676+
beforeEach(() => {
677+
instrumentation.setConfig();
678+
});
679+
680+
afterEach(() => {
681+
instrumentation.setConfig();
682+
});
683+
684+
it('should not create spans without parent span when requireParentSpan is explicitly set to true', done => {
685+
context.with(trace.deleteSpan(context.active()), () => {
686+
collection
687+
.insertOne({ a: 1 })
688+
.then(() => {
689+
assert.strictEqual(getTestSpans().length, 0);
690+
done();
691+
})
692+
.catch(err => {
693+
done(err);
694+
});
695+
});
696+
});
697+
698+
it('should create spans without parent span when requireParentSpan is false', done => {
699+
instrumentation.setConfig({
700+
requireParentSpan: false,
701+
});
702+
703+
context.with(trace.deleteSpan(context.active()), () => {
704+
collection
705+
.insertOne({ a: 1 })
706+
.then(() => {
707+
assert.strictEqual(getTestSpans().length, 1);
708+
done();
709+
})
710+
.catch(err => {
711+
done(err);
712+
});
713+
});
714+
});
715+
});
716+
674717
/** Should intercept command */
675718
describe('Removing Instrumentation', () => {
676719
it('should unpatch plugin', () => {

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,49 @@ describe('MongoDBInstrumentation-Tracing-v4', () => {
642642
});
643643
});
644644

645+
describe('requireParentSpan', () => {
646+
// Resetting the behavior to default to avoid flakes in other tests
647+
beforeEach(() => {
648+
instrumentation.setConfig();
649+
});
650+
651+
afterEach(() => {
652+
instrumentation.setConfig();
653+
});
654+
655+
it('should not create spans without parent span when requireParentSpan is explicitly set to true', done => {
656+
context.with(trace.deleteSpan(context.active()), () => {
657+
collection
658+
.insertOne({ a: 1 })
659+
.then(() => {
660+
assert.strictEqual(getTestSpans().length, 0);
661+
done();
662+
})
663+
.catch(err => {
664+
done(err);
665+
});
666+
});
667+
});
668+
669+
it('should create spans without parent span when requireParentSpan is false', done => {
670+
instrumentation.setConfig({
671+
requireParentSpan: false,
672+
});
673+
674+
context.with(trace.deleteSpan(context.active()), () => {
675+
collection
676+
.insertOne({ a: 1 })
677+
.then(() => {
678+
assert.strictEqual(getTestSpans().length, 1);
679+
done();
680+
})
681+
.catch(err => {
682+
done(err);
683+
});
684+
});
685+
});
686+
});
687+
645688
/** Should intercept command */
646689
describe('Removing Instrumentation', () => {
647690
it('should unpatch plugin', () => {

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,49 @@ describe('MongoDBInstrumentation-Tracing-v5', () => {
655655
});
656656
});
657657

658+
describe('requireParentSpan', () => {
659+
// Resetting the behavior to default to avoid flakes in other tests
660+
beforeEach(() => {
661+
instrumentation.setConfig();
662+
});
663+
664+
afterEach(() => {
665+
instrumentation.setConfig();
666+
});
667+
668+
it('should not create spans without parent span when requireParentSpan is explicitly set to true', done => {
669+
context.with(trace.deleteSpan(context.active()), () => {
670+
collection
671+
.insertOne({ a: 1 })
672+
.then(() => {
673+
assert.strictEqual(getTestSpans().length, 0);
674+
done();
675+
})
676+
.catch(err => {
677+
done(err);
678+
});
679+
});
680+
});
681+
682+
it('should create spans without parent span when requireParentSpan is false', done => {
683+
instrumentation.setConfig({
684+
requireParentSpan: false,
685+
});
686+
687+
context.with(trace.deleteSpan(context.active()), () => {
688+
collection
689+
.insertOne({ a: 1 })
690+
.then(() => {
691+
assert.strictEqual(getTestSpans().length, 1);
692+
done();
693+
})
694+
.catch(err => {
695+
done(err);
696+
});
697+
});
698+
});
699+
});
700+
658701
/** Should intercept command */
659702
describe('Removing Instrumentation', () => {
660703
it('should unpatch plugin', () => {

0 commit comments

Comments
 (0)