newrelic 8.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NEWS.md +15 -0
- package/THIRD_PARTY_NOTICES.md +28 -75
- package/bin/test-naming-rules.js +0 -1
- package/bin/tracetractor +0 -1
- package/lib/agent.js +51 -31
- package/lib/collector/api.js +2 -3
- package/lib/instrumentation/fastify/spec-builders.js +5 -0
- package/lib/instrumentation/fastify.js +5 -0
- package/lib/instrumentation/mongodb/common.js +186 -0
- package/lib/instrumentation/mongodb/constants.js +97 -0
- package/lib/instrumentation/mongodb/v2-mongo.js +108 -0
- package/lib/instrumentation/mongodb/v3-mongo.js +61 -0
- package/lib/instrumentation/mongodb/v4-mongo.js +96 -0
- package/lib/instrumentation/mongodb.js +17 -310
- package/lib/instrumentation/pg.js +35 -16
- package/package.json +9 -3
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
|
|
8
|
+
const DB_OPS = [
|
|
9
|
+
'addUser',
|
|
10
|
+
'authenticate',
|
|
11
|
+
'collection',
|
|
12
|
+
'collectionNames',
|
|
13
|
+
'collections',
|
|
14
|
+
'command',
|
|
15
|
+
'createCollection',
|
|
16
|
+
'createIndex',
|
|
17
|
+
'cursorInfo',
|
|
18
|
+
'dereference',
|
|
19
|
+
'dropCollection',
|
|
20
|
+
'dropDatabase',
|
|
21
|
+
'dropIndex',
|
|
22
|
+
'ensureIndex',
|
|
23
|
+
'eval',
|
|
24
|
+
'executeDbAdminCommand',
|
|
25
|
+
'indexInformation',
|
|
26
|
+
'logout',
|
|
27
|
+
'open',
|
|
28
|
+
'reIndex',
|
|
29
|
+
'removeUser',
|
|
30
|
+
'renameCollection',
|
|
31
|
+
'stats',
|
|
32
|
+
'_executeInsertCommand',
|
|
33
|
+
'_executeQueryCommand'
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
const COLLECTION_OPS = [
|
|
37
|
+
'aggregate',
|
|
38
|
+
'bulkWrite',
|
|
39
|
+
'count',
|
|
40
|
+
'createIndex',
|
|
41
|
+
'deleteMany',
|
|
42
|
+
'deleteOne',
|
|
43
|
+
'distinct',
|
|
44
|
+
'drop',
|
|
45
|
+
'dropAllIndexes',
|
|
46
|
+
'dropIndex',
|
|
47
|
+
'ensureIndex',
|
|
48
|
+
'findAndModify',
|
|
49
|
+
'findAndRemove',
|
|
50
|
+
'findOne',
|
|
51
|
+
'findOneAndDelete',
|
|
52
|
+
'findOneAndReplace',
|
|
53
|
+
'findOneAndUpdate',
|
|
54
|
+
'geoHaystackSearch',
|
|
55
|
+
'geoNear',
|
|
56
|
+
'group',
|
|
57
|
+
'indexes',
|
|
58
|
+
'indexExists',
|
|
59
|
+
'indexInformation',
|
|
60
|
+
'insert',
|
|
61
|
+
'insertMany',
|
|
62
|
+
'insertOne',
|
|
63
|
+
'isCapped',
|
|
64
|
+
'mapReduce',
|
|
65
|
+
'options',
|
|
66
|
+
'parallelCollectionScan',
|
|
67
|
+
'reIndex',
|
|
68
|
+
'remove',
|
|
69
|
+
'rename',
|
|
70
|
+
'replaceOne',
|
|
71
|
+
'save',
|
|
72
|
+
'stats',
|
|
73
|
+
'update',
|
|
74
|
+
'updateMany',
|
|
75
|
+
'updateOne'
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
const GRID_OPS = [
|
|
79
|
+
'put',
|
|
80
|
+
'get',
|
|
81
|
+
'delete'
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
const CURSOR_OPS = [
|
|
85
|
+
'nextObject',
|
|
86
|
+
'next',
|
|
87
|
+
'toArray',
|
|
88
|
+
'count',
|
|
89
|
+
'explain'
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
COLLECTION_OPS,
|
|
94
|
+
CURSOR_OPS,
|
|
95
|
+
DB_OPS,
|
|
96
|
+
GRID_OPS
|
|
97
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
captureAttributesOnStarted,
|
|
10
|
+
makeQueryDescFunc
|
|
11
|
+
} = require('./common')
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* parser used to grab the collection and operation
|
|
15
|
+
* from a running query
|
|
16
|
+
*
|
|
17
|
+
* @param {Object} operation
|
|
18
|
+
*/
|
|
19
|
+
function queryParser(operation) {
|
|
20
|
+
let collection = this.collectionName || 'unknown'
|
|
21
|
+
if (this.ns) {
|
|
22
|
+
collection = this.ns.split(/\./)[1] || collection
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {operation, collection}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Registers relevant instrumentation for mongo <= 3.0.6
|
|
29
|
+
* and >= 2. This relies on the built-in "APM" hook points
|
|
30
|
+
* to instrument their provided objects as well as sets
|
|
31
|
+
* up a listener for when commands start to properly
|
|
32
|
+
* add necessary attributes to segments
|
|
33
|
+
*
|
|
34
|
+
* @param {Shim} shim
|
|
35
|
+
* @param {Object} mongodb resolved package
|
|
36
|
+
*/
|
|
37
|
+
module.exports = function instrument(shim, mongodb) {
|
|
38
|
+
shim.setParser(queryParser)
|
|
39
|
+
|
|
40
|
+
const recordDesc = {
|
|
41
|
+
'Gridstore': {isQuery: false, makeDescFunc: function makeGridDesc(opName) {
|
|
42
|
+
return {name:'GridFS-' + opName, callback: shim.LAST}
|
|
43
|
+
}},
|
|
44
|
+
'OrderedBulkOperation': {isQuery: true, makeDescFunc: makeQueryDescFunc},
|
|
45
|
+
'UnorderedBulkOperation': {isQuery: true, makeDescFunc: makeQueryDescFunc},
|
|
46
|
+
'CommandCursor': {isQuery: true, makeDescFunc: makeQueryDescFunc},
|
|
47
|
+
'AggregationCursor': {isQuery: true, makeDescFunc: makeQueryDescFunc},
|
|
48
|
+
'Cursor': {isQuery: true, makeDescFunc: makeQueryDescFunc},
|
|
49
|
+
'Collection': {isQuery: true, makeDescFunc: makeQueryDescFunc},
|
|
50
|
+
'Db': {isQuery: false, makeDescFunc: function makeDbDesc() {
|
|
51
|
+
return {callback: shim.LAST}
|
|
52
|
+
}}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// instrument using the apm api
|
|
56
|
+
const instrumenter = mongodb.instrument(Object.create(null), instrumentModules)
|
|
57
|
+
captureAttributesOnStarted(shim, instrumenter)
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Every module groups instrumentations by their
|
|
61
|
+
* promise, callback, return permutations
|
|
62
|
+
* Iterate over permutations and properly
|
|
63
|
+
* wrap depending on the `recordDesc` above
|
|
64
|
+
* See: https://github.com/mongodb/node-mongodb-native/blob/v3.0.5/lib/collection.js#L384
|
|
65
|
+
*/
|
|
66
|
+
function instrumentModules(_, modules) {
|
|
67
|
+
modules.forEach((module) => {
|
|
68
|
+
const { obj, instrumentations, name } = module
|
|
69
|
+
instrumentations.forEach((meta) => {
|
|
70
|
+
applyInstrumentation(name, obj, meta)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Iterate over methods on object and lookup in `recordDesc` to decide
|
|
77
|
+
* if it needs to be wrapped as an operation or query
|
|
78
|
+
*
|
|
79
|
+
* @param {string} objectName name of class getting instrumented
|
|
80
|
+
* @param {Object} object reference to the class getting instrumented
|
|
81
|
+
* @param {Define} meta describes the methods and if they are callbacks
|
|
82
|
+
* promises, and return values
|
|
83
|
+
*/
|
|
84
|
+
function applyInstrumentation(objectName, object, meta) {
|
|
85
|
+
const { methods, options } = meta
|
|
86
|
+
if (options.callback) {
|
|
87
|
+
methods.forEach((method) => {
|
|
88
|
+
const { isQuery, makeDescFunc } = recordDesc[objectName]
|
|
89
|
+
const proto = object.prototype
|
|
90
|
+
if (isQuery) {
|
|
91
|
+
shim.recordQuery(proto, method, makeDescFunc(shim, method))
|
|
92
|
+
} else if (isQuery === false) { // could be unset
|
|
93
|
+
shim.recordOperation(proto, method, makeDescFunc(shim, method))
|
|
94
|
+
} else {
|
|
95
|
+
shim.logger.trace('No wrapping method found for %s', objectName)
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// the cursor object implements Readable stream and internally calls nextObject on
|
|
101
|
+
// each read, in which case we do not want to record each nextObject() call
|
|
102
|
+
if (/Cursor$/.test(objectName)) {
|
|
103
|
+
shim.recordOperation(object.prototype, 'pipe')
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
captureAttributesOnStarted,
|
|
10
|
+
instrumentCollection,
|
|
11
|
+
instrumentCursor,
|
|
12
|
+
instrumentDb
|
|
13
|
+
} = require('./common')
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* parser used to grab the collection and operation
|
|
17
|
+
* on every mongo operation
|
|
18
|
+
*
|
|
19
|
+
* @param {Object} operation
|
|
20
|
+
*/
|
|
21
|
+
function queryParser(operation) {
|
|
22
|
+
let collection = this.collectionName || 'unknown'
|
|
23
|
+
// in v3.3.0 aggregate commands added the collection
|
|
24
|
+
// to target
|
|
25
|
+
if (this.operation && this.operation.target) {
|
|
26
|
+
collection = this.operation.target
|
|
27
|
+
} else if (this.ns) {
|
|
28
|
+
collection = this.ns.split(/\./)[1] || collection
|
|
29
|
+
}
|
|
30
|
+
return {operation, collection}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Registers relevant instrumentation for mongo >= 3.0.6
|
|
35
|
+
* In 3.0.6 they refactored their "APM" module which removed
|
|
36
|
+
* a lot of niceities around instrumentation classes.
|
|
37
|
+
* see: https://github.com/mongodb/node-mongodb-native/pull/1675/files
|
|
38
|
+
* This reverts back to instrumenting pre-canned methods on classes
|
|
39
|
+
* as well as sets up a listener for when commands start to properly
|
|
40
|
+
* add necessary attributes to segments
|
|
41
|
+
*
|
|
42
|
+
* @param {Shim} shim
|
|
43
|
+
* @param {Object} mongodb resolved package
|
|
44
|
+
*/
|
|
45
|
+
module.exports = function instrument(shim, mongodb) {
|
|
46
|
+
shim.setParser(queryParser)
|
|
47
|
+
const instrumenter = mongodb.instrument(Object.create(null), () => {})
|
|
48
|
+
captureAttributesOnStarted(shim, instrumenter)
|
|
49
|
+
instrumentCursor(shim, mongodb.Cursor)
|
|
50
|
+
instrumentCursor(shim, shim.require('./lib/aggregation_cursor'))
|
|
51
|
+
instrumentCursor(shim, shim.require('./lib/command_cursor'))
|
|
52
|
+
instrumentCollection(shim, mongodb.Collection)
|
|
53
|
+
instrumentDb(shim, mongodb.Db)
|
|
54
|
+
|
|
55
|
+
// calling instrument sets up listeners for a few events
|
|
56
|
+
// we should restore this on unload to avoid leaking
|
|
57
|
+
// event emitters
|
|
58
|
+
shim.agent.once('unload', function uninstrumentMongo() {
|
|
59
|
+
instrumenter.uninstrument()
|
|
60
|
+
})
|
|
61
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
instrumentCollection,
|
|
10
|
+
instrumentCursor,
|
|
11
|
+
instrumentDb
|
|
12
|
+
} = require('./common')
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* parser used to grab the collection and operation
|
|
16
|
+
* from a running query
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} operation
|
|
19
|
+
*/
|
|
20
|
+
function queryParser(operation) {
|
|
21
|
+
let collection = this.collectionName || 'unknown'
|
|
22
|
+
|
|
23
|
+
// cursor methods have collection on namespace.collection
|
|
24
|
+
if (this.namespace && this.namespace.collection) {
|
|
25
|
+
collection = this.namespace.collection
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {operation, collection}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* `commandStarted` handler used to
|
|
33
|
+
* update host, port and database_name
|
|
34
|
+
* on segment attributes
|
|
35
|
+
*
|
|
36
|
+
* @param {Shim} shim
|
|
37
|
+
* @param {CommandStartedEvent} evnt
|
|
38
|
+
*/
|
|
39
|
+
function cmdStartedHandler(shim, evnt) {
|
|
40
|
+
if (evnt.connectionId) {
|
|
41
|
+
let [host,port] = evnt.address.split(':')
|
|
42
|
+
|
|
43
|
+
// connection via socket get port from client.url
|
|
44
|
+
// which looks like "mongodb://%2Ftmp%2Fmongodb-27017.sock"
|
|
45
|
+
if (['undefined'].includes(host, port)) {
|
|
46
|
+
host = 'localhost'
|
|
47
|
+
// socket path is uri encoded
|
|
48
|
+
// decode it to properly name attrs
|
|
49
|
+
const socketPath = this.s.url.split(':')
|
|
50
|
+
if (socketPath.length && socketPath[1].startsWith('//')) {
|
|
51
|
+
port = decodeURIComponent(socketPath[1].substr(2))
|
|
52
|
+
}
|
|
53
|
+
} else if (host === '127.0.0.1') {
|
|
54
|
+
host = 'localhost'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
shim.captureInstanceAttributes(host, port, evnt.databaseName)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* function executed when client.connect is called
|
|
63
|
+
* enable APM(monitorCommands) and add the
|
|
64
|
+
* `commandStarted` listener
|
|
65
|
+
*
|
|
66
|
+
* @param {Shim} shim
|
|
67
|
+
*/
|
|
68
|
+
function wrapConnect(shim) {
|
|
69
|
+
this.monitorCommands = true
|
|
70
|
+
this.on('commandStarted', cmdStartedHandler.bind(this, shim))
|
|
71
|
+
return { callback: shim.LAST }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Wraps connect to record as operation but also to add a listener
|
|
76
|
+
* for `commandStarted`. This will be emitted before every command starts
|
|
77
|
+
* so we can properly update the segment attributes with a more accurate
|
|
78
|
+
* host/port/database name
|
|
79
|
+
*
|
|
80
|
+
* @param {Shim} shim
|
|
81
|
+
* @param {MongoClient} MongoClient reference
|
|
82
|
+
*/
|
|
83
|
+
function instrumentMongoClient(shim, MongoClient) {
|
|
84
|
+
shim.recordOperation(MongoClient.prototype, 'connect', wrapConnect)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
module.exports = function instrument(shim, mongodb) {
|
|
89
|
+
shim.setParser(queryParser)
|
|
90
|
+
instrumentMongoClient(shim, mongodb.MongoClient)
|
|
91
|
+
instrumentCursor(shim, mongodb.AbstractCursor)
|
|
92
|
+
instrumentCursor(shim, mongodb.FindCursor)
|
|
93
|
+
instrumentCursor(shim, mongodb.AggregationCursor)
|
|
94
|
+
instrumentCollection(shim, mongodb.Collection)
|
|
95
|
+
instrumentDb(shim, mongodb.Db)
|
|
96
|
+
}
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
8
|
const semver = require('semver')
|
|
9
|
+
const instrument = require('./mongodb/v2-mongo')
|
|
10
|
+
const instrumentV3 = require('./mongodb/v3-mongo')
|
|
11
|
+
const instrumentV4 = require('./mongodb/v4-mongo')
|
|
9
12
|
|
|
10
13
|
// XXX: When this instrumentation is modularized, update this thread
|
|
11
14
|
// with a cautionary note:
|
|
@@ -15,325 +18,29 @@ const semver = require('semver')
|
|
|
15
18
|
// snippet. The snippet will break once this file is moved from this
|
|
16
19
|
// location.
|
|
17
20
|
|
|
18
|
-
// legacy endpoint enumerations
|
|
19
|
-
const DB_OPS = [
|
|
20
|
-
'addUser',
|
|
21
|
-
'authenticate',
|
|
22
|
-
'collection',
|
|
23
|
-
'collectionNames',
|
|
24
|
-
'collections',
|
|
25
|
-
'command',
|
|
26
|
-
'createCollection',
|
|
27
|
-
'createIndex',
|
|
28
|
-
'cursorInfo',
|
|
29
|
-
'dereference',
|
|
30
|
-
'dropCollection',
|
|
31
|
-
'dropDatabase',
|
|
32
|
-
'dropIndex',
|
|
33
|
-
'ensureIndex',
|
|
34
|
-
'eval',
|
|
35
|
-
'executeDbAdminCommand',
|
|
36
|
-
'indexInformation',
|
|
37
|
-
'logout',
|
|
38
|
-
'open',
|
|
39
|
-
'reIndex',
|
|
40
|
-
'removeUser',
|
|
41
|
-
'renameCollection',
|
|
42
|
-
'stats',
|
|
43
|
-
'_executeInsertCommand',
|
|
44
|
-
'_executeQueryCommand'
|
|
45
|
-
]
|
|
46
|
-
|
|
47
|
-
const COLLECTION_OPS = [
|
|
48
|
-
'aggregate',
|
|
49
|
-
'bulkWrite',
|
|
50
|
-
'count',
|
|
51
|
-
'createIndex',
|
|
52
|
-
'deleteMany',
|
|
53
|
-
'deleteOne',
|
|
54
|
-
'distinct',
|
|
55
|
-
'drop',
|
|
56
|
-
'dropAllIndexes',
|
|
57
|
-
'dropIndex',
|
|
58
|
-
'ensureIndex',
|
|
59
|
-
'findAndModify',
|
|
60
|
-
'findAndRemove',
|
|
61
|
-
'findOne',
|
|
62
|
-
'findOneAndDelete',
|
|
63
|
-
'findOneAndReplace',
|
|
64
|
-
'findOneAndUpdate',
|
|
65
|
-
'geoHaystackSearch',
|
|
66
|
-
'geoNear',
|
|
67
|
-
'group',
|
|
68
|
-
'indexes',
|
|
69
|
-
'indexExists',
|
|
70
|
-
'indexInformation',
|
|
71
|
-
'insert',
|
|
72
|
-
'insertMany',
|
|
73
|
-
'insertOne',
|
|
74
|
-
'isCapped',
|
|
75
|
-
'mapReduce',
|
|
76
|
-
'options',
|
|
77
|
-
'parallelCollectionScan',
|
|
78
|
-
'reIndex',
|
|
79
|
-
'remove',
|
|
80
|
-
'rename',
|
|
81
|
-
'replaceOne',
|
|
82
|
-
'save',
|
|
83
|
-
'stats',
|
|
84
|
-
'update',
|
|
85
|
-
'updateMany',
|
|
86
|
-
'updateOne'
|
|
87
|
-
]
|
|
88
|
-
|
|
89
|
-
const GRID_OPS = [
|
|
90
|
-
'put',
|
|
91
|
-
'get',
|
|
92
|
-
'delete'
|
|
93
|
-
]
|
|
94
|
-
|
|
95
|
-
const CURSOR_OPS = [
|
|
96
|
-
'nextObject',
|
|
97
|
-
'next',
|
|
98
|
-
'toArray',
|
|
99
|
-
'count',
|
|
100
|
-
'explain'
|
|
101
|
-
]
|
|
102
|
-
|
|
103
21
|
module.exports = initialize
|
|
104
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Registers the query parser, and relevant instrumentation
|
|
25
|
+
* based on version of mongodb
|
|
26
|
+
*
|
|
27
|
+
* @param {Agent} agent
|
|
28
|
+
* @param {Object} mongodb resolved package
|
|
29
|
+
* @param {string} moduleName name of module
|
|
30
|
+
* @param {Shim} shim
|
|
31
|
+
*/
|
|
105
32
|
function initialize(agent, mongodb, moduleName, shim) {
|
|
106
33
|
if (!mongodb) return
|
|
107
34
|
|
|
108
35
|
shim.setDatastore(shim.MONGODB)
|
|
109
|
-
shim.setParser(function mongoQueryParser(operation) {
|
|
110
|
-
let collection = this.collectionName || 'unknown'
|
|
111
|
-
if (this.collection && this.collection.collectionName) {
|
|
112
|
-
collection = this.collection.collectionName
|
|
113
|
-
} else if (this.s && this.s.name) {
|
|
114
|
-
collection = this.s.name
|
|
115
|
-
} else if (this.ns) {
|
|
116
|
-
collection = this.ns.split(/\./)[1] || collection
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return {operation, collection}
|
|
120
|
-
})
|
|
121
36
|
|
|
122
37
|
const mongoVersion = shim.require('./package.json').version
|
|
123
|
-
if (semver.satisfies(mongoVersion, '>=
|
|
124
|
-
|
|
125
|
-
} else if (
|
|
126
|
-
|
|
38
|
+
if (semver.satisfies(mongoVersion, '>=4.0.0')) {
|
|
39
|
+
instrumentV4(shim, mongodb)
|
|
40
|
+
} else if (semver.satisfies(mongoVersion, '>=3.0.6')) {
|
|
41
|
+
instrumentV3(shim, mongodb)
|
|
127
42
|
} else {
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function instrument306(shim, mongodb) {
|
|
133
|
-
const instrumenter = mongodb.instrument(Object.create(null), () => {})
|
|
134
|
-
captureAttributesOnStarted(shim, instrumenter)
|
|
135
|
-
instrumentLegacy(shim, mongodb)
|
|
136
|
-
|
|
137
|
-
if (shim.isFunction(instrumenter.uninstrument)) {
|
|
138
|
-
shim.agent.once('unload', function uninstrumentMongo() {
|
|
139
|
-
instrumenter.uninstrument()
|
|
140
|
-
})
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function instrumentInstrument(shim, mongodb) {
|
|
145
|
-
const recordDesc = {
|
|
146
|
-
'Gridstore': {isQuery: false, makeDesc: function makeGridDesc(opName) {
|
|
147
|
-
return {name:'GridFS-' + opName, callback: shim.LAST}
|
|
148
|
-
}},
|
|
149
|
-
'OrderedBulkOperation': {isQuery: true, makeDesc: makeQueryDescFunc},
|
|
150
|
-
'UnorderedBulkOperation': {isQuery: true, makeDesc: makeQueryDescFunc},
|
|
151
|
-
'CommandCursor': {isQuery: true, makeDesc: makeQueryDescFunc},
|
|
152
|
-
'AggregationCursor': {isQuery: true, makeDesc: makeQueryDescFunc},
|
|
153
|
-
'Cursor': {isQuery: true, makeDesc: makeQueryDescFunc},
|
|
154
|
-
'Collection': {isQuery: true, makeDesc: makeQueryDescFunc},
|
|
155
|
-
'Db': {isQuery: false, makeDesc: function makeDbDesc() {
|
|
156
|
-
return {callback: shim.LAST}
|
|
157
|
-
}}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// instrument using the apm api
|
|
161
|
-
const instrumenter = mongodb.instrument(Object.create(null), instrumentModules)
|
|
162
|
-
captureAttributesOnStarted(shim, instrumenter)
|
|
163
|
-
|
|
164
|
-
function instrumentModules(err, instrumentations) {
|
|
165
|
-
if (err) {
|
|
166
|
-
shim.logger
|
|
167
|
-
.trace('Unable to instrument mongo using the apm api due to error: %s', err)
|
|
168
|
-
// fallback to legacy instrumentation?
|
|
169
|
-
return
|
|
170
|
-
}
|
|
171
|
-
instrumentations.forEach(instrumentModule)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function instrumentModule(module) {
|
|
175
|
-
var object = module.obj
|
|
176
|
-
var instrumentations = module.instrumentations
|
|
177
|
-
for (var i = 0; i < instrumentations.length; i++) {
|
|
178
|
-
applyInstrumentation(module.name, object, instrumentations[i])
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function applyInstrumentation(objectName, object, instrumentation) {
|
|
183
|
-
var methods = instrumentation.methods
|
|
184
|
-
var methodOptions = instrumentation.options
|
|
185
|
-
if (methodOptions.callback) {
|
|
186
|
-
for (var j = 0; j < methods.length; j++) {
|
|
187
|
-
var method = methods[j]
|
|
188
|
-
|
|
189
|
-
var isQuery = recordDesc[objectName].isQuery
|
|
190
|
-
var makeDescFunc = recordDesc[objectName].makeDesc
|
|
191
|
-
var proto = object.prototype
|
|
192
|
-
if (isQuery) {
|
|
193
|
-
shim.recordQuery(proto, method, makeDescFunc(shim, method))
|
|
194
|
-
} else if (isQuery === false) { // could be unset
|
|
195
|
-
shim.recordOperation(proto, method, makeDescFunc(shim, method))
|
|
196
|
-
} else {
|
|
197
|
-
shim.logger.trace('No wrapping method found for %s', objectName)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// the cursor object implements Readable stream and internally calls nextObject on
|
|
203
|
-
// each read, in which case we do not want to record each nextObject() call
|
|
204
|
-
if (/Cursor$/.test(objectName)) {
|
|
205
|
-
shim.recordOperation(object.prototype, 'pipe')
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function captureAttributesOnStarted(shim, instrumenter) {
|
|
211
|
-
instrumenter.on('started', function onMongoEventStarted(evnt) {
|
|
212
|
-
// This assumes that this `started` event is fired _after_ our wrapper
|
|
213
|
-
// starts and creates the segment. We perform a check of the segment name
|
|
214
|
-
// out of an excess of caution.
|
|
215
|
-
const connId = evnt.connectionId
|
|
216
|
-
if (connId) {
|
|
217
|
-
// Mongo sticks the path to the domain socket in the "host" slot, but we
|
|
218
|
-
// want it in the "port", so if we have a domain socket we need to change
|
|
219
|
-
// the order of our parameters.
|
|
220
|
-
if (typeof connId === 'string') {
|
|
221
|
-
const parts = connId.split(':')
|
|
222
|
-
if (parts.length && parts[0][0] === '/') {
|
|
223
|
-
shim.captureInstanceAttributes('localhost', parts[0], evnt.databaseName)
|
|
224
|
-
} else {
|
|
225
|
-
shim.captureInstanceAttributes(parts[0], parts[1], evnt.databaseName)
|
|
226
|
-
}
|
|
227
|
-
} else if (connId.domainSocket) {
|
|
228
|
-
shim.captureInstanceAttributes('localhost', connId.host, evnt.databaseName)
|
|
229
|
-
} else {
|
|
230
|
-
shim.captureInstanceAttributes(connId.host, connId.port, evnt.databaseName)
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
})
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function instrumentLegacy(shim, mongodb) {
|
|
237
|
-
instrumentCursor(mongodb.Cursor)
|
|
238
|
-
instrumentCursor(shim.require('./lib/aggregation_cursor'))
|
|
239
|
-
instrumentCursor(shim.require('./lib/command_cursor'))
|
|
240
|
-
|
|
241
|
-
if (mongodb.Collection && mongodb.Collection.prototype) {
|
|
242
|
-
const proto = mongodb.Collection.prototype
|
|
243
|
-
for (let i = 0; i < COLLECTION_OPS.length; i++) {
|
|
244
|
-
shim.recordQuery(
|
|
245
|
-
proto,
|
|
246
|
-
COLLECTION_OPS[i],
|
|
247
|
-
makeQueryDescFunc(shim, COLLECTION_OPS[i])
|
|
248
|
-
)
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (mongodb.Grid && mongodb.Grid.prototype) {
|
|
253
|
-
const proto = mongodb.Grid.prototype
|
|
254
|
-
for (let i = 0; i < CURSOR_OPS.length; i++) {
|
|
255
|
-
shim.recordOperation(proto, GRID_OPS[i],
|
|
256
|
-
{name:'GridFS-' + GRID_OPS[i], callback: shim.LAST})
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (mongodb.Db && mongodb.Db.prototype) {
|
|
261
|
-
const proto = mongodb.Db.prototype
|
|
262
|
-
shim.recordOperation(proto, DB_OPS, {callback: shim.LAST})
|
|
263
|
-
shim.recordOperation(mongodb.Db, 'connect', {callback: shim.LAST})
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function instrumentCursor(Cursor) {
|
|
267
|
-
if (Cursor && Cursor.prototype) {
|
|
268
|
-
const proto = Cursor.prototype
|
|
269
|
-
for (let i = 0; i < CURSOR_OPS.length; i++) {
|
|
270
|
-
shim.recordQuery(proto, CURSOR_OPS[i], makeQueryDescFunc(shim, CURSOR_OPS[i]))
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
shim.recordQuery(proto, 'each', makeQueryDescFunc(shim, 'each'))
|
|
274
|
-
shim.recordOperation(proto, 'pipe')
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function makeQueryDescFunc(shim, methodName) {
|
|
280
|
-
if (methodName === 'each') {
|
|
281
|
-
return function eachDescFunc() {
|
|
282
|
-
const parameters = getInstanceAttributeParameters(shim, this)
|
|
283
|
-
return {query: methodName, parameters, rowCallback: shim.LAST}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return function queryDescFunc() {
|
|
288
|
-
// segment name does not actually use query string
|
|
289
|
-
// method name is set as query so the query parser has access to the op name
|
|
290
|
-
const parameters = getInstanceAttributeParameters(shim, this)
|
|
291
|
-
return {query: methodName, parameters, promise: true, callback: shim.LAST}
|
|
43
|
+
instrument(shim, mongodb)
|
|
292
44
|
}
|
|
293
45
|
}
|
|
294
46
|
|
|
295
|
-
function getInstanceAttributeParameters(shim, obj) {
|
|
296
|
-
if (obj.db && obj.db.serverConfig) {
|
|
297
|
-
shim.logger.trace('Adding datastore instance attributes from obj.db.serverConfig')
|
|
298
|
-
const serverConfig = obj.db.serverConfig
|
|
299
|
-
const db = serverConfig.db || serverConfig.dbInstance
|
|
300
|
-
return doCapture(serverConfig, db && db.databaseName)
|
|
301
|
-
} else if (obj.s && obj.s.db && obj.s.topology) {
|
|
302
|
-
shim.logger.trace(
|
|
303
|
-
'Adding datastore instance attributes from obj.s.db + obj.s.topology'
|
|
304
|
-
)
|
|
305
|
-
const databaseName = obj.s.db.databaseName || null
|
|
306
|
-
const topology = obj.s.topology
|
|
307
|
-
if (topology.s && topology.s.options) {
|
|
308
|
-
return doCapture(topology.s.options, databaseName)
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
shim.logger.trace('Could not find datastore instance attributes.')
|
|
313
|
-
return {
|
|
314
|
-
host: null,
|
|
315
|
-
port_path_or_id: null,
|
|
316
|
-
database_name: null
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function doCapture(conf, database) {
|
|
320
|
-
let host = conf.host
|
|
321
|
-
let port = conf.port
|
|
322
|
-
|
|
323
|
-
// If using a domain socket, mongo stores the path as the host name, but we
|
|
324
|
-
// pass it through the port value.
|
|
325
|
-
if (
|
|
326
|
-
(conf.socketOptions && conf.socketOptions.domainSocket) ||
|
|
327
|
-
/\.sock$/.test(host)
|
|
328
|
-
) {
|
|
329
|
-
port = host
|
|
330
|
-
host = 'localhost'
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
return {
|
|
334
|
-
host: host,
|
|
335
|
-
port_path_or_id: port,
|
|
336
|
-
database_name: database
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|