Skip to content

Commit 560236e

Browse files
lu-zhengdabengltlhunter
authoredFeb 14, 2025
Inject trace info as comment to MongoDB operation when dbm propagation is enabled. (#5230)
* inject dbm trace comment * add service mode test * add unit test to verify both full and service mode * update test * fix lint * use find query * fix timeout * remove done * add tests to mongodb-core * fix service mode full * remove custom timeout * Update packages/dd-trace/src/plugins/database.js Co-authored-by: Bryan English <bryan.english@datadoghq.com> * merge duplicate code * add tests * add tests to verify command with comments * fix lint * Update index.js Co-authored-by: Thomas Hunter II <tlhunter@datadog.com> --------- Co-authored-by: Bryan English <bryan.english@datadoghq.com> Co-authored-by: Thomas Hunter II <tlhunter@datadog.com>
1 parent ff09f50 commit 560236e

File tree

5 files changed

+331
-7
lines changed

5 files changed

+331
-7
lines changed
 

‎docker-compose.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ services:
3737
ports:
3838
- "127.0.0.1:6379:6379"
3939
mongo:
40-
image: circleci/mongo:3.6
40+
image: circleci/mongo:4.4
4141
platform: linux/amd64
4242
ports:
4343
- "127.0.0.1:27017:27017"

‎packages/datadog-plugin-mongodb-core/src/index.js

+28-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ class MongodbCorePlugin extends DatabasePlugin {
1111
start ({ ns, ops, options = {}, name }) {
1212
const query = getQuery(ops)
1313
const resource = truncate(getResource(this, ns, query, name))
14-
this.startSpan(this.operationName(), {
15-
service: this.serviceName({ pluginConfig: this.config }),
14+
const service = this.serviceName({ pluginConfig: this.config })
15+
const span = this.startSpan(this.operationName(), {
16+
service,
1617
resource,
1718
type: 'mongodb',
1819
kind: 'client',
@@ -24,6 +25,7 @@ class MongodbCorePlugin extends DatabasePlugin {
2425
'out.port': options.port
2526
}
2627
})
28+
ops = this.injectDbmCommand(span, ops, service)
2729
}
2830

2931
getPeerService (tags) {
@@ -34,6 +36,30 @@ class MongodbCorePlugin extends DatabasePlugin {
3436
}
3537
return super.getPeerService(tags)
3638
}
39+
40+
injectDbmCommand (span, command, serviceName) {
41+
const dbmTraceComment = this.createDbmComment(span, serviceName)
42+
43+
if (!dbmTraceComment) {
44+
return command
45+
}
46+
47+
// create a copy of the command to avoid mutating the original
48+
const dbmTracedCommand = { ...command }
49+
50+
if (dbmTracedCommand.comment) {
51+
// if the command already has a comment, append the dbm trace comment
52+
if (typeof dbmTracedCommand.comment === 'string') {
53+
dbmTracedCommand.comment += `,${dbmTraceComment}`
54+
} else if (Array.isArray(dbmTracedCommand.comment)) {
55+
dbmTracedCommand.comment.push(dbmTraceComment)
56+
} // do nothing if the comment is not a string or an array
57+
} else {
58+
dbmTracedCommand.comment = dbmTraceComment
59+
}
60+
61+
return dbmTracedCommand
62+
}
3763
}
3864

3965
function sanitizeBigInt (data) {

‎packages/datadog-plugin-mongodb-core/test/core.spec.js

+180
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
'use strict'
22

3+
const sinon = require('sinon')
34
const semver = require('semver')
45
const agent = require('../../dd-trace/test/plugins/agent')
56
const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../dd-trace/src/constants')
67
const { expectedSchema, rawExpectedSchema } = require('./naming')
78

9+
const MongodbCorePlugin = require('../../datadog-plugin-mongodb-core/src/index')
10+
const ddpv = require('mocha/package.json').version
11+
812
const withTopologies = fn => {
913
withVersions('mongodb-core', ['mongodb-core', 'mongodb'], '<4', (version, moduleName) => {
1014
describe('using the server topology', () => {
@@ -29,6 +33,7 @@ describe('Plugin', () => {
2933
let id
3034
let tracer
3135
let collection
36+
let injectDbmCommandSpy
3237

3338
describe('mongodb-core (core)', () => {
3439
withTopologies(getServer => {
@@ -397,6 +402,181 @@ describe('Plugin', () => {
397402
}
398403
)
399404
})
405+
406+
describe('with dbmPropagationMode service', () => {
407+
before(() => {
408+
return agent.load('mongodb-core', { dbmPropagationMode: 'service' })
409+
})
410+
411+
after(() => {
412+
return agent.close({ ritmReset: false })
413+
})
414+
415+
beforeEach(done => {
416+
const Server = getServer()
417+
418+
server = new Server({
419+
host: '127.0.0.1',
420+
port: 27017,
421+
reconnect: false
422+
})
423+
424+
server.on('connect', () => done())
425+
server.on('error', done)
426+
427+
server.connect()
428+
429+
injectDbmCommandSpy = sinon.spy(MongodbCorePlugin.prototype, 'injectDbmCommand')
430+
})
431+
432+
afterEach(() => {
433+
injectDbmCommandSpy?.restore()
434+
})
435+
436+
it('DBM propagation should inject service mode as comment', done => {
437+
agent
438+
.use(traces => {
439+
const span = traces[0][0]
440+
441+
expect(injectDbmCommandSpy.called).to.be.true
442+
const instrumentedCommand = injectDbmCommandSpy.getCall(0).returnValue
443+
expect(instrumentedCommand).to.have.property('comment')
444+
expect(instrumentedCommand.comment).to.equal(
445+
`dddb='${encodeURIComponent(span.meta['db.name'])}',` +
446+
'dddbs=\'test-mongodb\',' +
447+
'dde=\'tester\',' +
448+
`ddh='${encodeURIComponent(span.meta['out.host'])}',` +
449+
`ddps='${encodeURIComponent(span.meta.service)}',` +
450+
`ddpv='${ddpv}',` +
451+
`ddprs='${encodeURIComponent(span.meta['peer.service'])}'`
452+
)
453+
})
454+
.then(done)
455+
.catch(done)
456+
457+
server.insert(`test.${collection}`, [{ a: 1 }], () => {})
458+
})
459+
460+
it('DBM propagation should inject service mode after eixsting str comment', done => {
461+
agent
462+
.use(traces => {
463+
const span = traces[0][0]
464+
465+
expect(injectDbmCommandSpy.called).to.be.true
466+
const instrumentedCommand = injectDbmCommandSpy.getCall(0).returnValue
467+
expect(instrumentedCommand).to.have.property('comment')
468+
expect(instrumentedCommand.comment).to.equal(
469+
'test comment,' +
470+
`dddb='${encodeURIComponent(span.meta['db.name'])}',` +
471+
'dddbs=\'test-mongodb\',' +
472+
'dde=\'tester\',' +
473+
`ddh='${encodeURIComponent(span.meta['out.host'])}',` +
474+
`ddps='${encodeURIComponent(span.meta.service)}',` +
475+
`ddpv='${ddpv}',` +
476+
`ddprs='${encodeURIComponent(span.meta['peer.service'])}'`
477+
)
478+
})
479+
.then(done)
480+
.catch(done)
481+
482+
server.command(`test.${collection}`, {
483+
find: `test.${collection}`,
484+
query: {
485+
_id: Buffer.from('1234')
486+
},
487+
comment: 'test comment'
488+
}, () => {})
489+
})
490+
491+
it('DBM propagation should inject service mode after eixsting array comment', done => {
492+
agent
493+
.use(traces => {
494+
const span = traces[0][0]
495+
496+
expect(injectDbmCommandSpy.called).to.be.true
497+
const instrumentedCommand = injectDbmCommandSpy.getCall(0).returnValue
498+
expect(instrumentedCommand).to.have.property('comment')
499+
expect(instrumentedCommand.comment).to.deep.equal([
500+
'test comment',
501+
`dddb='${encodeURIComponent(span.meta['db.name'])}',` +
502+
'dddbs=\'test-mongodb\',' +
503+
'dde=\'tester\',' +
504+
`ddh='${encodeURIComponent(span.meta['out.host'])}',` +
505+
`ddps='${encodeURIComponent(span.meta.service)}',` +
506+
`ddpv='${ddpv}',` +
507+
`ddprs='${encodeURIComponent(span.meta['peer.service'])}'`
508+
])
509+
})
510+
.then(done)
511+
.catch(done)
512+
513+
server.command(`test.${collection}`, {
514+
find: `test.${collection}`,
515+
query: {
516+
_id: Buffer.from('1234')
517+
},
518+
comment: ['test comment']
519+
}, () => {})
520+
})
521+
})
522+
523+
describe('with dbmPropagationMode full', () => {
524+
before(() => {
525+
return agent.load('mongodb-core', { dbmPropagationMode: 'full' })
526+
})
527+
528+
after(() => {
529+
return agent.close({ ritmReset: false })
530+
})
531+
532+
beforeEach(done => {
533+
const Server = getServer()
534+
535+
server = new Server({
536+
host: '127.0.0.1',
537+
port: 27017,
538+
reconnect: false
539+
})
540+
541+
server.on('connect', () => done())
542+
server.on('error', done)
543+
544+
server.connect()
545+
546+
injectDbmCommandSpy = sinon.spy(MongodbCorePlugin.prototype, 'injectDbmCommand')
547+
})
548+
549+
afterEach(() => {
550+
injectDbmCommandSpy?.restore()
551+
})
552+
553+
it('DBM propagation should inject full mode with traceparent as comment', done => {
554+
agent
555+
.use(traces => {
556+
const span = traces[0][0]
557+
const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0')
558+
const spanId = span.span_id.toString(16).padStart(16, '0')
559+
560+
expect(injectDbmCommandSpy.called).to.be.true
561+
const instrumentedCommand = injectDbmCommandSpy.getCall(0).returnValue
562+
expect(instrumentedCommand).to.have.property('comment')
563+
expect(instrumentedCommand.comment).to.equal(
564+
`dddb='${encodeURIComponent(span.meta['db.name'])}',` +
565+
'dddbs=\'test-mongodb\',' +
566+
'dde=\'tester\',' +
567+
`ddh='${encodeURIComponent(span.meta['out.host'])}',` +
568+
`ddps='${encodeURIComponent(span.meta.service)}',` +
569+
`ddpv='${ddpv}',` +
570+
`ddprs='${encodeURIComponent(span.meta['peer.service'])}',` +
571+
`traceparent='00-${traceId}-${spanId}-00'`
572+
)
573+
})
574+
.then(done)
575+
.catch(done)
576+
577+
server.insert(`test.${collection}`, [{ a: 1 }], () => {})
578+
})
579+
})
400580
})
401581
})
402582
})

‎packages/datadog-plugin-mongodb-core/test/mongodb.spec.js

+108
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
'use strict'
22

3+
const sinon = require('sinon')
34
const semver = require('semver')
45
const agent = require('../../dd-trace/test/plugins/agent')
56
const { expectedSchema, rawExpectedSchema } = require('./naming')
67

8+
const MongodbCorePlugin = require('../../datadog-plugin-mongodb-core/src/index')
9+
const ddpv = require('mocha/package.json').version
10+
711
const withTopologies = fn => {
812
const isOldNode = semver.satisfies(process.version, '<=14')
913
const range = isOldNode ? '>=2 <6' : '>=2' // TODO: remove when 3.x support is removed.
@@ -44,6 +48,7 @@ describe('Plugin', () => {
4448
let collection
4549
let db
4650
let BSON
51+
let injectDbmCommandSpy
4752

4853
describe('mongodb-core', () => {
4954
withTopologies(createClient => {
@@ -334,6 +339,109 @@ describe('Plugin', () => {
334339
}
335340
)
336341
})
342+
343+
describe('with dbmPropagationMode service', () => {
344+
before(() => {
345+
return agent.load('mongodb-core', {
346+
dbmPropagationMode: 'service'
347+
})
348+
})
349+
350+
after(() => {
351+
return agent.close({ ritmReset: false })
352+
})
353+
354+
beforeEach(async () => {
355+
client = await createClient()
356+
db = client.db('test')
357+
collection = db.collection(collectionName)
358+
359+
injectDbmCommandSpy = sinon.spy(MongodbCorePlugin.prototype, 'injectDbmCommand')
360+
})
361+
362+
afterEach(() => {
363+
injectDbmCommandSpy?.restore()
364+
})
365+
366+
it('DBM propagation should inject service mode as comment', done => {
367+
agent
368+
.use(traces => {
369+
const span = traces[0][0]
370+
371+
expect(injectDbmCommandSpy.called).to.be.true
372+
const instrumentedCommand = injectDbmCommandSpy.getCall(0).returnValue
373+
expect(instrumentedCommand).to.have.property('comment')
374+
expect(instrumentedCommand.comment).to.equal(
375+
`dddb='${encodeURIComponent(span.meta['db.name'])}',` +
376+
'dddbs=\'test-mongodb\',' +
377+
'dde=\'tester\',' +
378+
`ddh='${encodeURIComponent(span.meta['out.host'])}',` +
379+
`ddps='${encodeURIComponent(span.meta.service)}',` +
380+
`ddpv='${ddpv}',` +
381+
`ddprs='${encodeURIComponent(span.meta['peer.service'])}'`
382+
)
383+
})
384+
.then(done)
385+
.catch(done)
386+
387+
collection.find({
388+
_id: Buffer.from('1234')
389+
}).toArray()
390+
})
391+
})
392+
393+
describe('with dbmPropagationMode full', () => {
394+
before(() => {
395+
return agent.load('mongodb-core', {
396+
dbmPropagationMode: 'full'
397+
})
398+
})
399+
400+
after(() => {
401+
return agent.close({ ritmReset: false })
402+
})
403+
404+
beforeEach(async () => {
405+
client = await createClient()
406+
db = client.db('test')
407+
collection = db.collection(collectionName)
408+
409+
injectDbmCommandSpy = sinon.spy(MongodbCorePlugin.prototype, 'injectDbmCommand')
410+
})
411+
412+
afterEach(() => {
413+
injectDbmCommandSpy?.restore()
414+
})
415+
416+
it('DBM propagation should inject full mode with traceparent as comment', done => {
417+
agent
418+
.use(traces => {
419+
const span = traces[0][0]
420+
const traceId = span.meta['_dd.p.tid'] + span.trace_id.toString(16).padStart(16, '0')
421+
const spanId = span.span_id.toString(16).padStart(16, '0')
422+
423+
expect(injectDbmCommandSpy.called).to.be.true
424+
const instrumentedCommand = injectDbmCommandSpy.getCall(0).returnValue
425+
expect(instrumentedCommand).to.have.property('comment')
426+
expect(instrumentedCommand.comment).to.equal(
427+
`dddb='${encodeURIComponent(span.meta['db.name'])}',` +
428+
'dddbs=\'test-mongodb\',' +
429+
'dde=\'tester\',' +
430+
`ddh='${encodeURIComponent(span.meta['out.host'])}',` +
431+
`ddps='${encodeURIComponent(span.meta.service)}',` +
432+
`ddpv='${ddpv}',` +
433+
`ddprs='${encodeURIComponent(span.meta['peer.service'])}',` +
434+
`traceparent='00-${traceId}-${spanId}-00'`
435+
)
436+
})
437+
.then(done)
438+
.catch(done)
439+
440+
collection.find({
441+
_id: Buffer.from('1234')
442+
}).toArray()
443+
})
444+
})
337445
})
338446
})
339447
})

0 commit comments

Comments
 (0)
Please sign in to comment.