dd-trace 2.2.1 → 2.4.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.
Files changed (41) hide show
  1. package/index.d.ts +57 -0
  2. package/package.json +2 -2
  3. package/packages/datadog-instrumentations/index.js +9 -0
  4. package/packages/datadog-instrumentations/src/amqp10.js +70 -0
  5. package/packages/datadog-instrumentations/src/amqplib.js +58 -0
  6. package/packages/datadog-instrumentations/src/cassandra-driver.js +191 -0
  7. package/packages/datadog-instrumentations/src/cucumber.js +118 -0
  8. package/packages/datadog-instrumentations/src/elasticsearch.js +9 -4
  9. package/packages/datadog-instrumentations/src/helpers/instrument.js +3 -3
  10. package/packages/datadog-instrumentations/src/ioredis.js +8 -5
  11. package/packages/datadog-instrumentations/src/mocha.js +122 -0
  12. package/packages/datadog-instrumentations/src/mongodb-core.js +179 -0
  13. package/packages/datadog-instrumentations/src/pg.js +75 -0
  14. package/packages/datadog-instrumentations/src/redis.js +8 -8
  15. package/packages/datadog-instrumentations/src/rhea.js +224 -0
  16. package/packages/datadog-instrumentations/src/tedious.js +66 -0
  17. package/packages/datadog-plugin-amqp10/src/index.js +79 -122
  18. package/packages/datadog-plugin-amqplib/src/index.js +77 -142
  19. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
  20. package/packages/datadog-plugin-cassandra-driver/src/index.js +52 -224
  21. package/packages/datadog-plugin-cucumber/src/index.js +85 -128
  22. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +5 -3
  23. package/packages/datadog-plugin-mocha/src/index.js +96 -207
  24. package/packages/datadog-plugin-mongodb-core/src/index.js +119 -3
  25. package/packages/datadog-plugin-pg/src/index.js +32 -69
  26. package/packages/datadog-plugin-rhea/src/index.js +59 -225
  27. package/packages/datadog-plugin-tedious/src/index.js +38 -86
  28. package/packages/dd-trace/lib/version.js +1 -1
  29. package/packages/dd-trace/src/appsec/recommended.json +137 -116
  30. package/packages/dd-trace/src/config.js +13 -1
  31. package/packages/dd-trace/src/noop/tracer.js +4 -0
  32. package/packages/dd-trace/src/opentracing/propagation/text_map.js +34 -1
  33. package/packages/dd-trace/src/opentracing/tracer.js +1 -1
  34. package/packages/dd-trace/src/plugins/log_plugin.js +10 -5
  35. package/packages/dd-trace/src/plugins/util/git.js +1 -1
  36. package/packages/dd-trace/src/proxy.js +4 -0
  37. package/packages/dd-trace/src/span_processor.js +22 -7
  38. package/packages/dd-trace/src/tracer.js +16 -0
  39. package/packages/datadog-plugin-mongodb-core/src/legacy.js +0 -59
  40. package/packages/datadog-plugin-mongodb-core/src/unified.js +0 -138
  41. package/packages/datadog-plugin-mongodb-core/src/util.js +0 -143
package/index.d.ts CHANGED
@@ -108,6 +108,13 @@ export declare interface Tracer extends opentracing.Tracer {
108
108
  * should not be cached.
109
109
  */
110
110
  getRumData(): string;
111
+
112
+ /**
113
+ * Links an authenticated user to the current trace.
114
+ * @param {User} user Properties of the authenticated user. Accepts custom fields.
115
+ * @returns {Tracer} The Tracer instance for chaining.
116
+ */
117
+ setUser(user: User): Tracer;
111
118
  }
112
119
 
