newrelic 9.7.0 → 9.7.2

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.
Files changed (37) hide show
  1. package/NEWS.md +42 -18
  2. package/THIRD_PARTY_NOTICES.md +203 -2
  3. package/api.js +155 -103
  4. package/lib/agent.js +29 -82
  5. package/lib/collector/api.js +234 -201
  6. package/lib/config/attribute-filter.js +35 -25
  7. package/lib/config/default.js +45 -19
  8. package/lib/config/index.js +260 -199
  9. package/lib/environment.js +16 -12
  10. package/lib/errors/error-collector.js +124 -55
  11. package/lib/errors/index.js +59 -49
  12. package/lib/instrumentation/@hapi/hapi.js +56 -50
  13. package/lib/instrumentation/@node-redis/client.js +50 -41
  14. package/lib/instrumentation/amqplib.js +116 -151
  15. package/lib/instrumentation/core/{async_hooks.js → async-hooks.js} +26 -11
  16. package/lib/instrumentation/core/globals.js +1 -1
  17. package/lib/instrumentation/core/http-outbound.js +193 -78
  18. package/lib/instrumentation/core/timers.js +106 -59
  19. package/lib/instrumentation/grpc-js/grpc.js +20 -23
  20. package/lib/instrumentation/mongodb/common.js +87 -85
  21. package/lib/instrumentation/redis.js +112 -90
  22. package/lib/instrumentation/undici.js +204 -192
  23. package/lib/instrumentation/{when.js → when/constants.js} +13 -10
  24. package/lib/instrumentation/when/contextualizer.js +168 -0
  25. package/lib/instrumentation/when/index.js +354 -0
  26. package/lib/instrumentation/when/nr-hooks.js +15 -0
  27. package/lib/instrumentations.js +1 -1
  28. package/lib/shim/shim.js +2 -0
  29. package/lib/shim/webframework-shim.js +19 -0
  30. package/lib/symbols.js +1 -0
  31. package/lib/system-info.js +240 -163
  32. package/lib/util/async-each-limit.js +30 -0
  33. package/lib/util/attributes.js +159 -0
  34. package/lib/util/code-level-metrics.js +58 -0
  35. package/lib/util/deep-equal.js +11 -144
  36. package/package.json +5 -4
  37. package/lib/instrumentation/promise.js +0 -572
@@ -5,8 +5,6 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- /* eslint sonarjs/cognitive-complexity: ["error", 40] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
9
-
10
8
  const recordExternal = require('../../metrics/recorders/http_external')
11
9
  const cat = require('../../util/cat')
12
10
  const urltils = require('../../util/urltils')
@@ -15,6 +13,7 @@ const shimmer = require('../../shimmer')
15
13
  const url = require('url')
16
14
  const copy = require('../../util/copy')
17
15
  const symbols = require('../../symbols')
16
+ const http = require('http')
18
17
 
19
18
  const NAMES = require('../../metrics/names')
20
19
 
@@ -25,27 +24,73 @@ const DEFAULT_SSL_PORT = 443
25
24
  const NEWRELIC_SYNTHETICS_HEADER = 'x-newrelic-synthetics'
26
25
 
27
26
  /**
28
- * Instruments an outbound HTTP request.
27
+ * Determines the default port to 80 if protocol is undefined or http:
28
+ * Otherwise it assigns it as 443
29
29
  *
30
- * @param {object} agent instantiation of lib/agent.js
31
30
  * @param {object} opts HTTP request options
32
- * @param {Function} makeRequest function for issuing actual HTTP request
33
- * @returns {object} The instrumented outbound HTTP request.
31
+ * @returns {number} default port
34
32
  */
