newrelic 7.5.2 → 8.3.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 +4989 -4886
- package/THIRD_PARTY_NOTICES.md +625 -216
- package/api.js +105 -174
- package/bin/test-naming-rules.js +38 -32
- package/bin/tracetractor +0 -1
- package/index.js +12 -18
- package/lib/agent.js +108 -111
- package/lib/aggregators/base-aggregator.js +1 -1
- package/lib/aggregators/event-aggregator.js +6 -6
- package/lib/attributes.js +12 -13
- package/lib/collector/api.js +32 -42
- package/lib/collector/facts.js +67 -65
- package/lib/collector/http-agents.js +30 -27
- package/lib/collector/parse-response.js +1 -2
- package/lib/collector/remote-method.js +43 -36
- package/lib/collector/serverless.js +13 -18
- package/lib/collector/ssl/certificates.js +1056 -1056
- package/lib/config/attribute-filter.js +90 -82
- package/lib/config/default.js +30 -18
- package/lib/config/env.js +17 -12
- package/lib/config/harvest-config-validator.js +1 -1
- package/lib/config/index.js +160 -177
- package/lib/config/merge-server-config.js +7 -5
- package/lib/custom-events/custom-event-aggregator.js +1 -1
- package/lib/db/parse-sql.js +6 -9
- package/lib/db/parsed-statement.js +6 -13
- package/lib/db/query-parsers/sql.js +6 -9
- package/lib/db/query-sample.js +7 -3
- package/lib/db/query-trace-aggregator.js +12 -12
- package/lib/db/utils.js +1 -1
- package/lib/environment.js +173 -138
- package/lib/errors/error-collector.js +18 -31
- package/lib/errors/error-event-aggregator.js +1 -1
- package/lib/errors/error-trace-aggregator.js +2 -3
- package/lib/errors/helper.js +21 -30
- package/lib/errors/index.js +16 -28
- package/lib/feature_flags.js +5 -8
- package/lib/grpc/connection/states.js +6 -6
- package/lib/grpc/connection.js +35 -46
- package/lib/header-attributes.js +49 -53
- package/lib/instrumentation/amqplib.js +8 -17
- package/lib/instrumentation/bluebird.js +61 -20
- package/lib/instrumentation/cassandra-driver.js +2 -2
- package/lib/instrumentation/connect.js +4 -3
- package/lib/instrumentation/core/async_hooks.js +11 -4
- package/lib/instrumentation/core/child_process.js +20 -28
- package/lib/instrumentation/core/crypto.js +1 -7
- package/lib/instrumentation/core/dns.js +3 -7
- package/lib/instrumentation/core/domain.js +2 -8
- package/lib/instrumentation/core/fs.js +22 -37
- package/lib/instrumentation/core/globals.js +3 -7
- package/lib/instrumentation/core/http-outbound.js +15 -40
- package/lib/instrumentation/core/http.js +29 -63
- package/lib/instrumentation/core/inspector.js +6 -10
- package/lib/instrumentation/core/net.js +3 -1
- package/lib/instrumentation/core/timers.js +17 -24
- package/lib/instrumentation/core/zlib.js +2 -10
- package/lib/instrumentation/express.js +4 -2
- package/lib/instrumentation/fastify/spec-builders.js +5 -0
- package/lib/instrumentation/fastify.js +37 -10
- package/lib/instrumentation/hapi/hapi-17.js +9 -10
- package/lib/instrumentation/hapi.js +15 -15
- package/lib/instrumentation/ioredis.js +1 -2
- package/lib/instrumentation/memcached.js +39 -43
- package/lib/instrumentation/mongodb/common.js +218 -0
- package/lib/instrumentation/mongodb/constants.js +87 -0
- package/lib/instrumentation/mongodb/v2-mongo.js +110 -0
- package/lib/instrumentation/mongodb/v3-mongo.js +80 -0
- package/lib/instrumentation/mongodb/v4-mongo.js +90 -0
- package/lib/instrumentation/mongodb.js +20 -312
- package/lib/instrumentation/mysql.js +33 -26
- package/lib/instrumentation/oracle.js +10 -14
- package/lib/instrumentation/pg.js +30 -21
- package/lib/instrumentation/promise.js +20 -17
- package/lib/instrumentation/redis.js +30 -30
- package/lib/instrumentation/when.js +9 -1
- package/lib/instrumentations.js +24 -24
- package/lib/logger.js +1 -1
- package/lib/metrics/index.js +37 -17
- package/lib/metrics/mapper.js +9 -9
- package/lib/metrics/metric-aggregator.js +5 -10
- package/lib/metrics/names.js +8 -7
- package/lib/metrics/normalizer/rule.js +6 -7
- package/lib/metrics/normalizer/tx_segment.js +17 -13
- package/lib/metrics/normalizer.js +30 -33
- package/lib/metrics/recorders/custom.js +3 -1
- package/lib/metrics/recorders/distributed-trace.js +3 -18
- package/lib/metrics/recorders/generic.js +3 -2
- package/lib/metrics/recorders/http.js +4 -6
- package/lib/metrics/recorders/http_external.js +1 -3
- package/lib/metrics/recorders/message-transaction.js +2 -12
- package/lib/metrics/recorders/other.js +2 -12
- package/lib/parse-proc-cpuinfo.js +11 -16
- package/lib/parse-proc-meminfo.js +5 -3
- package/lib/prioritized-attributes.js +12 -17
- package/lib/priority-queue.js +1 -1
- package/lib/proxy/grpc.js +1 -1
- package/lib/reservoir.js +9 -3
- package/lib/sampler.js +12 -13
- package/lib/serverless/api-gateway.js +3 -3
- package/lib/serverless/aws-lambda.js +28 -37
- package/lib/shim/conglomerate-shim.js +4 -4
- package/lib/shim/datastore-shim.js +29 -38
- package/lib/shim/index.js +8 -9
- package/lib/shim/message-shim.js +111 -113
- package/lib/shim/promise-shim.js +18 -13
- package/lib/shim/shim.js +72 -66
- package/lib/shim/specs/index.js +18 -19
- package/lib/shim/transaction-shim.js +26 -36
- package/lib/shim/webframework-shim.js +25 -35
- package/lib/shimmer.js +184 -166
- package/lib/spans/create-span-event-aggregator.js +24 -44
- package/lib/spans/span-context.js +4 -10
- package/lib/spans/span-event-aggregator.js +68 -13
- package/lib/spans/span-event.js +5 -7
- package/lib/spans/span-streamer.js +10 -18
- package/lib/spans/streaming-span-event-aggregator.js +1 -1
- package/lib/spans/streaming-span-event.js +4 -4
- package/lib/stats/apdex.js +5 -11
- package/lib/stats/index.js +12 -17
- package/lib/system-info.js +7 -3
- package/lib/timer.js +30 -16
- package/lib/transaction/dt-payload.js +1 -1
- package/lib/transaction/handle.js +19 -19
- package/lib/transaction/index.js +94 -112
- package/lib/transaction/name-state.js +5 -6
- package/lib/transaction/trace/aggregator.js +78 -73
- package/lib/transaction/trace/exclusive-time-calculator.js +5 -9
- package/lib/transaction/trace/index.js +26 -24
- package/lib/transaction/trace/segment.js +39 -56
- package/lib/transaction/tracecontext.js +37 -45
- package/lib/transaction/tracer/index.js +79 -44
- package/lib/transaction/transaction-event-aggregator.js +29 -31
- package/lib/uninstrumented.js +4 -5
- package/lib/util/attribute-types.js +1 -5
- package/lib/util/byte-limit.js +0 -1
- package/lib/util/cat.js +5 -13
- package/lib/util/codec.js +8 -4
- package/lib/util/deep-equal.js +48 -18
- package/lib/util/flatten.js +5 -2
- package/lib/util/hashes.js +2 -2
- package/lib/util/label-parser.js +7 -13
- package/lib/util/logger.js +31 -13
- package/lib/util/process-version.js +1 -1
- package/lib/util/properties.js +1 -1
- package/lib/util/sql/obfuscate.js +7 -17
- package/lib/util/stream-sink.js +2 -3
- package/lib/util/urltils.js +9 -13
- package/lib/utilization/aws-info.js +2 -3
- package/lib/utilization/azure-info.js +32 -30
- package/lib/utilization/common.js +5 -4
- package/lib/utilization/docker-info.js +4 -6
- package/lib/utilization/gcp-info.js +36 -35
- package/lib/utilization/index.js +1 -1
- package/lib/utilization/kubernetes-info.js +2 -2
- package/lib/utilization/pcf-info.js +4 -4
- package/package.json +32 -18
- package/stub_api.js +8 -6
|
@@ -29,51 +29,47 @@ module.exports = function initialize(agent, memcached, moduleName, shim) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
shim.setDatastore(shim.MEMCACHED)
|
|
32
|
-
shim.recordOperation(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
shim.logger.debug(err, 'Unable to stringify memcache key')
|
|
48
|
-
parameters.key = '<unknown>'
|
|
49
|
-
}
|
|
32
|
+
shim.recordOperation(proto, 'command', function commandWrapper(shim, original, name, args) {
|
|
33
|
+
// The `command` method takes two arguments: a query generator and a server
|
|
34
|
+
// address. The query generator returns a simple object describing the
|
|
35
|
+
// memcached call. The server parameter is only provided for multi-calls.
|
|
36
|
+
// When not provided, it can be derived from the key being interacted with.
|
|
37
|
+
var metacall = args[0]()
|
|
38
|
+
var server = args[1]
|
|
39
|
+
var keys = wrapKeys(metacall)
|
|
40
|
+
var parameters = Object.create(null)
|
|
41
|
+
try {
|
|
42
|
+
parameters.key = stringify(keys[0])
|
|
43
|
+
} catch (err) {
|
|
44
|
+
shim.logger.debug(err, 'Unable to stringify memcache key')
|
|
45
|
+
parameters.key = '<unknown>'
|
|
46
|
+
}
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
48
|
+
// Capture connection info for datastore instance metric.
|
|
49
|
+
var location = null
|
|
50
|
+
if (typeof server === 'string') {
|
|
51
|
+
location = server
|
|
52
|
+
} else if (this.HashRing && this.HashRing.get && metacall.key) {
|
|
53
|
+
location = this.HashRing.get(metacall.key)
|
|
54
|
+
}
|
|
55
|
+
if (location) {
|
|
56
|
+
location = location.split(':')
|
|
57
|
+
parameters.host = location[0]
|
|
58
|
+
parameters.port_path_or_id = location[1]
|
|
59
|
+
}
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
// rewrap the metacall for the command object
|
|
62
|
+
args[0] = function rewrapped() {
|
|
63
|
+
return metacall
|
|
64
|
+
}
|
|
68
65
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
66
|
+
// finally, execute the original command
|
|
67
|
+
return {
|
|
68
|
+
name: metacall.type || 'Unknown',
|
|
69
|
+
callback: function wrapCallback(shim, fn, fnName, opSegment) {
|
|
70
|
+
shim.bindCallbackSegment(metacall, 'callback', opSegment)
|
|
71
|
+
},
|
|
72
|
+
parameters: parameters
|
|
77
73
|
}
|
|
78
|
-
)
|
|
74
|
+
})
|
|
79
75
|
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict'
|
|
7
|
+
const { CURSOR_OPS, COLLECTION_OPS, DB_OPS } = require('./constants')
|
|
8
|
+
const common = module.exports
|
|
9
|
+
common.NR_ATTRS = Symbol('NR_ATTRS')
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Instruments all methods from constants.CURSOR_OPS on a given
|
|
13
|
+
* cursor class
|
|
14
|
+
*
|
|
15
|
+
* @param {Shim} shim
|
|
16
|
+
* @param {Cursor} Cursor
|
|
17
|
+
*/
|
|
18
|
+
common.instrumentCursor = function instrumentCursor(shim, Cursor) {
|
|
19
|
+
if (Cursor && Cursor.prototype) {
|
|
20
|
+
const proto = Cursor.prototype
|
|
21
|
+
for (let i = 0; i < CURSOR_OPS.length; i++) {
|
|
22
|
+
shim.recordQuery(proto, CURSOR_OPS[i], common.makeQueryDescFunc(shim, CURSOR_OPS[i]))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
shim.recordQuery(proto, 'each', common.makeQueryDescFunc(shim, 'each'))
|
|
26
|
+
shim.recordOperation(proto, 'pipe', { opaque: true })
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Instruments all methods from constants.COLLECTION_OPS on
|
|
32
|
+
* the Collection class
|
|
33
|
+
*
|
|
34
|
+
* @param {Shim} shim
|
|
35
|
+
* @param {Collection} Collection
|
|
36
|
+
*/
|
|
37
|
+
common.instrumentCollection = function instrumentCollection(shim, Collection) {
|
|
38
|
+
if (Collection && Collection.prototype) {
|
|
39
|
+
const proto = Collection.prototype
|
|
40
|
+
for (let i = 0; i < COLLECTION_OPS.length; i++) {
|
|
41
|
+
shim.recordQuery(proto, COLLECTION_OPS[i], common.makeQueryDescFunc(shim, COLLECTION_OPS[i]))
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Instruments all methods from constants.DB_OPS on
|
|
48
|
+
* the Db class.
|
|
49
|
+
*
|
|
50
|
+
* @param {Object} params
|
|
51
|
+
* @param {Shim} params.shim
|
|
52
|
+
* @param {Db} params.Db
|
|
53
|
+
*/
|
|
54
|
+
common.instrumentDb = function instrumentDb(shim, Db) {
|
|
55
|
+
if (Db && Db.prototype) {
|
|
56
|
+
const proto = Db.prototype
|
|
57
|
+
shim.recordOperation(proto, DB_OPS, { callback: shim.LAST, opaque: true })
|
|
58
|
+
// link to client.connect(removed in v4.0)
|
|
59
|
+
shim.recordOperation(Db, 'connect', { callback: shim.LAST })
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Sets up the desc for all instrumented query methods
|
|
65
|
+
* @param {Shim} shim
|
|
66
|
+
* @param {string} methodName
|
|
67
|
+
*/
|
|
68
|
+
common.makeQueryDescFunc = function makeQueryDescFunc(shim, methodName) {
|
|
69
|
+
if (methodName === 'each') {
|
|
70
|
+
return function eachDescFunc() {
|
|
71
|
+
const parameters = getInstanceAttributeParameters(shim, this)
|
|
72
|
+
return { query: methodName, parameters, rowCallback: shim.LAST, opaque: true }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return function queryDescFunc() {
|
|
77
|
+
// segment name does not actually use query string
|
|
78
|
+
// method name is set as query so the query parser has access to the op name
|
|
79
|
+
const parameters = getInstanceAttributeParameters(shim, this)
|
|
80
|
+
return { query: methodName, parameters, promise: true, callback: shim.LAST, opaque: true }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Sets up a listener for `started` on instrumenter(mongo APM). This applies to
|
|
86
|
+
* mongo <4. The listener adds the following attributes to the active segment:
|
|
87
|
+
* host, port_path_or_id, and database_name
|
|
88
|
+
*
|
|
89
|
+
* @param {Shim} shim
|
|
90
|
+
* @param {Instrumentation} instrumenter instance of mongo APM class
|
|
91
|
+
*/
|
|
92
|
+
common.captureAttributesOnStarted = function captureAttributesOnStarted(shim, instrumenter) {
|
|
93
|
+
instrumenter.on('started', function onMongoEventStarted(evnt) {
|
|
94
|
+
// This assumes that this `started` event is fired _after_ our wrapper
|
|
95
|
+
// starts and creates the segment. We perform a check of the segment name
|
|
96
|
+
// out of an excess of caution.
|
|
97
|
+
const connId = evnt.connectionId
|
|
98
|
+
if (connId) {
|
|
99
|
+
// used in v3 when connection is a cluster pool
|
|
100
|
+
if (typeof connId === 'number') {
|
|
101
|
+
setHostPort(shim, evnt.address, evnt.databaseName, this.$MongoClient)
|
|
102
|
+
// used in v3 when connection is to 1 host
|
|
103
|
+
} else if (typeof connId === 'string') {
|
|
104
|
+
setHostPort(shim, connId, evnt.databaseName)
|
|
105
|
+
// v2 contains `domainSocket`, get socket connection from `host`
|
|
106
|
+
} else if (connId.domainSocket) {
|
|
107
|
+
shim.captureInstanceAttributes('localhost', connId.host, evnt.databaseName)
|
|
108
|
+
// v2 remote connection get `host` `port` from respective properties
|
|
109
|
+
} else {
|
|
110
|
+
shim.captureInstanceAttributes(connId.host, connId.port, evnt.databaseName)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Extracts the host and port from a connection string
|
|
118
|
+
* This also handles if connection string is a domain socket
|
|
119
|
+
* Mongo sticks the path to the domain socket in the "host" slot, but we
|
|
120
|
+
* want it in the "port", so if we have a domain socket we need to change
|
|
121
|
+
* the order of our parameters.
|
|
122
|
+
*
|
|
123
|
+
* @param {Shim} shim
|
|
124
|
+
* @param {string} connStr
|
|
125
|
+
* @param {string} db database name
|
|
126
|
+
* @param {Object} client mongo client instance
|
|
127
|
+
*/
|
|
128
|
+
function setHostPort(shim, connStr, db, client) {
|
|
129
|
+
const parts = connStr.split(':')
|
|
130
|
+
// in v3 when running with a cluster of socket connections
|
|
131
|
+
// the address is `undefined:undefined`. we will instead attempt
|
|
132
|
+
// to get connection details from the client symbol NR_ATTRS
|
|
133
|
+
// added in `lib/instrumentation/mongodb/v3-mongo` when a client connects
|
|
134
|
+
// with a URL string
|
|
135
|
+
if (parts.includes('undefined')) {
|
|
136
|
+
try {
|
|
137
|
+
const attrs = client[common.NR_ATTRS]
|
|
138
|
+
const socket = decodeURIComponent(attrs.split(',')[0].split('mongodb://')[1])
|
|
139
|
+
shim.captureInstanceAttributes('localhost', socket, db)
|
|
140
|
+
} catch (err) {
|
|
141
|
+
shim.logger.debug(err, 'Could not extract host/port from mongo command')
|
|
142
|
+
}
|
|
143
|
+
// connected using domain socket but the "host"(e.g: /path/to/mongo-socket-port.sock)
|
|
144
|
+
} else if (parts.length && parts[0][0] === '/') {
|
|
145
|
+
shim.captureInstanceAttributes('localhost', parts[0], db)
|
|
146
|
+
} else {
|
|
147
|
+
shim.captureInstanceAttributes(parts[0], parts[1], db)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get the database_name, host, port_path_or_id
|
|
153
|
+
* for the query segment. v4 refactored where the toplogy is stored.
|
|
154
|
+
* You can now get the details via the client obj that's deeply nested
|
|
155
|
+
* See: https://github.com/mongodb/node-mongodb-native/pull/2594/files#diff-1d214e57ddda9095d296e5700ebce701333bfefcf417e234c584d14091b2f50dR168
|
|
156
|
+
*/
|
|
157
|
+
function getInstanceAttributeParameters(shim, obj) {
|
|
158
|
+
if (obj.s && obj.s.db && obj.s.topology) {
|
|
159
|
+
shim.logger.trace('Adding datastore instance attributes from obj.s.db + obj.s.topology')
|
|
160
|
+
const databaseName = obj.s.db.databaseName || null
|
|
161
|
+
const topology = obj.s.topology
|
|
162
|
+
if (topology.s && topology.s.options) {
|
|
163
|
+
return doCapture(topology.s.options, databaseName)
|
|
164
|
+
}
|
|
165
|
+
} else if (
|
|
166
|
+
obj.s &&
|
|
167
|
+
obj.s.db &&
|
|
168
|
+
obj.s.db.s &&
|
|
169
|
+
obj.s.db.s.client &&
|
|
170
|
+
obj.s.db.s.client.s &&
|
|
171
|
+
obj.s.db.s.client.s.options &&
|
|
172
|
+
obj.s.db.s.client.s.options.hosts &&
|
|
173
|
+
obj.s.db.s.client.s.options.hosts.length
|
|
174
|
+
) {
|
|
175
|
+
// hosts is an array but we will always pull the first for consistency
|
|
176
|
+
let [{ host, port, socketPath }] = obj.s.db.s.client.s.options.hosts
|
|
177
|
+
|
|
178
|
+
if (socketPath) {
|
|
179
|
+
port = socketPath
|
|
180
|
+
host = 'localhost'
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
host,
|
|
184
|
+
port_path_or_id: port,
|
|
185
|
+
database_name: obj.s.db.databaseName
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
shim.logger.trace('Could not find datastore instance attributes.')
|
|
190
|
+
return {
|
|
191
|
+
host: null,
|
|
192
|
+
port_path_or_id: null,
|
|
193
|
+
database_name: null
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function doCapture(conf, database) {
|
|
197
|
+
let host = null
|
|
198
|
+
let port = null
|
|
199
|
+
|
|
200
|
+
// servers is an array but we will always pull the first for consistency
|
|
201
|
+
if (conf.servers && conf.servers.length) {
|
|
202
|
+
;[{ host, port }] = conf.servers
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// host is a domain socket. set host as localhost and use the domain
|
|
206
|
+
// socket host as the port
|
|
207
|
+
if (host && host.endsWith('.sock')) {
|
|
208
|
+
port = host
|
|
209
|
+
host = 'localhost'
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
host: host,
|
|
214
|
+
port_path_or_id: port,
|
|
215
|
+
database_name: database
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
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 = ['put', 'get', 'delete']
|
|
79
|
+
|
|
80
|
+
const CURSOR_OPS = ['nextObject', 'next', 'toArray', 'count', 'explain']
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
COLLECTION_OPS,
|
|
84
|
+
CURSOR_OPS,
|
|
85
|
+
DB_OPS,
|
|
86
|
+
GRID_OPS
|
|
87
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
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 { captureAttributesOnStarted, makeQueryDescFunc } = require('./common')
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* parser used to grab the collection and operation
|
|
12
|
+
* from a running query
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} operation
|
|
15
|
+
*/
|
|
16
|
+
function queryParser(operation) {
|
|
17
|
+
let collection = this.collectionName || 'unknown'
|
|
18
|
+
if (this.ns) {
|
|
19
|
+
collection = this.ns.split(/\./)[1] || collection
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { operation, collection }
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Registers relevant instrumentation for mongo <= 3.0.6
|
|
26
|
+
* and >= 2. This relies on the built-in "APM" hook points
|
|
27
|
+
* to instrument their provided objects as well as sets
|
|
28
|
+
* up a listener for when commands start to properly
|
|
29
|
+
* add necessary attributes to segments
|
|
30
|
+
*
|
|
31
|
+
* @param {Shim} shim
|
|
32
|
+
* @param {Object} mongodb resolved package
|
|
33
|
+
*/
|
|
34
|
+
module.exports = function instrument(shim, mongodb) {
|
|
35
|
+
shim.setParser(queryParser)
|
|
36
|
+
|
|
37
|
+
const recordDesc = {
|
|
38
|
+
Gridstore: {
|
|
39
|
+
isQuery: false,
|
|
40
|
+
makeDescFunc: function makeGridDesc(opName) {
|
|
41
|
+
return { name: 'GridFS-' + opName, callback: shim.LAST }
|
|
42
|
+
}
|
|
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: {
|
|
51
|
+
isQuery: false,
|
|
52
|
+
makeDescFunc: function makeDbDesc() {
|
|
53
|
+
return { callback: shim.LAST }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// instrument using the apm api
|
|
59
|
+
const instrumenter = mongodb.instrument(Object.create(null), instrumentModules)
|
|
60
|
+
captureAttributesOnStarted(shim, instrumenter)
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Every module groups instrumentations by their
|
|
64
|
+
* promise, callback, return permutations
|
|
65
|
+
* Iterate over permutations and properly
|
|
66
|
+
* wrap depending on the `recordDesc` above
|
|
67
|
+
* See: https://github.com/mongodb/node-mongodb-native/blob/v3.0.5/lib/collection.js#L384
|
|
68
|
+
*/
|
|
69
|
+
function instrumentModules(_, modules) {
|
|
70
|
+
modules.forEach((module) => {
|
|
71
|
+
const { obj, instrumentations, name } = module
|
|
72
|
+
instrumentations.forEach((meta) => {
|
|
73
|
+
applyInstrumentation(name, obj, meta)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Iterate over methods on object and lookup in `recordDesc` to decide
|
|
80
|
+
* if it needs to be wrapped as an operation or query
|
|
81
|
+
*
|
|
82
|
+
* @param {string} objectName name of class getting instrumented
|
|
83
|
+
* @param {Object} object reference to the class getting instrumented
|
|
84
|
+
* @param {Define} meta describes the methods and if they are callbacks
|
|
85
|
+
* promises, and return values
|
|
86
|
+
*/
|
|
87
|
+
function applyInstrumentation(objectName, object, meta) {
|
|
88
|
+
const { methods, options } = meta
|
|
89
|
+
if (options.callback) {
|
|
90
|
+
methods.forEach((method) => {
|
|
91
|
+
const { isQuery, makeDescFunc } = recordDesc[objectName]
|
|
92
|
+
const proto = object.prototype
|
|
93
|
+
if (isQuery) {
|
|
94
|
+
shim.recordQuery(proto, method, makeDescFunc(shim, method))
|
|
95
|
+
} else if (isQuery === false) {
|
|
96
|
+
// could be unset
|
|
97
|
+
shim.recordOperation(proto, method, makeDescFunc(shim, method))
|
|
98
|
+
} else {
|
|
99
|
+
shim.logger.trace('No wrapping method found for %s', objectName)
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// the cursor object implements Readable stream and internally calls nextObject on
|
|
105
|
+
// each read, in which case we do not want to record each nextObject() call
|
|
106
|
+
if (/Cursor$/.test(objectName)) {
|
|
107
|
+
shim.recordOperation(object.prototype, 'pipe')
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
NR_ATTRS
|
|
14
|
+
} = require('./common')
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* parser used to grab the collection and operation
|
|
18
|
+
* on every mongo operation
|
|
19
|
+
*
|
|
20
|
+
* @param {Object} operation
|
|
21
|
+
*/
|
|
22
|
+
function queryParser(operation) {
|
|
23
|
+
let collection = this.collectionName || 'unknown'
|
|
24
|
+
// in v3.3.0 aggregate commands added the collection
|
|
25
|
+
// to target
|
|
26
|
+
if (this.operation && this.operation.target) {
|
|
27
|
+
collection = this.operation.target
|
|
28
|
+
} else if (this.ns) {
|
|
29
|
+
collection = this.ns.split(/\./)[1] || collection
|
|
30
|
+
}
|
|
31
|
+
return { operation, collection }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Records the `mongo.MongoClient.connect` operations. It also adds the first arg of connect(url)
|
|
36
|
+
* to a Symbol on the MongoClient to be used later to extract the host/port in cases where the topology
|
|
37
|
+
* is a cluster of domain sockets
|
|
38
|
+
*
|
|
39
|
+
* @param {Shim} shim
|
|
40
|
+
* @param {Object} mongodb resolved package
|
|
41
|
+
*/
|
|
42
|
+
function instrumentClient(shim, mongodb) {
|
|
43
|
+
shim.recordOperation(mongodb.MongoClient, 'connect', function wrappedConnect(shim, _, __, args) {
|
|
44
|
+
// Add the connection url to the MongoClient to retrieve later in the `lib/instrumentation/mongo/common`
|
|
45
|
+
// captureAttributesOnStarted listener
|
|
46
|
+
this[NR_ATTRS] = args[0]
|
|
47
|
+
return { callback: shim.LAST }
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Registers relevant instrumentation for mongo >= 3.0.6
|
|
53
|
+
* In 3.0.6 they refactored their "APM" module which removed
|
|
54
|
+
* a lot of niceities around instrumentation classes.
|
|
55
|
+
* see: https://github.com/mongodb/node-mongodb-native/pull/1675/files
|
|
56
|
+
* This reverts back to instrumenting pre-canned methods on classes
|
|
57
|
+
* as well as sets up a listener for when commands start to properly
|
|
58
|
+
* add necessary attributes to segments
|
|
59
|
+
*
|
|
60
|
+
* @param {Shim} shim
|
|
61
|
+
* @param {Object} mongodb resolved package
|
|
62
|
+
*/
|
|
63
|
+
module.exports = function instrument(shim, mongodb) {
|
|
64
|
+
shim.setParser(queryParser)
|
|
65
|
+
instrumentClient(shim, mongodb)
|
|
66
|
+
const instrumenter = mongodb.instrument(Object.create(null), () => {})
|
|
67
|
+
captureAttributesOnStarted(shim, instrumenter)
|
|
68
|
+
instrumentCursor(shim, mongodb.Cursor)
|
|
69
|
+
instrumentCursor(shim, shim.require('./lib/aggregation_cursor'))
|
|
70
|
+
instrumentCursor(shim, shim.require('./lib/command_cursor'))
|
|
71
|
+
instrumentCollection(shim, mongodb.Collection)
|
|
72
|
+
instrumentDb(shim, mongodb.Db)
|
|
73
|
+
|
|
74
|
+
// calling instrument sets up listeners for a few events
|
|
75
|
+
// we should restore this on unload to avoid leaking
|
|
76
|
+
// event emitters
|
|
77
|
+
shim.agent.once('unload', function uninstrumentMongo() {
|
|
78
|
+
instrumenter.uninstrument()
|
|
79
|
+
})
|
|
80
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
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 { instrumentCollection, instrumentCursor, instrumentDb } = require('./common')
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* parser used to grab the collection and operation
|
|
12
|
+
* from a running query
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} operation
|
|
15
|
+
*/
|
|
16
|
+
function queryParser(operation) {
|
|
17
|
+
let collection = this.collectionName || 'unknown'
|
|
18
|
+
|
|
19
|
+
// cursor methods have collection on namespace.collection
|
|
20
|
+
if (this.namespace && this.namespace.collection) {
|
|
21
|
+
collection = this.namespace.collection
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { operation, collection }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* `commandStarted` handler used to
|
|
29
|
+
* update host, port and database_name
|
|
30
|
+
* on segment attributes
|
|
31
|
+
*
|
|
32
|
+
* @param {Shim} shim
|
|
33
|
+
* @param {CommandStartedEvent} evnt
|
|
34
|
+
*/
|
|
35
|
+
function cmdStartedHandler(shim, evnt) {
|
|
36
|
+
if (evnt.connectionId) {
|
|
37
|
+
let [host, port] = evnt.address.split(':')
|
|
38
|
+
|
|
39
|
+
// connection via socket get port from 1st host
|
|
40
|
+
// socketPath property
|
|
41
|
+
// which looks like mongodb:///tmp/mongodb-27017.sock"
|
|
42
|
+
if (['undefined'].includes(host, port)) {
|
|
43
|
+
host = 'localhost'
|
|
44
|
+
const hosts = this.s.options.hosts
|
|
45
|
+
if (hosts && hosts.length && hosts[0].socketPath) {
|
|
46
|
+
port = hosts[0].socketPath
|
|
47
|
+
}
|
|
48
|
+
} else if (host === '127.0.0.1') {
|
|
49
|
+
host = 'localhost'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
shim.captureInstanceAttributes(host, port, evnt.databaseName)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* function executed when client.connect is called
|
|
58
|
+
* enable APM(monitorCommands) and add the
|
|
59
|
+
* `commandStarted` listener
|
|
60
|
+
*
|
|
61
|
+
* @param {Shim} shim
|
|
62
|
+
*/
|
|
63
|
+
function wrapConnect(shim) {
|
|
64
|
+
this.monitorCommands = true
|
|
65
|
+
this.on('commandStarted', cmdStartedHandler.bind(this, shim))
|
|
66
|
+
return { callback: shim.LAST }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Wraps connect to record as operation but also to add a listener
|
|
71
|
+
* for `commandStarted`. This will be emitted before every command starts
|
|
72
|
+
* so we can properly update the segment attributes with a more accurate
|
|
73
|
+
* host/port/database name
|
|
74
|
+
*
|
|
75
|
+
* @param {Shim} shim
|
|
76
|
+
* @param {MongoClient} MongoClient reference
|
|
77
|
+
*/
|
|
78
|
+
function instrumentMongoClient(shim, MongoClient) {
|
|
79
|
+
shim.recordOperation(MongoClient.prototype, 'connect', wrapConnect)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = function instrument(shim, mongodb) {
|
|
83
|
+
shim.setParser(queryParser)
|
|
84
|
+
instrumentMongoClient(shim, mongodb.MongoClient)
|
|
85
|
+
instrumentCursor(shim, mongodb.AbstractCursor)
|
|
86
|
+
instrumentCursor(shim, mongodb.FindCursor)
|
|
87
|
+
instrumentCursor(shim, mongodb.AggregationCursor)
|
|
88
|
+
instrumentCollection(shim, mongodb.Collection)
|
|
89
|
+
instrumentDb(shim, mongodb.Db)
|
|
90
|
+
}
|