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
@@ -3,61 +3,70 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
- /* eslint sonarjs/cognitive-complexity: ["error", 20] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
7
-
8
6
  'use strict'
9
7
  const CLIENT_COMMANDS = ['select', 'quit', 'SELECT', 'QUIT']
8
+ const opts = Symbol('clientOptions')
10
9
 
11
- module.exports = function initialize(agent, redis, moduleName, shim) {
10
+ module.exports = function initialize(_agent, redis, _moduleName, shim) {
12
11
  shim.setDatastore(shim.REDIS)
13
12
  const COMMANDS = Object.keys(shim.require('dist/lib/client/commands.js').default)
14
13
  const CMDS_TO_INSTRUMENT = [...COMMANDS, ...CLIENT_COMMANDS]
15
14
  shim.wrap(redis, 'createClient', function wrapCreateClient(shim, original) {
16
15
  return function wrappedCreateClient() {
17
16
  const client = original.apply(this, arguments)
18
- const clientOptions = getRedisParams(client.options)
19
- CMDS_TO_INSTRUMENT.forEach((cmd) => {
20
- shim.recordOperation(client, cmd, function wrapCommand(shim, fn, fnName, args) {
21
- const [key, value] = args
22
- const parameters = Object.assign({}, clientOptions)
23
- // If selecting a database, subsequent commands
24
- // will be using said database, update the clientOptions
25
- // but not the current parameters(feature parity with v3)
26
- if (cmd.toLowerCase() === 'select') {
27
- clientOptions.database_name = key
28
- }
29
- if (agent.config.attributes.enabled) {
30
- if (key) {
31
- parameters.key = JSON.stringify(key)
32
- }
33
- if (value) {
34
- parameters.value = JSON.stringify(value)
35
- }
36
- }
37
-
38
- return {
39
- name: (cmd && cmd.toLowerCase()) || 'other',
40
- parameters,
41
- promise: true
42
- }
43
- })
44
- })
45
-
17
+ client[opts] = getRedisParams(client.options)
18
+ CMDS_TO_INSTRUMENT.forEach(instrumentClientCommand.bind(null, shim, client))
46
19
  return client
47
20
  }
48
21
  })
22
+ }
23
+
24
+ /**
25
+ * Instruments a given command on the client by calling `shim.recordOperation`
26
+ *
27
+ * @param {Shim} shim shim instance
28
+ * @param {object} client redis client instance
29
+ * @param {string} cmd command to instrument
30
+ */
31
+ function instrumentClientCommand(shim, client, cmd) {
32
+ const { agent } = shim
33
+
34
+ shim.recordOperation(client, cmd, function wrapCommand(_shim, _fn, _fnName, args) {
35
+ const [key, value] = args
36
+ const parameters = Object.assign({}, client[opts])
37
+ // If selecting a database, subsequent commands
38
+ // will be using said database, update the clientOptions
39
+ // but not the current parameters(feature parity with v3)
40
+ if (cmd.toLowerCase() === 'select') {
41
+ client[opts].database_name = key
42
+ }
43
+ if (agent.config.attributes.enabled) {
44
+ if (key) {
45
+ parameters.key = JSON.stringify(key)
46
+ }
47
+ if (value) {
48
+ parameters.value = JSON.stringify(value)
49
+ }
50
+ }
49
51
 
50
- /**
51
- * Extracts the datastore parameters from the client options
52
- *
53
- * @param {object} opts client.options
54
- * @returns {object} params
55
- */
56
- function getRedisParams(opts) {
57
52
  return {
58
- host: (opts.socket && opts.socket.host) || 'localhost',
59
- port_path_or_id: (opts.socket && (opts.socket.path || opts.socket.port)) || '6379',
60
- database_name: opts.database || 0
53
+ name: (cmd && cmd.toLowerCase()) || 'other',
54
+ parameters,
55
+ promise: true
61
56
  }
57
+ })
58
+ }
59
+
60
+ /**
61
+ * Extracts the datastore parameters from the client options
62
+ *
63
+ * @param {object} clientOpts client.options
64
+ * @returns {object} params
65
+ */
66
+ function getRedisParams(clientOpts) {
67
+ return {
68
+ host: clientOpts?.socket?.host || 'localhost',
69
+ port_path_or_id: clientOpts?.socket?.path || clientOpts?.socket?.port || '6379',
70
+ database_name: clientOpts.database || 0
62
71
  }
63
72
  }
@@ -5,17 +5,8 @@
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 url = require('url')
11
9
 
12
- // TODO: Make this an external module.
13
- // var newrelic = require('newrelic')
14
- // newrelic.instrumentMessages('amqplib', instrumentChannelAPI)
15
- // newrelic.instrumentMessages('amqplib/channel_api', instrumentChannelAPI)
16
- // newrelic.instrumentMessages('amqplib/channel_api.js', instrumentChannelAPI)
17
- // newrelic.instrumentMessages('amqplib/callback_api', instrumentCallbackAPI)
18
- // newrelic.instrumentMessages('amqplib/callback_api.js', instrumentCallbackAPI)
19
10
  module.exports.selfRegister = function selfRegister(shimmer) {
20
11
  shimmer.registerInstrumentation({
21
12
  moduleName: 'amqplib',
@@ -67,20 +58,47 @@ const CHANNEL_METHODS = [
67
58
 
68
59
  const TEMP_RE = /^amq\./
69
60
 
61
+ /**
62
+ * Register all the necessary instrumentation when using
63
+ * promise based methods
64
+ *
65
+ * @param {Shim} shim instance of shim
66
+ * @param {object} amqp amqplib object
67
+ */
70
68
  function instrumentChannelAPI(shim, amqp) {
71
69
  instrumentAMQP(shim, amqp, true)
72
- wrapPromiseChannel(shim)
70
+ // 👀 take note the model is channel not callback 👀
71
+ const model = shim.require('./lib/channel_model')
72
+ wrapModel(shim, model, true)
73
73
  }
74
74
 
75
+ /**
76
+ * Register all the necessary instrumentation when using
77
+ * callback based methods
78
+ *
79
+ * @param {Shim} shim instance of shim
80
+ * @param {object} amqp amqplib object
81
+ */
75
82
  function instrumentCallbackAPI(shim, amqp) {
76
83
  instrumentAMQP(shim, amqp, false)
77
- wrapCallbackChannel(shim)
84
+ // 👀 take note the model is callback not channel 👀
85
+ const model = shim.require('./lib/callback_model')
86
+ wrapModel(shim, model, false)
78
87
  }
79
88
 
89
+ /**
90
+ *
91
+ * Instruments the connect method and channel prototype of amqplib
92
+ *
93
+ * @param {Shim} shim instance of shim
94
+ * @param {object} amqp amqplib object
95
+ * @param {boolean} promiseMode is this promise based?
96
+ * @returns {void}
97
+ */
80
98
  function instrumentAMQP(shim, amqp, promiseMode) {
81
99
  if (!amqp || !amqp.connect) {
82
100
  shim.logger.debug("This module is not the amqplib we're looking for.")
83
- return false
101
+ return
84
102
  }
85
103
 
86
104
  if (shim.isWrapped(amqp.connect)) {
@@ -89,8 +107,33 @@ function instrumentAMQP(shim, amqp, promiseMode) {
89
107
  }
90
108
  shim.setLibrary(shim.RABBITMQ)
91
109
 
110
+ wrapConnect(shim, amqp, promiseMode)
111
+ wrapChannel(shim)
112
+ }
113
+
114
+ /**
115
+ * Helper to set the appropriate value of the callback property
116
+ * in the spec. If it's a promise set to null otherwise set it to `shim.LAST`
117
+ *
118
+ * @param {Shim} shim instance of shim
119
+ * @param {boolean} promiseMode is this promise based?
120
+ * @returns {string|null} appropriate value
121
+ */
122
+ function setCallback(shim, promiseMode) {
123
+ return promiseMode ? null : shim.LAST
124
+ }
125
+
126
+ /**
127
+ *
128
+ * Instruments the connect method
129
+ *
130
+ * @param {Shim} shim instance of shim
131
+ * @param {object} amqp amqplib object
132
+ * @param {boolean} promiseMode is this promise based?
133
+ */
134
+ function wrapConnect(shim, amqp, promiseMode) {
92
135
  shim.record(amqp, 'connect', function recordConnect(shim, connect, name, args) {
93
- let connArgs = args[0]
136
+ let [connArgs] = args
94
137
  let params = null
95
138
 
96
139
  if (shim.isString(connArgs)) {
@@ -103,21 +146,24 @@ function instrumentAMQP(shim, amqp, promiseMode) {
103
146
 
104
147
  return {
105
148
  name: 'amqplib.connect',
106
- callback: promiseMode ? null : shim.LAST,
149
+ callback: setCallback(shim, promiseMode),
107
150
  promise: promiseMode,
108
151
  parameters: params,
109
-
110
152
  stream: null,
111
153
  recorder: null
112
154
  }
113
155
  })
114
-
115
- wrapChannel(shim)
116
156
  }
117
157
 
158
+ /**
159
+ *
160
+ * Instruments the sendOrEnqueue and sendMessage methods of the ampqlib channel.
161
+ *
162
+ * @param {Shim} shim instance of shim
163
+ */
118
164
  function wrapChannel(shim) {
119
165
  const libChannel = shim.require('./lib/channel')
120
- if (!libChannel || !libChannel.Channel || !libChannel.Channel.prototype) {
166
+ if (!libChannel?.Channel?.prototype) {
121
167
  shim.logger.debug('Could not get Channel class to instrument.')
122
168
  return
123
169
  }
@@ -149,29 +195,7 @@ function wrapChannel(shim) {
149
195
  }
150
196
  })
151
197
 
152
- // Example fields:
153
- // { exchange: 'test-exchange-topic',
154
- // routingKey: 'routing.key',
155
- // mandatory: false,
156
- // immediate: false,
157
- // ticket: undefined,
158
- // contentType: undefined,
159
- // contentEncoding: undefined,
160
- // headers: {},
161
- // deliveryMode: undefined,
162
- // priority: undefined,
163
- // correlationId: undefined,
164
- // replyTo: undefined,
165
- // expiration: undefined,
166
- // messageId: undefined,
167
- // timestamp: undefined,
168
- // type: undefined,
169
- // userId: undefined,
170
- // appId: undefined,
171
- // clusterId: undefined }
172
-
173
- shim.recordProduce(proto, 'sendMessage', recordSendMessage)
174
- function recordSendMessage(shim, fn, n, args) {
198
+ shim.recordProduce(proto, 'sendMessage', function recordSendMessage(shim, fn, n, args) {
175
199
  const fields = args[0]
176
200
  if (!fields) {
177
201
  return null
@@ -189,9 +213,16 @@ function wrapChannel(shim) {
189
213
  headers: fields.headers,
190
214
  parameters: getParameters(Object.create(null), fields)
191
215
  }
192
- }
216
+ })
193
217
  }
194
218
 
219
+ /**
220
+ * Sets the relevant message parameters
221
+ *
222
+ * @param {object} parameters object used to store the message parameters
223
+ * @param {object} fields fields from the sendMessage method
224
+ * @returns {object} parameters updated parameters
225
+ */
195
226
  function getParameters(parameters, fields) {
196
227
  if (fields.routingKey) {
197
228
  parameters.routing_key = fields.routingKey
@@ -206,29 +237,42 @@ function getParameters(parameters, fields) {
206
237
  return parameters
207
238
  }
208
239
 
209
- function wrapPromiseChannel(shim) {
210
- const libPModel = shim.require('./lib/channel_model')
211
- if (!libPModel || !libPModel.Channel || !libPModel.Channel.prototype) {
212
- shim.logger.debug('Could not get promise model Channel to instrument')
240
+ /**
241
+ *
242
+ * Instruments the relevant channel callback_model or channel_model.
243
+ *
244
+ * @param {Shim} shim instance of shim
245
+ * @param {object} Model either channel or callback model
246
+ * @param {boolean} promiseMode is this promise based?
247
+ */
248
+ function wrapModel(shim, Model, promiseMode) {
249
+ if (!Model.Channel?.prototype) {
250
+ shim.logger.debug(
251
+ `Could not get ${promiseMode ? 'promise' : 'callback'} model Channel to instrument.`
252
+ )
213
253
  }
214
254
 
215
- const proto = libPModel.Channel.prototype
255
+ const proto = Model.Channel.prototype
216
256
  if (shim.isWrapped(proto.consume)) {
217
- shim.logger.trace('Promise model already instrumented.')
257
+ shim.logger.trace(`${promiseMode ? 'promise' : 'callback'} model already instrumented.`)
218
258
  return
219
259
  }
220
260
 
221
261
  shim.record(proto, CHANNEL_METHODS, function recordChannelMethod(shim, fn, name) {
222
262
  return {
223
263
  name: 'Channel#' + name,
224
- promise: true
264
+ callback: setCallback(shim, promiseMode),
265
+ promise: promiseMode
225
266
  }
226
267
  })
227
268
 
228
269
  shim.recordConsume(proto, 'get', {
229
270
  destinationName: shim.FIRST,
230
- promise: true,
271
+ callback: setCallback(shim, promiseMode),
272
+ promise: promiseMode,
231
273
  messageHandler: function handleConsumedMessage(shim, fn, name, message) {
274
+ // the message is the param when using the promised based model
275
+ message = promiseMode ? message : message[1]
232
276
  if (!message) {
233
277
  shim.logger.trace('No results from consume.')
234
278
  return null
@@ -237,95 +281,9 @@ function wrapPromiseChannel(shim) {
237
281
  getParameters(parameters, message.fields)
238
282
  getParameters(parameters, message.properties)
239
283
 
240
- let headers = null
241
- if (message.properties && message.properties.headers) {
242
- headers = message.properties.headers
243
- }
244
-
245
- return { parameters: parameters, headers: headers }
246
- }
247
- })
248
-
249
- shim.recordPurgeQueue(proto, 'purgeQueue', function recordPurge(shim, fn, name, args) {
250
- let queue = args[0] || null
251
- if (TEMP_RE.test(queue)) {
252
- queue = null
253
- }
254
-
255
- return { queue: queue, promise: true }
256
- })
257
-
258
- shim.recordSubscribedConsume(proto, 'consume', {
259
- name: 'amqplib.Channel#consume',
260
- queue: shim.FIRST,
261
- consumer: shim.SECOND,
262
- promise: true,
263
- messageHandler: describeMessage
264
- })
265
- }
266
-
267
- function wrapCallbackChannel(shim) {
268
- const libCbModel = shim.require('./lib/callback_model')
269
- if (!libCbModel || !libCbModel.Channel || !libCbModel.Channel.prototype) {
270
- shim.logger.debug('Could not get callback model Channel to instrument')
271
- return
272
- }
273
-
274
- const proto = libCbModel.Channel.prototype
275
- if (shim.isWrapped(proto.consume)) {
276
- return
277
- }
278
-
279
- // Example message:
280
- // { fields:
281
- // { consumerTag: 'amq.ctag-8oZE10ovvyAP8e-vgbOnSA',
282
- // deliveryTag: 1,
283
- // redelivered: false,
284
- // exchange: 'test-exchange-topic',
285
- // routingKey: 'routing.key' },
286
- // properties:
287
- // { contentType: undefined,
288
- // contentEncoding: undefined,
289
- // headers: {},
290
- // deliveryMode: undefined,
291
- // priority: undefined,
292
- // correlationId: undefined,
293
- // replyTo: undefined,
294
- // expiration: undefined,
295
- // messageId: undefined,
296
- // timestamp: undefined,
297
- // type: undefined,
298
- // userId: undefined,
299
- // appId: undefined,
300
- // clusterId: undefined },
301
- // content: Buffer [ 97 ] }
302
-
303
- shim.record(proto, CHANNEL_METHODS, function recordChannelMethod(shim, fn, name) {
304
- return {
305
- name: 'Channel#' + name,
306
- callback: shim.LAST
307
- }
308
- })
309
-
310
- shim.recordConsume(proto, 'get', {
311
- destinationName: shim.FIRST,
312
- callback: shim.LAST,
313
- messageHandler: function handleConsumedMessage(shim, fn, name, args) {
314
- const message = args[1]
315
- if (!message) {
316
- shim.logger.trace('No results from consume.')
317
- return null
318
- }
319
- const parameters = Object.create(null)
320
- getParameters(parameters, message.fields)
321
- getParameters(parameters, message.properties)
322
-
323
- let headers = null
324
- if (message.properties && message.properties.headers) {
325
- headers = message.properties.headers
326
- }
284
+ const headers = message?.properties?.headers
327
285
 
328
- return { parameters: parameters, headers: headers }
286
+ return { parameters, headers }
329
287
  }
330
288
  })
331
289
 
@@ -334,42 +292,49 @@ function wrapCallbackChannel(shim) {
334
292
  if (TEMP_RE.test(queue)) {
335
293
  queue = null
336
294
  }
337
-
338
- return { queue: queue, callback: shim.LAST }
295
+ return { queue, promise: promiseMode, callback: setCallback(shim, promiseMode) }
339
296
  })
340
297
 
341
298
  shim.recordSubscribedConsume(proto, 'consume', {
342
299
  name: 'amqplib.Channel#consume',
343
300
  queue: shim.FIRST,
344
301
  consumer: shim.SECOND,
345
- callback: shim.FOURTH,
346
- promise: false,
302
+ promise: promiseMode,
303
+ callback: promiseMode ? null : shim.FOURTH,
347
304
  messageHandler: describeMessage
348
305
  })
349
306
  }
350
307
 
351
- function describeMessage(shim, consumer, name, args) {
352
- const message = args[0]
353
- if (!message || !message.properties) {
308
+ /**
309
+ * Extracts the appropriate messageHandler parameters for the consume method.
310
+ *
311
+ * @param {Shim} shim instance of shim
312
+ * @param {object} _consumer not used
313
+ * @param {string} _name not used
314
+ * @param {Array} args arguments passed to the consume method
315
+ * @returns {object} message params
316
+ */
317
+ function describeMessage(shim, _consumer, _name, args) {
318
+ const [message] = args
319
+
320
+ if (!message?.properties) {
354
321
  shim.logger.debug({ message: message }, 'Failed to find message in consume arguments.')
355
322
  return null
356
323
  }
357
324
 
358
- let exchangeName = message.fields.exchange
359
325
  const parameters = getParameters(Object.create(null), message.fields)
360
326
  getParameters(parameters, message.properties)
327
+ let exchangeName = message?.fields?.exchange || 'Default'
361
328
 
362
- if (!exchangeName) {
363
- exchangeName = 'Default'
364
- } else if (TEMP_RE.test(exchangeName)) {
329
+ if (TEMP_RE.test(exchangeName)) {
365
330
  exchangeName = null
366
331
  }
367
332
 
368
333
  return {
369
334
  destinationName: exchangeName,
370
335
  destinationType: shim.EXCHANGE,
371
- routingKey: message.fields.routingKey,
336
+ routingKey: message?.fields?.routingKey,
372
337
  headers: message.properties.headers,
373
- parameters: parameters
338
+ parameters
374
339
  }
375
340
  }
@@ -5,8 +5,6 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- /* eslint sonarjs/cognitive-complexity: ["error", 18] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */
9
-
10
8
  const logger = require('../../logger').child({ component: 'async_hooks' })
11
9
  const asyncHooks = require('async_hooks')
12
10
 
@@ -26,7 +24,8 @@ function initialize(agent, shim) {
26
24
  const segmentMap = new Map()
27
25
  module.exports.segmentMap = segmentMap
28
26
 
29
- const hookHandlers = getPromiseResolveStyleHooks(segmentMap, agent, shim)
27
+ const hookHandlers = getHookHandlers(segmentMap, agent, shim)
28
+ maybeRegisterDestroyHook(segmentMap, agent, hookHandlers)
30
29
 
31
30
  const hook = asyncHooks.createHook(hookHandlers)
32
31
  hook.enable()
@@ -38,8 +37,18 @@ function initialize(agent, shim) {
38
37
  return true
39
38
  }
40
39
 
41
- function getPromiseResolveStyleHooks(segmentMap, agent, shim) {
42
- const hooks = {
40
+ /**
41
+ * Registers the async hooks events
42
+ *
43
+ * Note: The init only fires when the type is PROMISE.
44
+ *
45
+ * @param {Map} segmentMap map of async ids and segments
46
+ * @param {Agent} agent New Relic APM agent
47
+ * @param {Shim} shim instance of shim
48
+ * @returns {object} event handlers for async hooks
49
+ */
50
+ function getHookHandlers(segmentMap, agent, shim) {
51
+ return {
43
52
  init: function initHook(id, type, triggerId) {
44
53
  if (type !== 'PROMISE') {
45
54
  return
@@ -101,17 +110,23 @@ function getPromiseResolveStyleHooks(segmentMap, agent, shim) {
101
110
  }
102
111
  }
103
112
  }
113
+ }
104
114
 
105
- // Clean up any unresolved promises that have been destroyed.
106
- // This defaults to true but does have a significant performance impact
107
- // when customers have a lot of promises.
108
- // See: https://github.com/newrelic/node-newrelic/issues/760
115
+ /**
116
+ * Adds the destroy async hook event that will lean up any unresolved promises that have been destroyed.
117
+ * This defaults to true but does have a significant performance impact
118
+ * when customers have a lot of promises.
119
+ * See: https://github.com/newrelic/node-newrelic/issues/760
120
+ *
121
+ * @param {Map} segmentMap map of async ids and segments
122
+ * @param {Agent} agent New Relic APM agent
123
+ * @param {object} hooks async-hook events
124
+ */
125
+ function maybeRegisterDestroyHook(segmentMap, agent, hooks) {
109
126
  if (agent.config.feature_flag.unresolved_promise_cleanup) {
110
127
  logger.info('Adding destroy hook to clean up unresolved promises.')
111
128
  hooks.destroy = function destroyHandler(id) {
112
129
  segmentMap.delete(id)
113
130
  }
114
131
  }
115
-
116
- return hooks
117
132
  }
@@ -5,7 +5,7 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- const asyncHooks = require('./async_hooks')
8
+ const asyncHooks = require('./async-hooks')
9
9
  const symbols = require('../../symbols')
10
10
 
11
11
  module.exports = initialize