35
- module.exports = function instrumentOutbound(agent, opts, makeRequest) {
33
+ function getDefaultPort(opts) {
34
+ return !opts.protocol || opts.protocol === 'http:' ? DEFAULT_HTTP_PORT : DEFAULT_SSL_PORT
35
+ }
36
+
37
+ /**
38
+ * Determines the port based on http opts
39
+ *
40
+ * @param {object} opts HTTP request options
41
+ * @param {number} defaultPort the default port
42
+ * @returns {number} port
43
+ */
44
+ function getPort(opts, defaultPort) {
45
+ let port = opts.port || opts.defaultPort
46
+ if (!port) {
47
+ port = defaultPort
48
+ }
49
+
50
+ return port
51
+ }
52
+
53
+ /**
54
+ * Determines the default hostname based on http opts
55
+ *
56
+ * @param {object} opts HTTP request options
57
+ * @returns {string} default host
58
+ */
59
+ function getDefaultHostName(opts) {
60
+ return opts.hostname || opts.host || DEFAULT_HOST
61
+ }
62
+
63
+ /**
64
+ * Parses http opts to an object
65
+ * If string will call url.parse, otherwise it will
66
+ * do a shallow copy
67
+ *
68
+ * @param {string|object} opts a url string or HTTP request options
69
+ * @returns {object} parsed http opts
70
+ */
71
+ function parseOpts(opts) {
36
72
  if (typeof opts === 'string') {
37
73
  opts = url.parse(opts)
38
74
  } else {
39
75
  opts = copy.shallow(opts)
40
76
  }
41
77
 
42
- const defaultPort =
43
- !opts.protocol || opts.protocol === 'http:' ? DEFAULT_HTTP_PORT : DEFAULT_SSL_PORT
44
- let hostname = opts.hostname || opts.host || DEFAULT_HOST
45
- let port = opts.port || opts.defaultPort
46
- if (!port) {
47
- port = defaultPort
48
- }
78
+ return opts
79
+ }
80
+
81
+ /**
82
+ * Instruments an outbound HTTP request.
83
+ *
84
+ * @param {Agent} agent instantiation of lib/agent.js
85
+ * @param {object} opts HTTP request options
86
+ * @param {Function} makeRequest function for issuing actual HTTP request
87
+ * @returns {object} The instrumented outbound HTTP request.
88
+ */
89
+ module.exports = function instrumentOutbound(agent, opts, makeRequest) {
90
+ opts = parseOpts(opts)
91
+ const defaultPort = getDefaultPort(opts)
92
+ let hostname = getDefaultHostName(opts)
93
+ const port = getPort(opts, defaultPort)
49
94
 
50
95
  if (!hostname || port < 1) {
51
96
  logger.warn('Invalid host name (%s) or port (%s) for outbound request.', hostname, port)
@@ -74,79 +119,144 @@ module.exports = function instrumentOutbound(agent, opts, makeRequest) {
74
119
  recordExternal(hostname, 'http'),
75
120
  parent,
76
121
  false,
77
- instrumentRequest
122
+ instrumentRequest.bind(null, agent, opts, makeRequest, hostname)
78
123
  )
124
+ }
125
+
126
+ /**
127
+ * Injects DT/CAT headers, creates segment for outbound http request.
128
+ * Instruments the request.emit to properly handle the response of the
129
+ * outbound http request
130
+ *
131
+ * @param {Agent} agent New Relic agent
132
+ * @param {string|object} opts a url string or HTTP request options
133
+ * @param {Function} makeRequest function to make request
134
+ * @param {string} hostname host of outbound request
135
+ * @param {TraceSegment} segment outbound http segment
136
+ * @returns {http.IncomingMessage} request actual http outbound request
137
+ */
138
+ function instrumentRequest(agent, opts, makeRequest, hostname, segment) {
139
+ const transaction = segment.transaction
140
+ const outboundHeaders = Object.create(null)
79
141
 
80
- function instrumentRequest(segment) {
81
- const transaction = segment.transaction
82
- const outboundHeaders = Object.create(null)
142
+ if (agent.config.encoding_key && transaction.syntheticsHeader) {
143
+ outboundHeaders[NEWRELIC_SYNTHETICS_HEADER] = transaction.syntheticsHeader
144
+ }
83
145
 
84
- if (agent.config.encoding_key && transaction.syntheticsHeader) {
85
- outboundHeaders[NEWRELIC_SYNTHETICS_HEADER] = transaction.syntheticsHeader
86
- }
146
+ maybeAddDtCatHeaders(agent, transaction, outboundHeaders, !!opts?.headers?.[symbols.disableDT])
147
+ opts.headers = assignOutgoingHeaders(opts.headers, outboundHeaders)
87
148
 
88
- // TODO: abstract header logic shared with TransactionShim#insertCATRequestHeaders
89
- if (agent.config.distributed_tracing.enabled) {
90
- if (opts.headers && opts.headers[symbols.disableDT]) {
91
- logger.trace('Distributed tracing disabled by instrumentation.')
92
- } else {
93
- transaction.insertDistributedTraceHeaders(outboundHeaders)
94
- }
95
- } else if (agent.config.cross_application_tracer.enabled) {
96
- cat.addCatHeaders(agent.config, transaction, outboundHeaders)
149
+ const request = applySegment(opts, makeRequest, hostname, segment)
150
+
151
+ instrumentRequestEmit(agent, hostname, segment, request)
152
+
153
+ return request
154
+ }
155
+
156
+ /**
157
+ * Depending on configuration it will either add DT or CAT headers to the
158
+ * outgoing headers
159
+ *
160
+ * @param {Agent} agent Node.js agent
161
+ * @param {Transaction} transaction active transaction
162
+ * @param {object} outboundHeaders headers that are getting attached to external http call
163
+ * @param {boolean} hasDtSym flag to tell if the disableDT header exists on headers
164
+ */
165
+ // TODO: abstract header logic shared with TransactionShim#insertCATRequestHeaders
166
+ function maybeAddDtCatHeaders(agent, transaction, outboundHeaders, hasDtSym = false) {
167
+ if (agent.config.distributed_tracing.enabled) {
168
+ if (hasDtSym) {
169
+ logger.trace('Distributed tracing disabled by instrumentation.')
97
170
  } else {
98
- logger.trace('Both DT and CAT are disabled, not adding headers!')
171
+ transaction.insertDistributedTraceHeaders(outboundHeaders)
99
172
  }
173
+ } else if (agent.config.cross_application_tracer.enabled) {
174
+ cat.addCatHeaders(agent.config, transaction, outboundHeaders)
175
+ } else {
176
+ logger.trace('Both DT and CAT are disabled, not adding headers!')
177
+ }
178
+ }
100
179
 
101
- if (Array.isArray(opts.headers)) {
102
- opts.headers = opts.headers.slice()
103
- Array.prototype.push.apply(
104
- opts.headers,
105
- Object.keys(outboundHeaders).map(function getHeaderTuples(key) {
106
- return [key, outboundHeaders[key]]
107
- })
108
- )
109
- } else {
110
- opts.headers = Object.assign(Object.create(null), opts.headers, outboundHeaders)
180
+ /**
181
+ * Assigns new headers for outgoing request
182
+ *
183
+ * @param {object|Array} currentHeaders current headers from request options headers
184
+ * @param {object} outboundHeaders headers to assign to outgoing request
185
+ * @returns {object|Array} properly formatted headers
186
+ */
187
+ function assignOutgoingHeaders(currentHeaders, outboundHeaders) {
188
+ let headers
189
+
190
+ if (Array.isArray(currentHeaders)) {
191
+ headers = currentHeaders.slice()
192
+ Array.prototype.push.apply(
193
+ headers,
194
+ Object.keys(outboundHeaders).map(function getHeaderTuples(key) {
195
+ return [key, outboundHeaders[key]]
196
+ })
197
+ )
198
+ } else {
199
+ headers = Object.assign(Object.create(null), currentHeaders, outboundHeaders)
200
+ }
201
+
202
+ return headers
203
+ }
204
+
205
+ /**
206
+ * Starts the http outbound segment and attaches relevant attributes to the segment/span.
207
+ *
208
+ * @param {string|object} opts a url string or HTTP request options
209
+ * @param {Function} makeRequest function to make request
210
+ * @param {string} hostname host of outbound request
211
+ * @param {TraceSegment} segment outbound http segment
212
+ * @returns {http.IncomingMessage} request actual http outbound request
213
+ */
214
+ function applySegment(opts, makeRequest, hostname, segment) {
215
+ segment.start()
216
+ const request = makeRequest(opts)
217
+ const parsed = urltils.scrubAndParseParameters(request.path)
218
+ const proto = parsed.protocol || opts.protocol || 'http:'
219
+ segment.name += parsed.path
220
+ request[symbols.segment] = segment
221
+
222
+ if (parsed.parameters) {
223
+ // Scrub and parse returns on object with a null prototype.
224
+ // eslint-disable-next-line guard-for-in
225
+ for (const key in parsed.parameters) {
226
+ segment.addSpanAttribute(`request.parameters.${key}`, parsed.parameters[key])
111
227
  }
228
+ }
229
+ segment.addAttribute('url', `${proto}//${hostname}${parsed.path}`)
230
+ segment.addAttribute('procedure', opts.method || 'GET')
231
+ return request
232
+ }
112
233
 
113
- segment.start()
114
- const request = makeRequest(opts)
115
- const parsed = urltils.scrubAndParseParameters(request.path)
116
- const proto = parsed.protocol || opts.protocol || 'http:'
117
- segment.name += parsed.path
118
- request[symbols.segment] = segment
119
-
120
- if (parsed.parameters) {
121
- // Scrub and parse returns on object with a null prototype.
122
- // eslint-disable-next-line guard-for-in
123
- for (const key in parsed.parameters) {
124
- segment.addSpanAttribute(`request.parameters.${key}`, parsed.parameters[key])
234
+ /**
235
+ * Wrap the emit method. We're doing a special wrapper instead of using
236
+ * `tracer.bindEmitter` because we want to do some logic based on certain
237
+ * events.
238
+ *
239
+ * @param {Agent} agent New Relic agent
240
+ * @param {string} hostname host of outbound request
241
+ * @param {TraceSegment} segment outbound http segment
242
+ * @param {http.IncomingMessage} request actual http outbound request
243
+ */
244
+ function instrumentRequestEmit(agent, hostname, segment, request) {
245
+ shimmer.wrapMethod(request, 'request.emit', 'emit', function wrapEmit(emit) {
246
+ const boundEmit = agent.tracer.bindFunction(emit, segment)
247
+ return function wrappedRequestEmit(evnt, arg) {
248
+ if (evnt === 'error') {
249
+ segment.end()
250
+ handleError(segment, request, arg)
251
+ } else if (evnt === 'response') {
252
+ handleResponse(segment, hostname, arg)
125
253
  }
254
+
255
+ return boundEmit.apply(this, arguments)
126
256
  }
127
- segment.addAttribute('url', `${proto}//${hostname}${parsed.path}`)
128
- segment.addAttribute('procedure', opts.method || 'GET')
129
-
130
- // Wrap the emit method. We're doing a special wrapper instead of using
131
- // `tracer.bindEmitter` because we want to do some logic based on certain
132
- // events.
133
- shimmer.wrapMethod(request, 'request.emit', 'emit', function wrapEmit(emit) {
134
- const boundEmit = agent.tracer.bindFunction(emit, segment)
135
- return function wrappedRequestEmit(evnt, arg) {
136
- if (evnt === 'error') {
137
- segment.end()
138
- handleError(segment, request, arg)
139
- } else if (evnt === 'response') {
140
- handleResponse(segment, hostname, request, arg)
141
- }
142
-
143
- return boundEmit.apply(this, arguments)
144
- }
145
- })
146
- _makeNonEnumerable(request, 'emit')
257
+ })
147
258
 
148
- return request
149
- }
259
+ _makeNonEnumerable(request, 'emit')
150
260
  }
151
261
 
152
262
  /**
@@ -175,10 +285,9 @@ function handleError(segment, req, error) {
175
285
  *
176
286
  * @param {object} segment TraceSegment instance
177
287
  * @param {string} hostname host of the HTTP request
178
- * @param {object} req http.ClientRequest
179
- * @param {object} res http.IncomingMessage
288
+ * @param {object} res http.ServerResponse
180
289
  */
181
- function handleResponse(segment, hostname, req, res) {
290
+ function handleResponse(segment, hostname, res) {
182
291
  // Add response attributes for spans
183
292
  segment.addSpanAttribute('http.statusCode', res.statusCode)
184
293
  segment.addSpanAttribute('http.statusText', res.statusMessage)
@@ -204,6 +313,12 @@ function handleResponse(segment, hostname, req, res) {
204
313
  _makeNonEnumerable(res, 'emit')
205
314
  }
206
315
 
316
+ /**
317
+ * Makes a property non-enumerable
318
+ *
319
+ * @param {object} obj object that contains property that needs to be non-enumerable
320
+ * @param {string} prop property to make non-enumerable
321
+ */
207
322
  function _makeNonEnumerable(obj, prop) {
208
323
  try {
209
324
  const desc = Object.getOwnPropertyDescriptor(obj, prop)
@@ -5,73 +5,114 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- /* eslint sonarjs/cognitive-complexity: ["error", 16] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
9
-
10
8
  const symbols = require('../../symbols')
9
+ const Timers = require('timers')
11
10
 
12
11
  module.exports = initialize
13
12
 
14
- function initialize(agent, timers, moduleName, shim) {
15
- if (!agent.config.feature_flag.async_local_context) {
16
- instrumentProcessMethods(shim, process)
17
- }
18
-
19
- instrumentTimerMethods(timers)
13
+ function initialize(agent, timers, _moduleName, shim) {
14
+ const isAsyncLocalContext = agent.config.feature_flag.async_local_context
20
15
 
21
- // If we need to instrument separate references to timers on the global object,
22
- // do that now.
23
- if (!shim.isWrapped(global.setTimeout)) {
24
- instrumentTimerMethods(global)
16
+ if (!isAsyncLocalContext) {
17
+ instrumentProcessMethods(shim, process)
18
+ instrumentSetImmediate(shim, [timers, global])
25
19
  }
26
20
 
27
- function instrumentTimerMethods(nodule) {
28
- const asynchronizers = ['setTimeout', 'setInterval']
29
-
30
- shim.record(nodule, asynchronizers, recordAsynchronizers)
21
+ instrumentTimerMethods(shim, [timers, global])
22
+ }
31
23
 
32
- if (!agent.config.feature_flag.async_local_context) {
33
- // We don't want to create segments for setImmediate calls, as the
34
- // object allocation may incur too much overhead in some situations
35
- shim.wrap(nodule, 'setImmediate', wrapSetImmediate)
24
+ /**
25
+ * Sets up instrumentation for setImmediate on both timers and global.
26
+ *
27
+ * We do not want to create segments for setImmediate calls,
28
+ * as the object allocation may incur too much overhead in some situations
29
+ *
30
+ * @param {Shim} shim instance of shim
31
+ * @param {Array<Timers,global>} pkgs array with references to timers and global
32
+ */
33
+ function instrumentSetImmediate(shim, pkgs) {
34
+ pkgs.forEach((nodule) => {
35
+ if (shim.isWrapped(nodule.setImmediate)) {
36
+ return
36
37
  }
37
38
 
38
- shim.wrap(nodule, 'clearTimeout', wrapClearTimeout)
39
+ shim.wrap(nodule, 'setImmediate', function wrapSetImmediate(shim, fn) {
40
+ return function wrappedSetImmediate() {
41
+ const segment = shim.getActiveSegment()
42
+ if (!segment) {
43
+ return fn.apply(this, arguments)
44
+ }
39
45
 
40
- makeWrappedPromisifyCompatible(shim, nodule)
41
- }
46
+ const args = shim.argsToArray.apply(shim, arguments, segment)
47
+ shim.bindSegment(args, shim.FIRST)
42
48
 
43
- function wrapSetImmediate(shim, fn) {
44
- return function wrappedSetImmediate() {
45
- const segment = shim.getActiveSegment()
46
- if (!segment) {
47
- return fn.apply(this, arguments)
49
+ return fn.apply(this, args)
48
50
  }
51
+ })
49
52
 
50
- const args = shim.argsToArray.apply(shim, arguments, segment)
51
- shim.bindSegment(args, shim.FIRST)
53
+ copySymbols(shim, nodule, 'setImmediate')
54
+ })
55
+ }
52
56
 
53
- return fn.apply(this, args)
57
+ /**
58
+ * Sets up instrumentation for setTimeout, setInterval and clearTimeout
59
+ * on timers and global.
60
+ *
61
+ * @param {Shim} shim instance of shim
62
+ * @param {Array<Timers,global>} pkgs array with references to timers and global
63
+ */
64
+ function instrumentTimerMethods(shim, pkgs) {
65
+ pkgs.forEach((nodule) => {
66
+ if (shim.isWrapped(nodule.setTimeout)) {
67
+ return
54
68
  }
55
- }
56
69
 
57
- function wrapClearTimeout(shim, fn) {
58
- return function wrappedClearTimeout(timer) {
59
- if (timer && timer._onTimeout) {
60
- const segment = timer._onTimeout[symbols.segment]
61
- if (segment && !segment.opaque) {
62
- segment.ignore = true
63
- }
64
- }
70
+ const asynchronizers = ['setTimeout', 'setInterval']
71
+ shim.record(nodule, asynchronizers, recordAsynchronizers)
72
+ shim.wrap(nodule, 'clearTimeout', wrapClearTimeout)
73
+ makeWrappedPromisifyCompatible(shim, nodule)
74
+ })
75
+ }
65
76
 
66
- return fn.apply(this, arguments)
77
+ /**
78
+ * Ignores the segment when clearTimeout is called
79
+ *
80
+ * @param {Shim} _shim instance of shim
81
+ * @param {Function} fn clearTimeout
82
+ * @returns {Function} wrapped clearTimeout
83
+ */
84
+ function wrapClearTimeout(_shim, fn) {
85
+ return function wrappedClearTimeout(timer) {
86
+ if (timer && timer._onTimeout) {
87
+ const segment = timer._onTimeout[symbols.segment]
88
+ if (segment && !segment.opaque) {
89
+ segment.ignore = true
90
+ }
67
91
  }
68
- }
69
92
 
70
- function recordAsynchronizers(shim, fn, name) {
71
- return { name: 'timers.' + name, callback: shim.FIRST }
93
+ return fn.apply(this, arguments)
72
94
  }
73
95
  }
74
96
 
97
+ /**
98
+ * Defines the spec for setTimeout and setInterval
99
+ *
100
+ * @param {Shim} shim instance of shim
101
+ * @param {Function} _fn original function
102
+ * @param {string} name name of function
103
+ * @returns {object} spec defining how to instrument
104
+ */
105
+ function recordAsynchronizers(shim, _fn, name) {
106
+ return { name: 'timers.' + name, callback: shim.FIRST }
107
+ }
108
+
109
+ /**
110
+ * Instruments core process methods: nextTick, _nextDomainTick, _tickDomainCallback
111
+ * Note: This does not get registered when the context manager is async local
112
+ *
113
+ * @param {Shim} shim instance of shim
114
+ * @param {process} process global process object
115
+ */
75
116
  function instrumentProcessMethods(shim, process) {
76
117
  const processMethods = ['nextTick', '_nextDomainTick', '_tickDomainCallback']
77
118
 
@@ -96,21 +137,27 @@ function instrumentProcessMethods(shim, process) {
96
137
  })
97
138
  }
98
139
 
99
- function makeWrappedPromisifyCompatible(shim, timers) {
100
- const originalSetTimeout = shim.getOriginal(timers.setTimeout)
101
- Object.getOwnPropertySymbols(originalSetTimeout).forEach((symbol) => {
102
- timers.setTimeout[symbol] = originalSetTimeout[symbol]
103
- })
140
+ /**
141
+ * Copies the symbols from original setTimeout and setInterval onto the wrapped functions
142
+ *
143
+ * @param {Shim} shim instance of shim
144
+ * @param {Timers} nodule Timers class
145
+ */
146
+ function makeWrappedPromisifyCompatible(shim, nodule) {
147
+ copySymbols(shim, nodule, 'setTimeout')
148
+ copySymbols(shim, nodule, 'setInterval')
149
+ }
104
150
 
105
- const originalSetInterval = shim.getOriginal(timers.setInterval)
106
- Object.getOwnPropertySymbols(originalSetInterval).forEach((symbol) => {
107
- timers.setInterval[symbol] = originalSetInterval[symbol]
151
+ /**
152
+ * Helper to copy symbols from original function to wrapped one
153
+ *
154
+ * @param {Shim} shim instance of shim
155
+ * @param {Timers} nodule Timers class
156
+ * @param {string} name name of function
157
+ */
158
+ function copySymbols(shim, nodule, name) {
159
+ const originalFunction = shim.getOriginal(nodule[name])
160
+ Object.getOwnPropertySymbols(originalFunction).forEach((symbol) => {
161
+ nodule[name][symbol] = originalFunction[symbol]
108
162
  })
109
-
110
- if (!shim.agent.config.feature_flag.async_local_context) {
111
- const originalSetImmediate = shim.getOriginal(timers.setImmediate)
112
- Object.getOwnPropertySymbols(originalSetImmediate).forEach((symbol) => {
113
- timers.setImmediate[symbol] = originalSetImmediate[symbol]
114
- })
115
- }
116
163
  }
@@ -12,11 +12,23 @@ const DESTINATION = DESTINATIONS.TRANS_EVENT | DESTINATIONS.ERROR_EVENT
12
12
  const semver = require('semver')
13
13
 
14
14
  module.exports = function instrument(shim) {
15
- const genericShim = shim.makeSpecializedShim(shim.GENERIC, 'call-stream')
16
- const callStream = genericShim.require('./build/src/call-stream')
17
- genericShim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
15
+ const grpcVersion = shim.require('./package.json').version
16
+ const genericShim = shim.makeSpecializedShim(shim.GENERIC, 'grpc-client-interceptor')
17
+
18
+ if (semver.gte(grpcVersion, '1.8.0')) {
19
+ const resolvingCall = genericShim.require('./build/src/resolving-call')
20
+ genericShim.wrap(resolvingCall.ResolvingCall.prototype, 'start', wrapStart)
21
+ } else {
22
+ const callStream = genericShim.require('./build/src/call-stream')
23
+ genericShim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
24
+ }
18
25
 
19
26
  const webFrameworkShim = shim.makeSpecializedShim(shim.WEB_FRAMEWORK, 'server')
27
+ if (semver.lt(grpcVersion, '1.4.0')) {
28
+ shim.logger.debug('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
29
+ return
30
+ }
31
+
20
32
  const server = webFrameworkShim.require('./build/src/server')
21
33
  webFrameworkShim.setFramework('gRPC')
22
34
  webFrameworkShim.wrap(server.Server.prototype, 'register', wrapRegister)
@@ -41,9 +53,11 @@ function wrapStart(shim, original) {
41
53
 
42
54
  const channel = this.channel
43
55
  const authorityName = (channel.target && channel.target.path) || channel.getDefaultAuthority
56
+ // in 1.8.0 this changed from methodName to method
57
+ const method = this.methodName || this.method
44
58
 
45
59
  const segment = shim.createSegment({
46
- name: `External/${authorityName}${this.methodName}`,
60
+ name: `External/${authorityName}${method}`,
47
61
  opaque: true,
48
62
  recorder: recordExternal(authorityName, 'gRPC')
49
63
  })
@@ -89,10 +103,10 @@ function wrapStart(shim, original) {
89
103
 
90
104
  const protocol = 'grpc'
91
105
 
92
- const url = `${protocol}://${authorityName}${this.methodName}`
106
+ const url = `${protocol}://${authorityName}${method}`
93
107
 
94
108
  segment.addAttribute('http.url', url)
95
- segment.addAttribute('http.method', this.methodName)
109
+ segment.addAttribute('http.method', method)
96
110
 
97
111
  if (originalListener && originalListener.onReceiveStatus) {
98
112
  const onReceiveStatuts = shim.bindSegment(originalListener.onReceiveStatus, segment)
@@ -212,20 +226,3 @@ function shouldTrackError(statusCode, config) {
212
226
  !config.grpc.ignore_status_codes.includes(statusCode)
213
227
  )
214
228
  }
215
-
216
- module.exports = function instrument(shim) {
217
- const genericShim = shim.makeSpecializedShim(shim.GENERIC, 'call-stream')
218
- const callStream = genericShim.require('./build/src/call-stream')
219
- genericShim.wrap(callStream.Http2CallStream.prototype, 'start', wrapStart)
220
-
221
- const webFrameworkShim = shim.makeSpecializedShim(shim.WEB_FRAMEWORK, 'server')
222
- const grpcVersion = shim.require('./package.json').version
223
- if (semver.lt(grpcVersion, '1.4.0')) {
224
- shim.logger.debug('gRPC server-side instrumentation only supported on grpc-js >=1.4.0')
225
- return
226
- }
227
-
228
- const server = webFrameworkShim.require('./build/src/server')
229
- webFrameworkShim.setFramework('gRPC')
230
- webFrameworkShim.wrap(server.Server.prototype, 'register', wrapRegister)
231
- }