113
120
  export declare interface TraceOptions extends Analyzable {
@@ -266,6 +273,12 @@ export declare interface TracerOptions {
266
273
  */
267
274
  flushInterval?: number;
268
275
 
276
+ /**
277
+ * Number of spans before partially exporting a trace. This prevents keeping all the spans in memory for very large traces.
278
+ * @default 1000
279
+ */
280
+ flushMinSpans?: number;
281
+
269
282
  /**
270
283
  * Whether to enable runtime metrics.
271
284
  * @default false
@@ -305,6 +318,7 @@ export declare interface TracerOptions {
305
318
  */
306
319
  experimental?: boolean | {
307
320
  b3?: boolean
321
+ traceparent?: boolean
308
322
 
309
323
  /**
310
324
  * Whether to add an auto-generated `runtime-id` tag to metrics.
@@ -418,6 +432,49 @@ export declare interface TracerOptions {
418
432
  };
419
433
  }
420
434
 
435
+ /**
436
+ * User object that can be passed to `tracer.setUser()`.
437
+ */
438
+ export declare interface User {
439
+ /**
440
+ * Unique identifier of the user.
441
+ * Mandatory.
442
+ */
443
+ id: string,
444
+
445
+ /**
446
+ * Email of the user.
447
+ */
448
+ email?: string,
449
+
450
+ /**
451
+ * User-friendly name of the user.
452
+ */
453
+ name?: string,
454
+
455
+ /**
456
+ * Session ID of the user.
457
+ */
458
+ session_id?: string,
459
+
460
+ /**
461
+ * Role the user is making the request under.
462
+ */
463
+ role?: string,
464
+
465
+ /**
466
+ * Scopes or granted authorizations the user currently possesses.
467
+ * The value could come from the scope associated with an OAuth2
468
+ * Access Token or an attribute value in a SAML 2 Assertion.
469
+ */
470
+ scope?: string,
471
+
472
+ /**
473
+ * Custom fields to attach to the user (RBAC, Oauth, etc…).
474
+ */
475
+ [key: string]: string | undefined
476
+ }
477
+
421
478
  /** @hidden */
422
479
  interface EventEmitter {
423
480
  emit(eventName: string | symbol, ...args: any[]): any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "2.2.1",
3
+ "version": "2.4.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -61,7 +61,7 @@
61
61
  "node": ">=12"
62
62
  },
63
63
  "dependencies": {
64
- "@datadog/native-appsec": "^0.8.1",
64
+ "@datadog/native-appsec": "^0.8.2",
65
65
  "@datadog/native-metrics": "^1.1.0",
66
66
  "@datadog/pprof": "^0.3.0",
67
67
  "@datadog/sketches-js": "^1.0.4",
@@ -1,21 +1,30 @@
1
1
  'use strict'
2
2
 
3
+ require('./src/amqplib')
4
+ require('./src/amqp10')
3
5
  require('./src/bluebird')
4
6
  require('./src/bunyan')
7
+ require('./src/cassandra-driver')
5
8
  require('./src/couchbase')
9
+ require('./src/cucumber')
6
10
  require('./src/dns')
7
11
  require('./src/elasticsearch')
8
12
  require('./src/generic-pool')
9
13
  require('./src/ioredis')
10
14
  require('./src/memcached')
15
+ require('./src/mongodb-core')
11
16
  require('./src/mongoose')
12
17
  require('./src/mysql')
13
18
  require('./src/mysql2')
19
+ require('./src/mocha')
14
20
  require('./src/pino')
21
+ require('./src/pg')
15
22
  require('./src/promise')
16
23
  require('./src/promise-js')
17
24
  require('./src/q')
18
25
  require('./src/redis')
26
+ require('./src/rhea')
19
27
  require('./src/sharedb')
28
+ require('./src/tedious')
20
29
  require('./src/when')
21
30
  require('./src/winston')
@@ -0,0 +1,70 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ channel,
5
+ addHook,
6
+ AsyncResource
7
+ } = require('./helpers/instrument')
8
+ const shimmer = require('../../datadog-shimmer')
9
+
10
+ addHook({ name: 'amqp10', file: 'lib/sender_link.js', versions: ['>=3'] }, SenderLink => {
11
+ const startCh = channel('apm:amqp10:send:start')
12
+ const asyncEndCh = channel('apm:amqp10:send:async-end')
13
+ const endCh = channel('apm:amqp10:send:end')
14
+ const errorCh = channel('apm:amqp10:send:error')
15
+ shimmer.wrap(SenderLink.prototype, 'send', send => function (msg, options) {
16
+ if (!startCh.hasSubscribers) {
17
+ return send.apply(this, arguments)
18
+ }
19
+ startCh.publish({ link: this })
20
+ try {
21
+ const promise = send.apply(this, arguments)
22
+
23
+ if (!promise) {
24
+ finish(asyncEndCh, errorCh)
25
+ return promise
26
+ }
27
+
28
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
29
+
30
+ promise.then(asyncResource.bind(() => finish(asyncEndCh, errorCh)),
31
+ asyncResource.bind(e => finish(asyncEndCh, errorCh, e)))
32
+
33
+ return promise
34
+ } catch (err) {
35
+ finish(asyncEndCh, errorCh, err)
36
+ throw err
37
+ } finally {
38
+ endCh.publish(undefined)
39
+ }
40
+ })
41
+ return SenderLink
42
+ })
43
+
44
+ addHook({ name: 'amqp10', file: 'lib/receiver_link.js', versions: ['>=3'] }, ReceiverLink => {
45
+ const startCh = channel('apm:amqp10:receive:start')
46
+ const endCh = channel('apm:amqp10:receive:end')
47
+ const errorCh = channel('apm:amqp10:receive:error')
48
+ shimmer.wrap(ReceiverLink.prototype, '_messageReceived', messageReceived => function (transferFrame) {
49
+ if (!transferFrame || transferFrame.aborted || transferFrame.more) {
50
+ return messageReceived.apply(this, arguments)
51
+ }
52
+ startCh.publish({ link: this })
53
+ try {
54
+ return messageReceived.apply(this, arguments)
55
+ } catch (err) {
56
+ errorCh.publish(err)
57
+ throw err
58
+ } finally {
59
+ endCh.publish(undefined)
60
+ }
61
+ })
62
+ return ReceiverLink
63
+ })
64
+
65
+ function finish (asyncEndCh, errorCh, error) {
66
+ if (error) {
67
+ errorCh.publish(error)
68
+ }
69
+ asyncEndCh.publish(undefined)
70
+ }
@@ -0,0 +1,58 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ channel,
5
+ addHook
6
+ } = require('./helpers/instrument')
7
+ const kebabCase = require('lodash.kebabcase')
8
+ const shimmer = require('../../datadog-shimmer')
9
+
10
+ const startCh = channel('apm:amqplib:command:start')
11
+ const endCh = channel('apm:amqplib:command:end')
12
+ const errorCh = channel('apm:amqplib:command:error')
13
+
14
+ let methods = {}
15
+
16
+ addHook({ name: 'amqplib', file: 'lib/defs.js', versions: ['>=0.5'] }, defs => {
17
+ methods = Object.keys(defs)
18
+ .filter(key => Number.isInteger(defs[key]))
19
+ .filter(key => isCamelCase(key))
20
+ .reduce((acc, key) => Object.assign(acc, { [defs[key]]: kebabCase(key).replace('-', '.') }), {})
21
+ return defs
22
+ })
23
+
24
+ addHook({ name: 'amqplib', file: 'lib/channel.js', versions: ['>=0.5'] }, channel => {
25
+ shimmer.wrap(channel.Channel.prototype, 'sendImmediately', sendImmediately => function (method, fields) {
26
+ return instrument(sendImmediately, this, arguments, methods[method], fields)
27
+ })
28
+
29
+ shimmer.wrap(channel.Channel.prototype, 'sendMessage', sendMessage => function (fields) {
30
+ return instrument(sendMessage, this, arguments, 'basic.publish', fields)
31
+ })
32
+
33
+ shimmer.wrap(channel.BaseChannel.prototype, 'dispatchMessage', dispatchMessage => function (fields, message) {
34
+ return instrument(dispatchMessage, this, arguments, 'basic.deliver', fields, message)
35
+ })
36
+ return channel
37
+ })
38
+
39
+ function instrument (send, channel, args, method, fields, message) {
40
+ if (!startCh.hasSubscribers) {
41
+ return send.apply(this, arguments)
42
+ }
43
+ startCh.publish({ channel, method, fields, message })
44
+
45
+ try {
46
+ return send.apply(channel, args)
47
+ } catch (err) {
48
+ errorCh.publish(err)
49
+
50
+ throw err
51
+ } finally {
52
+ endCh.publish(undefined)
53
+ }
54
+ }
55
+
56
+ function isCamelCase (str) {
57
+ return /([A-Z][a-z0-9]+)+/.test(str)
58
+ }
@@ -0,0 +1,191 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ channel,
5
+ addHook,
6
+ AsyncResource
7
+ } = require('./helpers/instrument')
8
+ const shimmer = require('../../datadog-shimmer')
9
+
10
+ const startCh = channel('apm:cassandra:query:start')
11
+ const asyncEndCh = channel('apm:cassandra:query:async-end')
12
+ const endCh = channel('apm:cassandra:query:end')
13
+ const errorCh = channel('apm:cassandra:query:error')
14
+ const addConnectionCh = channel(`apm:cassandra:query:addConnection`)
15
+
16
+ addHook({ name: 'cassandra-driver', versions: ['>=3.0.0'] }, cassandra => {
17
+ shimmer.wrap(cassandra.Client.prototype, 'batch', batch => function (queries, options, callback) {
18
+ if (!startCh.hasSubscribers) {
19
+ return batch.apply(this, arguments)
20
+ }
21
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
22
+ startCh.publish({ keyspace: this.keyspace, query: queries })
23
+
24
+ const lastIndex = arguments.length - 1
25
+ let cb = arguments[lastIndex]
26
+
27
+ if (typeof cb === 'function') {
28
+ cb = asyncResource.bind(cb)
29
+ arguments[lastIndex] = wrapCallback(asyncEndCh, errorCh, cb)
30
+ }
31
+
32
+ try {
33
+ const res = batch.apply(this, arguments)
34
+ if (typeof res === 'function' || !res) {
35
+ return wrapCallback(asyncEndCh, errorCh, res)
36
+ } else {
37
+ const promiseAsyncResource = new AsyncResource('bound-anonymous-fn')
38
+ return res.then(
39
+ promiseAsyncResource.bind(() => finish(asyncEndCh, errorCh)),
40
+ promiseAsyncResource.bind(err => finish(asyncEndCh, errorCh, err))
41
+ )
42
+ }
43
+ } catch (e) {
44
+ finish(asyncEndCh, errorCh, e)
45
+ throw e
46
+ } finally {
47
+ endCh.publish(undefined)
48
+ }
49
+ })
50
+ return cassandra
51
+ })
52
+
53
+ addHook({ name: 'cassandra-driver', versions: ['>=4.4'] }, cassandra => {
54
+ shimmer.wrap(cassandra.Client.prototype, '_execute', _execute => function (query, params, execOptions, callback) {
55
+ if (!startCh.hasSubscribers) {
56
+ return _execute.apply(this, arguments)
57
+ }
58
+ startCh.publish({ keyspace: this.keyspace, query })
59
+ const promise = _execute.apply(this, arguments)
60
+
61
+ const promiseAsyncResource = new AsyncResource('bound-anonymous-fn')
62
+
63
+ promise.then(
64
+ promiseAsyncResource.bind(() => finish(asyncEndCh, errorCh)),
65
+ promiseAsyncResource.bind(err => finish(asyncEndCh, errorCh, err))
66
+ )
67
+ endCh.publish(undefined)
68
+ return promise
69
+ })
70
+ return cassandra
71
+ })
72
+
73
+ addHook({ name: 'cassandra-driver', versions: ['3 - 4.3'] }, cassandra => {
74
+ shimmer.wrap(cassandra.Client.prototype, '_innerExecute', _innerExecute =>
75
+ function (query, params, execOptions, callback) {
76
+ if (!startCh.hasSubscribers) {
77
+ return _innerExecute.apply(this, arguments)
78
+ }
79
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
80
+ const isValid = (args) => {
81
+ return args.length === 4 || typeof args[3] === 'function'
82
+ }
83
+
84
+ if (!isValid(arguments)) {
85
+ return _innerExecute.apply(this, arguments)
86
+ }
87
+
88
+ startCh.publish({ keyspace: this.keyspace, query })
89
+
90
+ const lastIndex = arguments.length - 1
91
+ let cb = arguments[lastIndex]
92
+
93
+ if (typeof cb === 'function') {
94
+ cb = asyncResource.bind(cb)
95
+ arguments[lastIndex] = wrapCallback(asyncEndCh, errorCh, cb)
96
+ }
97
+
98
+ try {
99
+ return _innerExecute.apply(this, arguments)
100
+ } catch (e) {
101
+ finish(asyncEndCh, errorCh, e)
102
+ throw e
103
+ } finally {
104
+ endCh.publish(undefined)
105
+ }
106
+ }
107
+ )
108
+ return cassandra
109
+ })
110
+
111
+ addHook({ name: 'cassandra-driver', versions: ['>=3.3'], file: 'lib/request-execution.js' }, RequestExecution => {
112
+ shimmer.wrap(RequestExecution.prototype, '_sendOnConnection', _sendOnConnection => function () {
113
+ if (!startCh.hasSubscribers) {
114
+ return _sendOnConnection.apply(this, arguments)
115
+ }
116
+ addConnectionCh.publish({ address: this._connection.address, port: this._connection.port })
117
+ return _sendOnConnection.apply(this, arguments)
118
+ })
119
+ return RequestExecution
120
+ })
121
+
122
+ addHook({ name: 'cassandra-driver', versions: ['3.3 - 4.3'], file: 'lib/request-execution.js' }, RequestExecution => {
123
+ shimmer.wrap(RequestExecution.prototype, 'start', start => function (getHostCallback) {
124
+ if (!startCh.hasSubscribers) {
125
+ return getHostCallback.apply(this, arguments)
126
+ }
127
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
128
+ const execution = this
129
+
130
+ if (!isRequestValid(this, arguments, 1)) {
131
+ return start.apply(this, arguments)
132
+ }
133
+
134
+ getHostCallback = asyncResource.bind(getHostCallback)
135
+
136
+ arguments[0] = AsyncResource.bind(function () {
137
+ addConnectionCh.publish({ address: execution._connection.address, port: execution._connection.port })
138
+ return getHostCallback.apply(this, arguments)
139
+ })
140
+
141
+ return start.apply(this, arguments)
142
+ })
143
+ return RequestExecution
144
+ })
145
+
146
+ addHook({ name: 'cassandra-driver', versions: ['3 - 3.2'], file: 'lib/request-handler.js' }, RequestHandler => {
147
+ shimmer.wrap(RequestHandler.prototype, 'send', send => function (request, options, callback) {
148
+ if (!startCh.hasSubscribers) {
149
+ return send.apply(this, arguments)
150
+ }
151
+ const handler = this
152
+
153
+ if (!isRequestValid(this, arguments, 3)) {
154
+ return send.apply(this, arguments)
155
+ }
156
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
157
+
158
+ callback = asyncResource.bind(callback)
159
+
160
+ arguments[2] = AsyncResource.bind(function () {
161
+ addConnectionCh.publish({ address: handler.connection.address, port: handler.connection.port })
162
+ return callback.apply(this, arguments)
163
+ })
164
+
165
+ return send.apply(this, arguments)
166
+ })
167
+ return RequestHandler
168
+ })
169
+
170
+ function finish (asyncEndCh, errorCh, error) {
171
+ if (error) {
172
+ errorCh.publish(error)
173
+ }
174
+ asyncEndCh.publish(undefined)
175
+ }
176
+
177
+ function wrapCallback (asyncEndCh, errorCh, callback) {
178
+ return AsyncResource.bind(function (err) {
179
+ finish(asyncEndCh, errorCh, err)
180
+ if (callback) {
181
+ return callback.apply(this, arguments)
182
+ }
183
+ })
184
+ }
185
+
186
+ function isRequestValid (exec, args, length) {
187
+ if (!exec) return false
188
+ if (args.length !== length || typeof args[length - 1] !== 'function') return false
189
+
190
+ return true
191
+ }
@@ -0,0 +1,118 @@
1
+ 'use strict'
2
+
3
+ const { addHook, channel } = require('./helpers/instrument')
4
+ const shimmer = require('../../datadog-shimmer')
5
+
6
+ const runStartCh = channel('ci:cucumber:run:start')
7
+ const runEndCh = channel('ci:cucumber:run:end')
8
+ const runAsyncEndCh = channel('ci:cucumber:run:async-end')
9
+ const runStepStartCh = channel('ci:cucumber:run-step:start')
10
+ const runStepEndCh = channel('ci:cucumber:run-step:end')
11
+ const errorCh = channel('ci:cucumber:error')
12
+
13
+ function getStatusFromResult (result) {
14
+ if (result.status === 1) {
15
+ return { status: 'pass' }
16
+ }
17
+ if (result.status === 2) {
18
+ return { status: 'skip' }
19
+ }
20
+ if (result.status === 4) {
21
+ return { status: 'skip', skipReason: 'not implemented' }
22
+ }
23
+ return { status: 'fail', errorMessage: result.message }
24
+ }
25
+
26
+ function getStatusFromResultLatest (result) {
27
+ if (result.status === 'PASSED') {
28
+ return { status: 'pass' }
29
+ }
30
+ if (result.status === 'SKIPPED' || result.status === 'PENDING') {
31
+ return { status: 'skip' }
32
+ }
33
+ if (result.status === 'UNDEFINED') {
34
+ return { status: 'skip', skipReason: 'not implemented' }
35
+ }
36
+ return { status: 'fail', errorMessage: result.message }
37
+ }
38
+
39
+ function wrapRun (pl, isLatestVersion) {
40
+ shimmer.wrap(pl.prototype, 'run', run => function () {
41
+ if (!runStartCh.hasSubscribers) {
42
+ return run.apply(this, arguments)
43
+ }
44
+
45
+ runStartCh.publish({ pickleName: this.pickle.name, pickleUri: this.pickle.uri })
46
+ try {
47
+ const promise = run.apply(this, arguments)
48
+ promise.finally(() => {
49
+ const result = this.getWorstStepResult()
50
+ const { status, skipReason, errorMessage } = isLatestVersion
51
+ ? getStatusFromResultLatest(result) : getStatusFromResult(result)
52
+
53
+ runAsyncEndCh.publish({ status, skipReason, errorMessage })
54
+ })
55
+ return promise
56
+ } catch (err) {
57
+ errorCh.publish(err)
58
+ throw err
59
+ } finally {
60
+ runEndCh.publish(undefined)
61
+ }
62
+ })
63
+ shimmer.wrap(pl.prototype, 'runStep', runStep => function () {
64
+ if (!runStepStartCh.hasSubscribers) {
65
+ return runStep.apply(this, arguments)
66
+ }
67
+ const testStep = arguments[0]
68
+ let resource
69
+
70
+ if (isLatestVersion) {
71
+ resource = testStep.text
72
+ } else {
73
+ resource = testStep.isHook ? 'hook' : testStep.pickleStep.text
74
+ }
75
+
76
+ runStepStartCh.publish({ resource })
77
+ try {
78
+ const promise = runStep.apply(this, arguments)
79
+
80
+ promise.then((result) => {
81
+ const { status, skipReason, errorMessage } = isLatestVersion
82
+ ? getStatusFromResultLatest(result) : getStatusFromResult(result)
83
+
84
+ runAsyncEndCh.publish({ isStep: true, status, skipReason, errorMessage })
85
+ })
86
+ return promise
87
+ } catch (err) {
88
+ errorCh.publish(err)
89
+ throw err
90
+ } finally {
91
+ runStepEndCh.publish(undefined)
92
+ }
93
+ })
94
+ }
95
+
96
+ addHook({
97
+ name: '@cucumber/cucumber',
98
+ versions: ['7.0.0 - 7.2.1'],
99
+ file: 'lib/runtime/pickle_runner.js'
100
+ }, (PickleRunner) => {
101
+ const pl = PickleRunner.default
102
+
103
+ wrapRun(pl, false)
104
+
105
+ return PickleRunner
106
+ })
107
+
108
+ addHook({
109
+ name: '@cucumber/cucumber',
110
+ versions: ['>=7.3.0'],
111
+ file: 'lib/runtime/test_case_runner.js'
112
+ }, (TestCaseRunner) => {
113
+ const pl = TestCaseRunner.default
114
+
115
+ wrapRun(pl, true)
116
+
117
+ return TestCaseRunner
118
+ })
@@ -35,18 +35,20 @@ function wrapRequest (request) {
35
35
 
36
36
  if (!params) return request.apply(this, arguments)
37
37
 
38
- const asyncResource = new AsyncResource('bound-anonymous-fn')
38
+ const parentResource = new AsyncResource('bound-anonymous-fn')
39
39
 
40
40
  startCh.publish({ params })
41
41
 
42
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
43
+
42
44
  try {
43
45
  const lastIndex = arguments.length - 1
44
46
  cb = arguments[lastIndex]
45
47
 
46
48
  if (typeof cb === 'function') {
47
- cb = asyncResource.bind(cb)
49
+ cb = parentResource.bind(cb)
48
50
 
49
- arguments[lastIndex] = AsyncResource.bind(function (error) {
51
+ arguments[lastIndex] = asyncResource.bind(function (error) {
50
52
  finish(params, error)
51
53
  return cb.apply(null, arguments)
52
54
  })
@@ -54,7 +56,10 @@ function wrapRequest (request) {
54
56
  } else {
55
57
  const promise = request.apply(this, arguments)
56
58
  if (promise && typeof promise.then === 'function') {
57
- promise.then(() => finish(params), e => finish(params, e))
59
+ const onResolve = asyncResource.bind(() => finish(params))
60
+ const onReject = asyncResource.bind(e => finish(params, e))
61
+
62
+ promise.then(onResolve, onReject)
58
63
  } else {
59
64
  finish(params)
60
65
  }
@@ -20,11 +20,11 @@ exports.channel = function channel (name) {
20
20
  }
21
21
 
22
22
  exports.addHook = function addHook ({ name, versions, file }, hook) {
23
- file = filename(name, file)
23
+ const fullFilename = filename(name, file)
24
24
  const loaderHook = (moduleExports, moduleName, moduleBaseDir) => {
25
25
  moduleName = moduleName.replace(pathSepExpr, '/')
26
26
  const moduleVersion = getVersion(moduleBaseDir)
27
- if (moduleName !== file || !matchVersion(moduleVersion, versions)) {
27
+ if (moduleName !== fullFilename || !matchVersion(moduleVersion, versions)) {
28
28
  return moduleExports
29
29
  }
30
30
  return hook(moduleExports)
@@ -65,7 +65,7 @@ function cjsPostLoad (instrumentation, hook) {
65
65
  if (!id.includes(`/node_modules/${instrumentation.name}/`)) continue
66
66
 
67
67
  if (instrumentation.file) {
68
- if (!id.endsWith(`/node_modules/${filename(instrumentation)}`)) continue
68
+ if (!id.endsWith(`/node_modules/${filename(instrumentation.name, instrumentation.file)}`)) continue
69
69
 
70
70
  const basedir = getBasedir(ids[i])
71
71
 
@@ -2,7 +2,8 @@
2
2
 
3
3
  const {
4
4
  channel,
5
- addHook
5
+ addHook,
6
+ AsyncResource
6
7
  } = require('./helpers/instrument')
7
8
  const shimmer = require('../../datadog-shimmer')
8
9
 
@@ -21,12 +22,14 @@ addHook({ name: 'ioredis', versions: ['>=2'] }, Redis => {
21
22
  const connectionName = options.connectionName
22
23
  const db = options.db
23
24
  const connectionOptions = { host: options.host, port: options.port }
25
+
24
26
  startCh.publish({ db, command: command.name, args: command.args, connectionOptions, connectionName })
25
27
 
26
- command.promise.then(
27
- () => finish(asyncEndCh, errorCh),
28
- err => finish(asyncEndCh, errorCh, err)
29
- )
28
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
29
+ const onResolve = asyncResource.bind(() => finish(asyncEndCh, errorCh))
30
+ const onReject = asyncResource.bind(err => finish(asyncEndCh, errorCh, err))
31
+
32
+ command.promise.then(onResolve, onReject)
30
33
 
31
34
  try {
32
35
  return sendCommand.apply(this, arguments)