dd-trace 5.51.0 → 5.53.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 (98) hide show
  1. package/LICENSE-3rdparty.csv +0 -6
  2. package/README.md +5 -0
  3. package/index.d.ts +88 -6
  4. package/package.json +3 -9
  5. package/packages/datadog-instrumentations/src/amqplib.js +8 -5
  6. package/packages/datadog-instrumentations/src/child_process.js +2 -1
  7. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +406 -0
  8. package/packages/datadog-instrumentations/src/couchbase.js +2 -1
  9. package/packages/datadog-instrumentations/src/cucumber.js +43 -45
  10. package/packages/datadog-instrumentations/src/dns.js +16 -14
  11. package/packages/datadog-instrumentations/src/express.js +2 -6
  12. package/packages/datadog-instrumentations/src/fs.js +43 -51
  13. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  14. package/packages/datadog-instrumentations/src/helpers/register.js +17 -12
  15. package/packages/datadog-instrumentations/src/http/client.js +2 -1
  16. package/packages/datadog-instrumentations/src/iovalkey.js +51 -0
  17. package/packages/datadog-instrumentations/src/jest.js +53 -40
  18. package/packages/datadog-instrumentations/src/kafkajs.js +21 -8
  19. package/packages/datadog-instrumentations/src/mocha/main.js +33 -46
  20. package/packages/datadog-instrumentations/src/mocha/utils.js +76 -74
  21. package/packages/datadog-instrumentations/src/mysql2.js +3 -1
  22. package/packages/datadog-instrumentations/src/net.js +27 -29
  23. package/packages/datadog-instrumentations/src/next.js +6 -14
  24. package/packages/datadog-instrumentations/src/pg.js +15 -7
  25. package/packages/datadog-instrumentations/src/playwright.js +64 -67
  26. package/packages/datadog-instrumentations/src/url.js +9 -17
  27. package/packages/datadog-instrumentations/src/vitest.js +66 -72
  28. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/batch-consumer.js +11 -0
  29. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/consumer.js +11 -0
  30. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +19 -0
  31. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/producer.js +11 -0
  32. package/packages/datadog-plugin-cucumber/src/index.js +32 -18
  33. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +3 -0
  34. package/packages/datadog-plugin-dns/src/lookup.js +10 -5
  35. package/packages/datadog-plugin-dns/src/lookup_service.js +6 -2
  36. package/packages/datadog-plugin-dns/src/resolve.js +5 -2
  37. package/packages/datadog-plugin-dns/src/reverse.js +6 -2
  38. package/packages/datadog-plugin-fs/src/index.js +9 -2
  39. package/packages/datadog-plugin-iovalkey/src/index.js +18 -0
  40. package/packages/datadog-plugin-jest/src/index.js +17 -8
  41. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -1
  42. package/packages/datadog-plugin-kafkajs/src/consumer.js +12 -21
  43. package/packages/datadog-plugin-kafkajs/src/producer.js +12 -5
  44. package/packages/datadog-plugin-kafkajs/src/utils.js +27 -0
  45. package/packages/datadog-plugin-langchain/src/index.js +0 -1
  46. package/packages/datadog-plugin-mocha/src/index.js +58 -35
  47. package/packages/datadog-plugin-net/src/ipc.js +6 -4
  48. package/packages/datadog-plugin-net/src/tcp.js +15 -9
  49. package/packages/datadog-plugin-pg/src/index.js +5 -1
  50. package/packages/datadog-plugin-playwright/src/index.js +29 -20
  51. package/packages/datadog-plugin-redis/src/index.js +8 -3
  52. package/packages/datadog-plugin-vitest/src/index.js +67 -44
  53. package/packages/datadog-shimmer/src/shimmer.js +164 -33
  54. package/packages/dd-trace/src/appsec/api_security_sampler.js +20 -12
  55. package/packages/dd-trace/src/appsec/graphql.js +2 -2
  56. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +14 -9
  57. package/packages/dd-trace/src/appsec/index.js +15 -12
  58. package/packages/dd-trace/src/appsec/rasp/index.js +4 -2
  59. package/packages/dd-trace/src/appsec/rasp/utils.js +11 -6
  60. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
  61. package/packages/dd-trace/src/appsec/telemetry/index.js +1 -2
  62. package/packages/dd-trace/src/appsec/telemetry/rasp.js +0 -9
  63. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +6 -6
  64. package/packages/dd-trace/src/baggage.js +36 -0
  65. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
  66. package/packages/dd-trace/src/config.js +14 -2
  67. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +61 -7
  68. package/packages/dd-trace/src/debugger/devtools_client/index.js +10 -26
  69. package/packages/dd-trace/src/debugger/devtools_client/send.js +8 -7
  70. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +15 -7
  71. package/packages/dd-trace/src/debugger/devtools_client/state.js +22 -2
  72. package/packages/dd-trace/src/dogstatsd.js +2 -0
  73. package/packages/dd-trace/src/exporters/common/docker.js +13 -31
  74. package/packages/dd-trace/src/guardrails/telemetry.js +2 -5
  75. package/packages/dd-trace/src/llmobs/tagger.js +3 -3
  76. package/packages/dd-trace/src/llmobs/writers/base.js +33 -12
  77. package/packages/dd-trace/src/noop/proxy.js +5 -0
  78. package/packages/dd-trace/src/opentelemetry/context_manager.js +2 -0
  79. package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -9
  80. package/packages/dd-trace/src/plugin_manager.js +2 -0
  81. package/packages/dd-trace/src/plugins/index.js +4 -0
  82. package/packages/dd-trace/src/plugins/log_plugin.js +9 -20
  83. package/packages/dd-trace/src/plugins/outbound.js +11 -3
  84. package/packages/dd-trace/src/plugins/tracing.js +8 -4
  85. package/packages/dd-trace/src/plugins/util/test.js +1 -1
  86. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -1
  87. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +1 -1
  88. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +1 -1
  89. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +2 -2
  90. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +1 -1
  91. package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +15 -14
  92. package/packages/dd-trace/src/proxy.js +12 -4
  93. package/packages/dd-trace/src/serverless.js +0 -48
  94. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +8 -0
  95. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +8 -0
  96. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  97. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  98. package/packages/dd-trace/src/standalone/product.js +3 -5
