Skip to content

Commit

Permalink
feat: set "destination" context for "mongodb" instrumentation (elasti…
Browse files Browse the repository at this point in the history
  • Loading branch information
trentm authored Nov 26, 2020
1 parent 3bf8c9f commit e6018a6
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 6 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ endif::[]

[float]
===== Features
* feat: transactionIgnoreUrl wildcard matching {pull}1870[#1870]

* feat: Set "destination" context on spans for "mongodb". {pull}1893[#1893] +
This allows Kibana APM Service Maps to show a "mongodb" node for services using
the https://www.npmjs.com/package/mongodb[mongodb] package (which includes
mongoose and mongojs).

* feat: transactionIgnoreUrl wildcard matching {pull}1870[#1870] +
Allows users to ignore URLs using simple wildcard matching patterns that behave
the same across language agents. See https://github.com/elastic/apm/issues/144

Expand Down
2 changes: 1 addition & 1 deletion docs/supported-technologies.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ The Node.js agent will automatically instrument the following modules to give yo
|https://www.npmjs.com/package/memcached[memcached] |>=2.2.0  |Will instrument all commands.
|https://www.npmjs.com/package/mongodb-core[mongodb-core] |>=1.2.19 <4 |Will instrument all queries.
A lot of higher level MongoDB modules use mongodb-core,
so those should be supported as well
so those should be supported as well.
|https://www.npmjs.com/package/mongodb[mongodb] |>=2.0.0 <3.3.0 |Supported via mongodb-core
|https://www.npmjs.com/package/mongodb[mongodb] |^3.3.0 |Will instrument all queries
|https://www.npmjs.com/package/mongojs[mongojs] |>=1.0.0 <2.7.0 |Supported via mongodb-core
Expand Down
31 changes: 30 additions & 1 deletion lib/instrumentation/modules/mongodb.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
'use strict'

const semver = require('semver')
const { getDBDestination } = require('../context')

// Match expected `<hostname>:<port>`.
const HOSTNAME_PORT_RE = /^([^:]+):(\d+)$/

module.exports = (mongodb, agent, { version, enabled }) => {
if (!enabled) return mongodb
if (!semver.satisfies(version, '>=3.3')) {
agent.logger.debug('mongodb version %s not supported - aborting...', version)
agent.logger.debug('mongodb version %s not instrumented (mongodb <3.3 is instrumented via mongodb-core)', version)
return mongodb
}

Expand All @@ -19,6 +23,18 @@ module.exports = (mongodb, agent, { version, enabled }) => {
return mongodb

function onStart (event) {
// `event` is a `CommandStartedEvent`
// https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst#api
// E.g. with [email protected]:
// CommandStartedEvent {
// address: '127.0.0.1:27017',
// connectionId: 1,
// requestId: 1,
// databaseName: 'test',
// commandName: 'insert',
// command:
// { ... } }

const name = [
event.databaseName,
collectionFor(event),
Expand All @@ -28,6 +44,19 @@ module.exports = (mongodb, agent, { version, enabled }) => {
const span = agent.startSpan(name, 'db', 'mongodb', 'query')
if (span) {
activeSpans.set(event.requestId, span)

// Per the following code it looks like "<hostname>:<port>" should be
// available via the `address` or `connectionId` field.
// https://github.com/mongodb/node-mongodb-native/blob/dd356f0ede/lib/core/connection/apm.js#L155-L169
const address = event.address || event.connectionId
let match
if (address && typeof (address) === 'string' &&
(match = HOSTNAME_PORT_RE.exec(address))) {
span.setDestinationContext(
getDBDestination(span, match[1], match[2]))
} else {
agent.logger.trace('could not set destination context on mongodb span from address=%j', address)
}
}
}

Expand Down
21 changes: 18 additions & 3 deletions test/instrumentation/modules/mongodb.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const test = require('tape')

const mockClient = require('../../_mock_http_client_states')

const host = process.env.MONGODB_HOST || 'localhost'
const url = `mongodb://${host}:27017`

test('instrument simple command', function (t) {
resetAgent([
makeSpanTest(t, 'elasticapm.test.insert'),
Expand All @@ -24,9 +27,6 @@ test('instrument simple command', function (t) {
t.end()
})

const host = process.env.MONGODB_HOST || 'localhost'
const url = `mongodb://${host}:27017`

const server = new MongoClient(url, {
useUnifiedTopology: true,
useNewUrlParser: true
Expand Down Expand Up @@ -105,6 +105,21 @@ function makeSpanTest (t, name) {
t.strictEqual(span.type, 'db', 'span type is "db"')
t.strictEqual(span.subtype, 'mongodb', 'span subtype is "mongodb"')
t.strictEqual(span.action, 'query', 'span action is "query"')

// We can't easily assert destination.address because mongodb >3.5.0
// returns a resolved IP for the given connection hostname. In our CI
// setup, the host is set to "mongodb" which is a Docker container with
// some IP. We could `dns.resolve4()` here, but that's overkill I think.
t.ok(span.context.destination.address, 'context.destination.address is defined')
t.deepEqual(span.context.destination, {
service: {
name: 'mongodb',
resource: 'mongodb',
type: 'db'
},
address: span.context.destination.address,
port: 27017
}, 'span.context.destination')
}
}
}
Expand Down

0 comments on commit e6018a6

Please sign in to comment.