@@ -149,11 +149,9 @@ addHook({ name: 'express', versions: ['>=4.3.0 <5.0.0'] }, express => {
149
149
  const queryReadCh = channel('datadog:express:query:finish')
150
150
 
151
151
  addHook({ name: 'express', file: ['lib/request.js'], versions: ['>=5.0.0'] }, request => {
152
- const requestDescriptor = Object.getOwnPropertyDescriptor(request, 'query')
153
-
154
- shimmer.wrap(requestDescriptor, 'get', function (originalGet) {
152
+ shimmer.wrap(request, 'query', function (originalGet) {
155
153
  return function wrappedGet () {
156
- const query = originalGet.apply(this, arguments)
154
+ const query = originalGet.call(this)
157
155
 
158
156
  if (queryReadCh.hasSubscribers && query) {
159
157
  queryReadCh.publish({ query })
@@ -163,7 +161,5 @@ addHook({ name: 'express', file: ['lib/request.js'], versions: ['>=5.0.0'] }, re
163
161
  }
164
162
  })
165
163
 
166
- Object.defineProperty(request, 'query', requestDescriptor)
167
-
168
164
  return request
169
165
  })
@@ -1,10 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const {
4
- channel,
5
- addHook,
6
- AsyncResource
7
- } = require('./helpers/instrument')
3
+ const { errorMonitor } = require('events')
4
+ const { channel, addHook } = require('./helpers/instrument')
8
5
  const shimmer = require('../../datadog-shimmer')
9
6
 
10
7
  const startChannel = channel('apm:fs:operation:start')
@@ -191,35 +188,34 @@ function wrapCreateStream (original) {
191
188
  return function (path, options) {
192
189
  if (!startChannel.hasSubscribers) return original.apply(this, arguments)
193
190
 
194
- const innerResource = new AsyncResource('bound-anonymous-fn')
195
- const message = getMessage(name, ['path', 'options'], arguments)
196
-
197
- return innerResource.runInAsyncScope(() => {
198
- startChannel.publish(message)
191
+ const ctx = getMessage(name, ['path', 'options'], arguments)
199
192
 
193
+ return startChannel.runStores(ctx, () => {
200
194
  try {
201
195
  const stream = original.apply(this, arguments)
202
- const onError = innerResource.bind(error => {
203
- errorChannel.publish(error)
196
+ const onError = error => {
197
+ ctx.error = error
198
+ errorChannel.publish(ctx)
204
199
  onFinish()
205
- })
206
- const onFinish = innerResource.bind(() => {
207
- finishChannel.publish()
208
- stream.off('close', onFinish)
209
- stream.off('end', onFinish)
210
- stream.off('finish', onFinish)
211
- stream.off('error', onError)
212
- })
200
+ }
201
+ const onFinish = () => {
202
+ finishChannel.runStores(ctx, () => {})
203
+ stream.removeListener('close', onFinish)
204
+ stream.removeListener('end', onFinish)
205
+ stream.removeListener('finish', onFinish)
206
+ stream.removeListener(errorMonitor, onError)
207
+ }
213
208
 
214
209
  stream.once('close', onFinish)
215
210
  stream.once('end', onFinish)
216
211
  stream.once('finish', onFinish)
217
- stream.once('error', onError)
212
+ stream.once(errorMonitor, onError)
218
213
 
219
214
  return stream
220
215
  } catch (error) {
221
- errorChannel.publish(error)
222
- finishChannel.publish()
216
+ ctx.error = error
217
+ errorChannel.publish(ctx)
218
+ finishChannel.runStores(ctx, () => {})
223
219
  }
224
220
  })
225
221
  }
@@ -239,17 +235,16 @@ function createWatchWrapFunction (override = '') {
239
235
  const operation = name
240
236
  return function () {
241
237
  if (!startChannel.hasSubscribers) return original.apply(this, arguments)
242
- const message = getMessage(method, watchMethods[operation], arguments, this)
243
- const innerResource = new AsyncResource('bound-anonymous-fn')
244
- return innerResource.runInAsyncScope(() => {
245
- startChannel.publish(message)
238
+ const ctx = getMessage(method, watchMethods[operation], arguments, this)
239
+ return startChannel.runStores(ctx, () => {
246
240
  try {
247
241
  const result = original.apply(this, arguments)
248
- finishChannel.publish()
242
+ finishChannel.runStores(ctx, () => {})
249
243
  return result
250
244
  } catch (error) {
251
- errorChannel.publish(error)
252
- finishChannel.publish()
245
+ ctx.error = error
246
+ errorChannel.publish(ctx)
247
+ finishChannel.runStores(ctx, () => {})
253
248
  throw error
254
249
  }
255
250
  })
@@ -268,30 +263,25 @@ function createWrapFunction (prefix = '', override = '') {
268
263
 
269
264
  const lastIndex = arguments.length - 1
270
265
  const cb = typeof arguments[lastIndex] === 'function' && arguments[lastIndex]
271
- const innerResource = new AsyncResource('bound-anonymous-fn')
272
266
  const params = getMethodParamsRelationByPrefix(prefix)[operation]
273
267
  const abortController = new AbortController()
274
- const message = { ...getMessage(method, params, arguments, this), abortController }
268
+ const ctx = { ...getMessage(method, params, arguments, this), abortController }
275
269
 
276
- const finish = innerResource.bind(function (error) {
270
+ const finish = function (error, cb = () => {}) {
277
271
  if (error !== null && typeof error === 'object') { // fs.exists receives a boolean
278
- errorChannel.publish(error)
272
+ ctx.error = error
273
+ errorChannel.publish(ctx)
279
274
  }
280
- finishChannel.publish()
281
- })
275
+ return finishChannel.runStores(ctx, cb)
276
+ }
282
277
 
283
278
  if (cb) {
284
- const outerResource = new AsyncResource('bound-anonymous-fn')
285
-
286
- arguments[lastIndex] = shimmer.wrapFunction(cb, cb => innerResource.bind(function (e) {
287
- finish(e)
288
- return outerResource.runInAsyncScope(() => cb.apply(this, arguments))
289
- }))
279
+ arguments[lastIndex] = shimmer.wrapFunction(cb, cb => function (e) {
280
+ return finish(e, () => cb.apply(this, arguments))
281
+ })
290
282
  }
291
283
 
292
- return innerResource.runInAsyncScope(() => {
293
- startChannel.publish(message)
294
-
284
+ return startChannel.runStores(ctx, () => {
295
285
  if (abortController.signal.aborted) {
296
286
  const error = abortController.signal.reason || new Error('Aborted')
297
287
 
@@ -318,23 +308,25 @@ function createWrapFunction (prefix = '', override = '') {
318
308
  if (isFirstMethodReturningFileHandle(original)) {
319
309
  wrapFileHandle(value)
320
310
  }
321
- finishChannel.publish()
311
+ finishChannel.runStores(ctx, () => {})
322
312
  return value
323
313
  },
324
314
  error => {
325
- errorChannel.publish(error)
326
- finishChannel.publish()
315
+ ctx.error = error
316
+ errorChannel.publish(ctx)
317
+ finishChannel.runStores(ctx, () => {})
327
318
  throw error
328
319
  }
329
320
  )
330
321
  }
331
322
 
332
- finishChannel.publish()
323
+ finishChannel.runStores(ctx, () => {})
333
324
 
334
325
  return result
335
326
  } catch (error) {
336
- errorChannel.publish(error)
337
- finishChannel.publish()
327
+ ctx.error = error
328
+ errorChannel.publish(ctx)
329
+ finishChannel.runStores(ctx, () => {})
338
330
  throw error
339
331
  }
340
332
  })
@@ -61,6 +61,7 @@ module.exports = {
61
61
  http2: () => require('../http2'),
62
62
  https: () => require('../http'),
63
63
  ioredis: () => require('../ioredis'),
64
+ iovalkey: () => require('../iovalkey'),
64
65
  'jest-circus': () => require('../jest'),
65
66
  'jest-config': () => require('../jest'),
66
67
  'jest-environment-node': () => require('../jest'),
@@ -70,6 +71,7 @@ module.exports = {
70
71
  knex: () => require('../knex'),
71
72
  koa: () => require('../koa'),
72
73
  'koa-router': () => require('../koa'),
74
+ '@confluentinc/kafka-javascript': () => require('../confluentinc-kafka-javascript'),
73
75
  kafkajs: () => require('../kafkajs'),
74
76
  langchain: () => require('../langchain'),
75
77
  ldapjs: () => require('../ldapjs'),
@@ -43,7 +43,7 @@ if (!disabledInstrumentations.has('process')) {
43
43
  require('../process')
44
44
  }
45
45
 
46
- const HOOK_SYMBOL = Symbol('hookExportsMap')
46
+ const HOOK_SYMBOL = Symbol('hookExportsSet')
47
47
 
48
48
  if (DD_TRACE_DEBUG && DD_TRACE_DEBUG.toLowerCase() !== 'false') {
49
49
  checkRequireCache.checkForRequiredModules()
@@ -89,12 +89,13 @@ for (const packageName of names) {
89
89
  fullFilePattern = filename(name, fullFilePattern)
90
90
  }
91
91
 
92
- // Create a WeakMap associated with the hook function so that patches on the same moduleExport only happens once
92
+ // Create a WeakSet associated with the hook function so that patches on the same moduleExport only happens once
93
93
  // for example by instrumenting both dns and node:dns double the spans would be created
94
- // since they both patch the same moduleExport, this WeakMap is used to mitigate that
95
- if (!hook[HOOK_SYMBOL]) {
96
- hook[HOOK_SYMBOL] = new WeakMap()
97
- }
94
+ // since they both patch the same moduleExport, this WeakSet is used to mitigate that
95
+ // TODO(BridgeAR): Instead of using a WeakSet here, why not just use aliases for the hook in register?
96
+ // That way it would also not be duplicated. The actual name being used has to be identified else wise.
97
+ // Maybe it is also not important to know what name was actually used?
98
+ hook[HOOK_SYMBOL] ??= new WeakSet()
98
99
  let matchesFile = false
99
100
 
100
101
  matchesFile = moduleName === fullFilename
@@ -114,7 +115,7 @@ for (const packageName of names) {
114
115
  log.error('Error getting version for "%s": %s', name, e.message, e)
115
116
  continue
116
117
  }
117
- if (typeof namesAndSuccesses[`${name}@${version}`] === 'undefined') {
118
+ if (namesAndSuccesses[`${name}@${version}`] === undefined) {
118
119
  // TODO If `file` is present, we might elsewhere instrument the result of the module
119
120
  // for a version range that actually matches, so we can't assume that we're _not_
120
121
  // going to instrument that. However, the way the data model around instrumentation
@@ -140,12 +141,16 @@ for (const packageName of names) {
140
141
  loadChannel.publish({ name, version, file })
141
142
  // Send the name and version of the module back to the callback because now addHook
142
143
  // takes in an array of names so by passing the name the callback will know which module name is being used
143
- moduleExports = hook(moduleExports, version, name)
144
- // Set the moduleExports in the hooks weakmap
145
- hook[HOOK_SYMBOL].set(moduleExports, name)
144
+ // TODO(BridgeAR): This is only true in case the name is identical
145
+ // in all loads. If they deviate, the deviating name would not be
146
+ // picked up due to the unification. Check what modules actually use the name.
147
+ // TODO(BridgeAR): Only replace moduleExports if the hook returns a new value.
148
+ // This allows to reduce the instrumentation code (no return needed).
149
+ moduleExports = hook(moduleExports, version, name) ?? moduleExports
150
+ // Set the moduleExports in the hooks WeakSet
151
+ hook[HOOK_SYMBOL].add(moduleExports)
146
152
  } catch (e) {
147
- log.info('Error during ddtrace instrumentation of application, aborting.')
148
- log.info(e)
153
+ log.info('Error during ddtrace instrumentation of application, aborting.', e)
149
154
  telemetry('error', [
150
155
  `error_type:${e.constructor.name}`,
151
156
  `integration:${name}`,
@@ -3,6 +3,7 @@
3
3
  /* eslint-disable no-fallthrough */
4
4
 
5
5
  const url = require('url')
6
+ const { errorMonitor } = require('events')
6
7
  const { channel, addHook } = require('../helpers/instrument')
7
8
  const shimmer = require('../../../datadog-shimmer')
8
9
 
@@ -88,7 +89,7 @@ function patch (http, methodName) {
88
89
  const res = arg
89
90
  ctx.res = res
90
91
  res.on('end', finish)
91
- res.on('error', finish)
92
+ res.on(errorMonitor, finish)
92
93
  break
93
94
  }
94
95
  case 'connect':
@@ -0,0 +1,51 @@
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:iovalkey:command:start')
11
+ const finishCh = channel('apm:iovalkey:command:finish')
12
+ const errorCh = channel('apm:iovalkey:command:error')
13
+
14
+ addHook({ name: 'iovalkey', versions: ['>=0.0.1'] }, Valkey => {
15
+ shimmer.wrap(Valkey.prototype, 'sendCommand', sendCommand => function (command, stream) {
16
+ if (!startCh.hasSubscribers) return sendCommand.apply(this, arguments)
17
+
18
+ if (!command?.promise) return sendCommand.apply(this, arguments)
19
+
20
+ const options = this.options || {}
21
+ const connectionName = options.connectionName
22
+ const db = options.db
23
+ const connectionOptions = { host: options.host, port: options.port }
24
+
25
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
26
+ return asyncResource.runInAsyncScope(() => {
27
+ startCh.publish({ db, command: command.name, args: command.args, connectionOptions, connectionName })
28
+
29
+ const onResolve = asyncResource.bind(() => finishCh.publish())
30
+ const onReject = asyncResource.bind(err => finish(finishCh, errorCh, err))
31
+
32
+ command.promise.then(onResolve, onReject)
33
+
34
+ try {
35
+ return sendCommand.apply(this, arguments)
36
+ } catch (err) {
37
+ errorCh.publish(err)
38
+
39
+ throw err
40
+ }
41
+ })
42
+ })
43
+ return Valkey
44
+ })
45
+
46
+ function finish (finishCh, errorCh, error) {
47
+ if (error) {
48
+ errorCh.publish(error)
49
+ }
50
+ finishCh.publish()
51
+ }
@@ -41,6 +41,7 @@ const testStartCh = channel('ci:jest:test:start')
41
41
  const testSkippedCh = channel('ci:jest:test:skip')
42
42
  const testFinishCh = channel('ci:jest:test:finish')
43
43
  const testErrCh = channel('ci:jest:test:err')
44
+ const testFnCh = channel('ci:jest:test:fn')
44
45
 
45
46
  const skippableSuitesCh = channel('ci:jest:test-suite:skippable')
46
47
  const libraryConfigurationCh = channel('ci:jest:library-configuration')
@@ -79,7 +80,7 @@ let testManagementAttemptToFixRetries = 0
79
80
 
80
81
  const sessionAsyncResource = new AsyncResource('bound-anonymous-fn')
81
82
 
82
- const asyncResources = new WeakMap()
83
+ const testContexts = new WeakMap()
83
84
  const originalTestFns = new WeakMap()
84
85
  const originalHookFns = new WeakMap()
85
86
  const retriedTestsToNumAttempts = new Map()
@@ -307,8 +308,6 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
307
308
  const testParameters = getTestParametersString(this.nameToParams, event.test.name)
308
309
  // Async resource for this test is created here
309
310
  // It is used later on by the test_done handler
310
- const asyncResource = new AsyncResource('bound-anonymous-fn')
311
- asyncResources.set(event.test, asyncResource)
312
311
  const testName = getJestTestName(event.test)
313
312
  const originalTestName = removeEfdStringFromTestName(removeAttemptToFixStringFromTestName(testName))
314
313
 
@@ -336,22 +335,24 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
336
335
  }
337
336
 
338
337
  const isJestRetry = event.test?.invocations > 1
339
- asyncResource.runInAsyncScope(() => {
340
- testStartCh.publish({
341
- name: originalTestName,
342
- suite: this.testSuite,
343
- testSourceFile: this.testSourceFile,
344
- displayName: this.displayName,
345
- testParameters,
346
- frameworkVersion: jestVersion,
347
- isNew: isNewTest,
348
- isEfdRetry: numEfdRetry > 0,
349
- isAttemptToFix,
350
- isAttemptToFixRetry: numOfAttemptsToFixRetries > 0,
351
- isJestRetry,
352
- isDisabled,
353
- isQuarantined
354
- })
338
+ const ctx = {
339
+ name: originalTestName,
340
+ suite: this.testSuite,
341
+ testSourceFile: this.testSourceFile,
342
+ displayName: this.displayName,
343
+ testParameters,
344
+ frameworkVersion: jestVersion,
345
+ isNew: isNewTest,
346
+ isEfdRetry: numEfdRetry > 0,
347
+ isAttemptToFix,
348
+ isAttemptToFixRetry: numOfAttemptsToFixRetries > 0,
349
+ isJestRetry,
350
+ isDisabled,
351
+ isQuarantined
352
+ }
353
+ testContexts.set(event.test, ctx)
354
+
355
+ testStartCh.runStores(ctx, () => {
355
356
  for (const hook of event.test.parent.hooks) {
356
357
  let hookFn = hook.fn
357
358
  if (!originalHookFns.has(hook)) {
@@ -359,10 +360,21 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
359
360
  } else {
360
361
  hookFn = originalHookFns.get(hook)
361
362
  }
362
- hook.fn = asyncResource.bind(hookFn)
363
+ const wrapperHook = function () {
364
+ return testFnCh.runStores(ctx, () => hookFn.apply(this, arguments))
365
+ }
366
+ // If we don't do this, the timeout will be not be triggered
367
+ Object.defineProperty(wrapperHook, 'length', { value: hookFn.length })
368
+ hook.fn = wrapperHook
363
369
  }
364
- originalTestFns.set(event.test, event.test.fn)
365
- event.test.fn = asyncResource.bind(event.test.fn)
370
+ const originalFn = event.test.fn
371
+ originalTestFns.set(event.test, originalFn)
372
+ const wrapper = function () {
373
+ return testFnCh.runStores(ctx, () => originalFn.apply(this, arguments))
374
+ }
375
+ // If we don't do this, the timeout will be not be triggered
376
+ Object.defineProperty(wrapper, 'length', { value: originalFn.length })
377
+ event.test.fn = wrapper
366
378
  })
367
379
  }
368
380
 
@@ -408,6 +420,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
408
420
  event.test.fn = originalTestFns.get(event.test)
409
421
 
410
422
  let attemptToFixPassed = false
423
+ let attemptToFixFailed = false
411
424
  let failedAllTests = false
412
425
  let isAttemptToFix = false
413
426
  if (this.isTestManagementTestsEnabled) {
@@ -425,6 +438,9 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
425
438
  // If it is, we'll set the failedAllTests flag to true if all the tests failed
426
439
  // If all tests passed, we'll set the attemptToFixPassed flag to true
427
440
  if (testStatuses.length === testManagementAttemptToFixRetries + 1) {
441
+ if (testStatuses.some(status => status === 'fail')) {
442
+ attemptToFixFailed = true
443
+ }
428
444
  if (testStatuses.every(status => status === 'fail')) {
429
445
  failedAllTests = true
430
446
  } else if (testStatuses.every(status => status === 'pass')) {
@@ -456,16 +472,15 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
456
472
  const willBeRetried = numRetries > 0 && numTestExecutions - 1 < numRetries
457
473
  const mightHitBreakpoint = this.isDiEnabled && numTestExecutions >= 2
458
474
 
459
- const asyncResource = asyncResources.get(event.test)
475
+ const ctx = testContexts.get(event.test)
460
476
 
461
477
  if (status === 'fail') {
462
478
  const shouldSetProbe = this.isDiEnabled && willBeRetried && numTestExecutions === 1
463
- asyncResource.runInAsyncScope(() => {
464
- testErrCh.publish({
465
- error: formatJestError(event.test.errors[0]),
466
- shouldSetProbe,
467
- promises
468
- })
479
+ testErrCh.publish({
480
+ ...ctx.currentStore,
481
+ error: formatJestError(event.test.errors[0]),
482
+ shouldSetProbe,
483
+ promises
469
484
  })
470
485
  }
471
486
 
@@ -484,14 +499,14 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
484
499
  isAtrRetry = true
485
500
  }
486
501
 
487
- asyncResource.runInAsyncScope(() => {
488
- testFinishCh.publish({
489
- status,
490
- testStartLine: getTestLineStart(event.test.asyncError, this.testSuite),
491
- attemptToFixPassed,
492
- failedAllTests,
493
- isAtrRetry
494
- })
502
+ testFinishCh.publish({
503
+ ...ctx.currentStore,
504
+ status,
505
+ testStartLine: getTestLineStart(event.test.asyncError, this.testSuite),
506
+ attemptToFixPassed,
507
+ failedAllTests,
508
+ attemptToFixFailed,
509
+ isAtrRetry
495
510
  })
496
511
 
497
512
  if (promises.isProbeReady) {
@@ -620,7 +635,7 @@ addHook({
620
635
  })
621
636
 
622
637
  function cliWrapper (cli, jestVersion) {
623
- const wrapped = shimmer.wrap(cli, 'runCLI', runCLI => async function () {
638
+ shimmer.wrap(cli, 'runCLI', runCLI => async function () {
624
639
  let onDone
625
640
  const configurationPromise = new Promise((resolve) => {
626
641
  onDone = resolve
@@ -859,8 +874,6 @@ function cliWrapper (cli, jestVersion) {
859
874
  return result
860
875
  })
861
876
 
862
- cli.runCLI = wrapped.runCLI
863
-
864
877
  return cli
865
878
  }
866
879
 
@@ -7,6 +7,8 @@ const {
7
7
  } = require('./helpers/instrument')
8
8
  const shimmer = require('../../datadog-shimmer')
9
9
 
10
+ const log = require('../../dd-trace/src/log')
11
+
10
12
  const producerStartCh = channel('apm:kafkajs:produce:start')
11
13
  const producerCommitCh = channel('apm:kafkajs:produce:commit')
12
14
  const producerFinishCh = channel('apm:kafkajs:produce:finish')
@@ -21,6 +23,8 @@ const batchConsumerStartCh = channel('apm:kafkajs:consume-batch:start')
21
23
  const batchConsumerFinishCh = channel('apm:kafkajs:consume-batch:finish')
22
24
  const batchConsumerErrorCh = channel('apm:kafkajs:consume-batch:error')
23
25
 
26
+ const disabledHeaderWeakSet = new WeakSet()
27
+
24
28
  function commitsFromEvent (event) {
25
29
  const { payload: { groupId, topics } } = event
26
30
  const commitList = []
@@ -65,15 +69,14 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
65
69
 
66
70
  try {
67
71
  const { topic, messages = [] } = arguments[0]
68
- for (const message of messages) {
69
- if (message !== null && typeof message === 'object') {
70
- message.headers = message.headers || {}
71
- }
72
- }
73
- producerStartCh.publish({ topic, messages, bootstrapServers, clusterId })
74
-
72
+ producerStartCh.publish({
73
+ topic,
74
+ messages,
75
+ bootstrapServers,
76
+ clusterId,
77
+ disableHeaderInjection: disabledHeaderWeakSet.has(producer)
78
+ })
75
79
  const result = send.apply(this, arguments)
76
-
77
80
  result.then(
78
81
  innerAsyncResource.bind(res => {
79
82
  producerFinishCh.publish(undefined)
@@ -81,6 +84,16 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
81
84
  }),
82
85
  innerAsyncResource.bind(err => {
83
86
  if (err) {
87
+ // Fixes bug where we would inject message headers for kafka brokers that don't support headers
88
+ // (version <0.11). On the error, we disable header injection.
89
+ // Tnfortunately the error name / type is not more specific.
90
+ // This approach is implemented by other tracers as well.
91
+ if (err.name === 'KafkaJSProtocolError' && err.type === 'UNKNOWN') {
92
+ disabledHeaderWeakSet.add(producer)
93
+ log.error('Kafka Broker responded with UNKNOWN_SERVER_ERROR (-1). ' +
94
+ 'Please look at broker logs for more information. ' +
95
+ 'Tracer message header injection for Kafka is disabled.')
96
+ }
84
97
  producerErrorCh.publish(err)
85
98
  }
86
99
  producerFinishCh.publish(undefined)