dd-trace 5.102.0 → 5.103.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 (133) hide show
  1. package/ext/exporters.js +1 -0
  2. package/package.json +12 -11
  3. package/packages/datadog-esbuild/src/utils.js +2 -2
  4. package/packages/datadog-instrumentations/src/ai.js +1 -1
  5. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +32 -15
  6. package/packages/datadog-instrumentations/src/couchbase.js +69 -220
  7. package/packages/datadog-instrumentations/src/cucumber.js +1 -1
  8. package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
  9. package/packages/datadog-instrumentations/src/electron.js +240 -0
  10. package/packages/datadog-instrumentations/src/fetch.js +5 -5
  11. package/packages/datadog-instrumentations/src/graphql.js +13 -12
  12. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
  13. package/packages/datadog-instrumentations/src/helpers/hook.js +4 -1
  14. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  15. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
  16. package/packages/datadog-instrumentations/src/helpers/kafka.js +41 -0
  17. package/packages/datadog-instrumentations/src/ioredis.js +16 -12
  18. package/packages/datadog-instrumentations/src/jest.js +351 -50
  19. package/packages/datadog-instrumentations/src/kafkajs.js +164 -173
  20. package/packages/datadog-instrumentations/src/mocha/main.js +73 -1
  21. package/packages/datadog-instrumentations/src/mongodb-core.js +33 -8
  22. package/packages/datadog-instrumentations/src/pg.js +24 -10
  23. package/packages/datadog-instrumentations/src/playwright.js +427 -55
  24. package/packages/datadog-instrumentations/src/redis.js +19 -10
  25. package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
  26. package/packages/datadog-plugin-aws-sdk/src/base.js +18 -24
  27. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
  28. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
  29. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  30. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
  31. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
  32. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
  33. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
  34. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
  35. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
  36. package/packages/datadog-plugin-couchbase/src/index.js +58 -52
  37. package/packages/datadog-plugin-cucumber/src/index.js +1 -0
  38. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +214 -22
  39. package/packages/datadog-plugin-cypress/src/support.js +13 -1
  40. package/packages/datadog-plugin-electron/src/index.js +17 -0
  41. package/packages/datadog-plugin-electron/src/ipc.js +143 -0
  42. package/packages/datadog-plugin-electron/src/net.js +82 -0
  43. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
  44. package/packages/datadog-plugin-graphql/src/execute.js +6 -28
  45. package/packages/datadog-plugin-graphql/src/resolve.js +30 -35
  46. package/packages/datadog-plugin-graphql/src/tools/signature.js +32 -7
  47. package/packages/datadog-plugin-graphql/src/tools/transforms.js +118 -100
  48. package/packages/datadog-plugin-graphql/src/utils.js +29 -0
  49. package/packages/datadog-plugin-grpc/src/client.js +6 -7
  50. package/packages/datadog-plugin-grpc/src/util.js +57 -22
  51. package/packages/datadog-plugin-http/src/client.js +2 -2
  52. package/packages/datadog-plugin-jest/src/index.js +92 -50
  53. package/packages/datadog-plugin-mocha/src/index.js +1 -0
  54. package/packages/datadog-plugin-mongodb-core/src/index.js +36 -70
  55. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  56. package/packages/datadog-plugin-openai/src/services.js +2 -1
  57. package/packages/datadog-plugin-pg/src/index.js +3 -3
  58. package/packages/datadog-plugin-playwright/src/index.js +4 -0
  59. package/packages/datadog-plugin-redis/src/index.js +18 -23
  60. package/packages/dd-trace/src/aiguard/index.js +3 -1
  61. package/packages/dd-trace/src/aiguard/sdk.js +36 -30
  62. package/packages/dd-trace/src/aiguard/tags.js +20 -11
  63. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
  64. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
  65. package/packages/dd-trace/src/azure_metadata.js +17 -6
  66. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +4 -4
  67. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  68. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -4
  69. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +1 -1
  70. package/packages/dd-trace/src/config/defaults.js +3 -14
  71. package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -1
  72. package/packages/dd-trace/src/config/helper.js +4 -0
  73. package/packages/dd-trace/src/config/index.js +2 -2
  74. package/packages/dd-trace/src/config/major-overrides.js +98 -0
  75. package/packages/dd-trace/src/config/parsers.js +7 -1
  76. package/packages/dd-trace/src/config/supported-configurations.json +51 -38
  77. package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
  78. package/packages/dd-trace/src/datastreams/manager.js +1 -1
  79. package/packages/dd-trace/src/datastreams/processor.js +2 -2
  80. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
  81. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
  82. package/packages/dd-trace/src/debugger/devtools_client/state.js +2 -1
  83. package/packages/dd-trace/src/debugger/index.js +7 -7
  84. package/packages/dd-trace/src/dogstatsd.js +2 -2
  85. package/packages/dd-trace/src/encode/0.4.js +45 -54
  86. package/packages/dd-trace/src/encode/0.5.js +34 -3
  87. package/packages/dd-trace/src/encode/agentless-json.js +1 -1
  88. package/packages/dd-trace/src/exporter.js +2 -0
  89. package/packages/dd-trace/src/exporters/agent/index.js +2 -1
  90. package/packages/dd-trace/src/exporters/agentless/index.js +3 -2
  91. package/packages/dd-trace/src/exporters/agentless/writer.js +2 -2
  92. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -1
  93. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  94. package/packages/dd-trace/src/exporters/electron/index.js +49 -0
  95. package/packages/dd-trace/src/external-logger/src/index.js +2 -1
  96. package/packages/dd-trace/src/git_metadata.js +10 -8
  97. package/packages/dd-trace/src/lambda/handler-paths.js +52 -0
  98. package/packages/dd-trace/src/lambda/index.js +62 -14
  99. package/packages/dd-trace/src/lambda/runtime/patch.js +21 -46
  100. package/packages/dd-trace/src/llmobs/index.js +13 -2
  101. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
  102. package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
  103. package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
  104. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +2 -1
  105. package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
  106. package/packages/dd-trace/src/payload-tagging/config/index.js +2 -2
  107. package/packages/dd-trace/src/plugins/ci_plugin.js +49 -4
  108. package/packages/dd-trace/src/plugins/database.js +54 -12
  109. package/packages/dd-trace/src/plugins/index.js +1 -0
  110. package/packages/dd-trace/src/plugins/plugin.js +2 -4
  111. package/packages/dd-trace/src/plugins/util/ci.js +8 -8
  112. package/packages/dd-trace/src/plugins/util/git-cache.js +20 -18
  113. package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
  114. package/packages/dd-trace/src/plugins/util/test.js +37 -5
  115. package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -15
  116. package/packages/dd-trace/src/priority_sampler.js +1 -1
  117. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  118. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
  119. package/packages/dd-trace/src/profiling/ssi-heuristics.js +1 -1
  120. package/packages/dd-trace/src/rate_limiter.js +1 -1
  121. package/packages/dd-trace/src/remote_config/scheduler.js +1 -1
  122. package/packages/dd-trace/src/ritm.js +2 -1
  123. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
  124. package/packages/dd-trace/src/serverless.js +5 -2
  125. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +20 -0
  126. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  127. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +20 -0
  128. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  129. package/packages/dd-trace/src/span_stats.js +1 -1
  130. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  131. package/packages/dd-trace/src/telemetry/endpoints.js +1 -1
  132. package/packages/dd-trace/src/telemetry/telemetry.js +2 -2
  133. package/packages/dd-trace/src/lambda/runtime/ritm.js +0 -133
@@ -0,0 +1,49 @@
1
+ 'use strict'
2
+
3
+ const { channel } = require('dc-polyfill')
4
+ const { truncateSpan, normalizeSpan } = require('../../encode/tags-processors')
5
+
6
+ const traceChannel = channel('datadog:apm:electron:export')
7
+
8
+ class ElectronExporter {
9
+ #timer
10
+ #traces = []
11
+
12
+ constructor (config) {
13
+ this._config = config
14
+
15
+ globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(this.flush.bind(this))
16
+ }
17
+
18
+ export (spans) {
19
+ this.#traces.push(spans)
20
+
21
+ const { flushInterval } = this._config
22
+
23
+ if (flushInterval === 0) {
24
+ this.flush()
25
+ } else if (this.#timer === undefined) {
26
+ this.#timer = setTimeout(() => {
27
+ this.flush()
28
+ this.#timer = undefined
29
+ }, flushInterval)
30
+ this.#timer.unref?.()
31
+ }
32
+ }
33
+
34
+ flush (done = () => {}) {
35
+ clearTimeout(this.#timer)
36
+ this.#timer = undefined
37
+
38
+ const traces = this.#traces.splice(0)
39
+
40
+ if (traces.length > 0 && traceChannel.hasSubscribers) {
41
+ const formattedTraces = traces.map(spans => spans.map(span => normalizeSpan(truncateSpan(span))))
42
+ traceChannel.publish(formattedTraces)
43
+ }
44
+
45
+ done()
46
+ }
47
+ }
48
+
49
+ module.exports = ElectronExporter
@@ -26,7 +26,8 @@ class ExternalLogger {
26
26
  }
27
27
  this.timer = setInterval(() => {
28
28
  this.flush()
29
- }, this.interval).unref()
29
+ }, this.interval)
30
+ this.timer.unref?.()
30
31
 
31
32
  tracerLogger.debug(`started log writer to https://${this.intake}${this.endpoint}`)
32
33
  }
@@ -12,19 +12,21 @@ const {
12
12
  resolveGitHeadSHA,
13
13
  } = require('./config/git_properties')
14
14
 
15
- /** @type {{ commitSHA: string | undefined, repositoryUrl: string | undefined } | undefined} */
16
- let cached
15
+ /**
16
+ * @typedef {{ commitSHA: string | undefined, repositoryUrl: string | undefined }} GitMetadata
17
+ * @type {{ enabled?: GitMetadata, disabled?: GitMetadata }}
18
+ */
19
+ const cache = {}
17
20
 
18
21
  /**
19
22
  * @param {import('./config/config-types').ConfigProperties} config
20
23
  */
21
24
  function getGitMetadata (config) {
22
- if (cached) return cached
23
-
24
25
  if (!config.DD_TRACE_GIT_METADATA_ENABLED) {
25
- cached = { commitSHA: undefined, repositoryUrl: undefined }
26
- return cached
26
+ cache.disabled ??= { commitSHA: undefined, repositoryUrl: undefined }
27
+ return cache.disabled
27
28
  }
29
+ if (cache.enabled) return cache.enabled
28
30
 
29
31
  let repositoryUrl = removeUserSensitiveInfo(config.DD_GIT_REPOSITORY_URL ?? config.tags[GIT_REPOSITORY_URL])
30
32
  let commitSHA = config.DD_GIT_COMMIT_SHA ?? config.tags[GIT_COMMIT_SHA]
@@ -59,8 +61,8 @@ function getGitMetadata (config) {
59
61
 
60
62
  commitSHA ??= resolveGitHeadSHA(gitFolderPath)
61
63
 
62
- cached = { commitSHA, repositoryUrl }
63
- return cached
64
+ cache.enabled = { commitSHA, repositoryUrl }
65
+ return cache.enabled
64
66
  }
65
67
 
66
68
  module.exports = getGitMetadata
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * Modifications copyright 2022 Datadog, Inc.
4
+ *
5
+ * Some functions are part of aws-lambda-nodejs-runtime-interface-client
6
+ * https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/v2.1.0/src/utils/UserFunction.ts
7
+ */
8
+ 'use strict'
9
+
10
+ const path = require('path')
11
+
12
+ /**
13
+ * Example: `'./api/src/index.nested.handler'` → `['./api/src/', 'index.nested.handler']`.
14
+ *
15
+ * @param {string} fullHandler
16
+ */
17
+ function extractModuleRootAndHandler (fullHandler) {
18
+ const handlerString = path.basename(fullHandler)
19
+ const moduleRoot = fullHandler.slice(0, Math.max(0, fullHandler.indexOf(handlerString)))
20
+ return [moduleRoot, handlerString]
21
+ }
22
+
23
+ /**
24
+ * Example: `'index.nested.handler'` → `['index', 'nested.handler']`.
25
+ *
26
+ * @param {string} handler
27
+ * @throws {Error} When the handler is not of the form `<module>.<path>`.
28
+ */
29
+ function extractModuleNameAndHandlerPath (handler) {
30
+ const match = handler.match(/^([^.]*)\.(.*)$/)
31
+ if (!match || match.length !== 3) {
32
+ throw new Error(`Malformed handler name: ${handler}`)
33
+ }
34
+ return [match[1], match[2]]
35
+ }
36
+
37
+ /**
38
+ * @param {string} lambdaStylePath `LAMBDA_TASK_ROOT` joined with the module root and module name.
39
+ */
40
+ function getLambdaFilePaths (lambdaStylePath) {
41
+ return [
42
+ `${lambdaStylePath}.js`,
43
+ `${lambdaStylePath}.mjs`,
44
+ `${lambdaStylePath}.cjs`,
45
+ ]
46
+ }
47
+
48
+ module.exports = {
49
+ extractModuleRootAndHandler,
50
+ extractModuleNameAndHandlerPath,
51
+ getLambdaFilePaths,
52
+ }
@@ -1,17 +1,65 @@
1
1
  'use strict'
2
2
 
3
- const { getValueFromEnvSources } = require('../config/helper')
4
- const { registerLambdaHook } = require('./runtime/ritm')
5
-
6
- /**
7
- * It is safe to do it this way, since customers will never be expected to disable
8
- * this specific instrumentation through the init config object.
9
- */
10
- const _DD_TRACE_DISABLED_INSTRUMENTATIONS = getValueFromEnvSources('DD_TRACE_DISABLED_INSTRUMENTATIONS') || ''
11
- const _disabledInstrumentations = new Set(
12
- _DD_TRACE_DISABLED_INSTRUMENTATIONS ? _DD_TRACE_DISABLED_INSTRUMENTATIONS.split(',') : []
13
- )
14
-
15
- if (!_disabledInstrumentations.has('lambda')) {
16
- registerLambdaHook()
3
+ const path = require('path')
4
+
5
+ const log = require('../log')
6
+ const { getEnvironmentVariable, getValueFromEnvSources } = require('../config/helper')
7
+ const Hook = require('../../../datadog-instrumentations/src/helpers/hook')
8
+ const instrumentations = require('../../../datadog-instrumentations/src/helpers/instrumentations')
9
+ const {
10
+ filename,
11
+ pathSepExpr,
12
+ } = require('../../../datadog-instrumentations/src/helpers/register')
13
+ const {
14
+ extractModuleNameAndHandlerPath,
15
+ extractModuleRootAndHandler,
16
+ getLambdaFilePaths,
17
+ } = require('./handler-paths')
18
+
19
+ if (!getValueFromEnvSources('DD_TRACE_DISABLED_INSTRUMENTATIONS')?.split(',').includes('lambda')) {
20
+ const lambdaTaskRoot = getEnvironmentVariable('LAMBDA_TASK_ROOT')
21
+ const originalLambdaHandler = getValueFromEnvSources('DD_LAMBDA_HANDLER')
22
+
23
+ if (originalLambdaHandler !== undefined && lambdaTaskRoot !== undefined) {
24
+ const [moduleRoot, moduleAndHandler] = extractModuleRootAndHandler(originalLambdaHandler)
25
+ const [moduleName] = extractModuleNameAndHandlerPath(moduleAndHandler)
26
+
27
+ const lambdaStylePath = path.resolve(lambdaTaskRoot, moduleRoot, moduleName)
28
+ const lambdaFilePaths = getLambdaFilePaths(lambdaStylePath)
29
+
30
+ // TODO: Redo this like any other instrumentation.
31
+ Hook(lambdaFilePaths, (moduleExports, name, _, moduleVersion) => {
32
+ require('./runtime/patch')
33
+
34
+ for (const { hook } of instrumentations[name]) {
35
+ try {
36
+ moduleExports = hook(moduleExports, moduleVersion) ?? moduleExports
37
+ } catch (error) {
38
+ log.error('Error executing lambda hook', error)
39
+ }
40
+ }
41
+
42
+ return moduleExports
43
+ })
44
+ return
45
+ }
46
+
47
+ const moduleToPatch = 'datadog-lambda-js'
48
+ Hook([moduleToPatch], (moduleExports, moduleName, _, moduleVersion) => {
49
+ moduleName = moduleName.replace(pathSepExpr, '/')
50
+ require('./runtime/patch')
51
+
52
+ for (const { file, hook } of instrumentations[moduleToPatch]) {
53
+ const fullFilename = filename(moduleToPatch, file)
54
+ if (moduleName === fullFilename) {
55
+ try {
56
+ moduleExports = hook(moduleExports, moduleVersion) ?? moduleExports
57
+ } catch (error) {
58
+ log.error('Error executing lambda hook for datadog-lambda-js', error)
59
+ }
60
+ }
61
+ }
62
+
63
+ return moduleExports
64
+ })
17
65
  }
@@ -6,54 +6,32 @@ const { datadog } = require('../handler')
6
6
  const { addHook } = require('../../../../datadog-instrumentations/src/helpers/instrument')
7
7
  const shimmer = require('../../../../datadog-shimmer')
8
8
  const { getEnvironmentVariable, getValueFromEnvSources } = require('../../config/helper')
9
- const { _extractModuleNameAndHandlerPath, _extractModuleRootAndHandler, _getLambdaFilePaths } = require('./ritm')
10
-
11
- /**
12
- * Patches a Datadog Lambda module by calling `patchDatadogLambdaHandler`
13
- * with the handler name `datadog`.
14
- *
15
- * @param {object} datadogLambdaModule node module to be patched.
16
- * @returns a Datadog Lambda module with the `datadog` function from
17
- * `datadog-lambda-js` patched.
18
- */
19
- const patchDatadogLambdaModule = (datadogLambdaModule) => {
9
+ const {
10
+ extractModuleNameAndHandlerPath,
11
+ extractModuleRootAndHandler,
12
+ getLambdaFilePaths,
13
+ } = require('../handler-paths')
14
+
15
+ /** @param {object} datadogLambdaModule */
16
+ function patchDatadogLambdaModule (datadogLambdaModule) {
20
17
  shimmer.wrap(datadogLambdaModule, 'datadog', patchDatadogLambdaHandler)
21
-
22
18
  return datadogLambdaModule
23
19
  }
24
20
 
25
- /**
26
- * Patches a Datadog Lambda handler in order to do
27
- * Datadog instrumentation by getting the Lambda handler from its
28
- * arguments.
29
- *
30
- * @param {Function} datadogHandler the Datadog Lambda handler to destructure.
31
- * @returns the datadogHandler with its arguments patched.
32
- */
21
+ /** @param {Function} datadogHandler */
33
22
  function patchDatadogLambdaHandler (datadogHandler) {
34
- return (userHandler) => {
35
- return datadogHandler(datadog(userHandler))
36
- }
23
+ return userHandler => datadogHandler(datadog(userHandler))
37
24
  }
38
25
 
39
- /**
40
- * Patches a Lambda module on the given handler path.
41
- *
42
- * @param {string} handlerPath path of the handler to be patched.
43
- * @returns a module with the given handler path patched.
44
- */
45
- const patchLambdaModule = (handlerPath) => (lambdaModule) => {
46
- shimmer.wrap(lambdaModule, handlerPath, patchLambdaHandler)
47
-
48
- return lambdaModule
26
+ /** @param {string} handlerPath */
27
+ function patchLambdaModule (handlerPath) {
28
+ return lambdaModule => {
29
+ shimmer.wrap(lambdaModule, handlerPath, patchLambdaHandler)
30
+ return lambdaModule
31
+ }
49
32
  }
50
33
 
51
- /**
52
- * Patches a Lambda handler in order to do Datadog instrumentation.
53
- *
54
- * @param {Function} lambdaHandler the Lambda handler to be patched.
55
- * @returns a function which patches the given Lambda handler.
56
- */
34
+ /** @param {Function} lambdaHandler */
57
35
  function patchLambdaHandler (lambdaHandler) {
58
36
  return datadog(lambdaHandler)
59
37
  }
@@ -62,16 +40,13 @@ const lambdaTaskRoot = getEnvironmentVariable('LAMBDA_TASK_ROOT')
62
40
  const originalLambdaHandler = getValueFromEnvSources('DD_LAMBDA_HANDLER')
63
41
 
64
42
  if (originalLambdaHandler === undefined) {
65
- // Instrumentation is done manually.
66
43
  addHook({ name: 'datadog-lambda-js' }, patchDatadogLambdaModule)
67
44
  } else {
68
- const [moduleRoot, moduleAndHandler] = _extractModuleRootAndHandler(originalLambdaHandler)
69
- const [_module, handlerPath] = _extractModuleNameAndHandlerPath(moduleAndHandler)
70
-
71
- const lambdaStylePath = path.resolve(lambdaTaskRoot, moduleRoot, _module)
72
- const lambdaFilePaths = _getLambdaFilePaths(lambdaStylePath)
45
+ const [moduleRoot, moduleAndHandler] = extractModuleRootAndHandler(originalLambdaHandler)
46
+ const [moduleName, handlerPath] = extractModuleNameAndHandlerPath(moduleAndHandler)
73
47
 
74
- for (const lambdaFilePath of lambdaFilePaths) {
48
+ const lambdaStylePath = path.resolve(lambdaTaskRoot, moduleRoot, moduleName)
49
+ for (const lambdaFilePath of getLambdaFilePaths(lambdaStylePath)) {
75
50
  addHook({ name: lambdaFilePath }, patchLambdaModule(handlerPath))
76
51
  }
77
52
  }
@@ -108,6 +108,10 @@ function disable () {
108
108
  // since LLMObs traces can extend between services and be the same trace,
109
109
  // we need to propagate the parent id and mlApp.
110
110
  function handleLLMObsParentIdInjection ({ carrier }) {
111
+ // Respect the standard propagator's gate: when trace tag propagation is
112
+ // disabled, don't write `x-datadog-tags` for LLMObs either.
113
+ if (globalTracerConfig.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) return
114
+
111
115
  const parent = storage.getStore()?.span
112
116
  const mlObsSpanTags = LLMObsTagger.tagMap.get(parent)
113
117
 
@@ -118,8 +122,15 @@ function handleLLMObsParentIdInjection ({ carrier }) {
118
122
  parentContext?._trace?.tags?.[PROPAGATED_ML_APP_KEY] ||
119
123
  globalTracerConfig.llmobs.mlApp
120
124
 
121
- if (parentId) carrier['x-datadog-tags'] += `,${PROPAGATED_PARENT_ID_KEY}=${parentId}`
122
- if (mlApp) carrier['x-datadog-tags'] += `,${PROPAGATED_ML_APP_KEY}=${mlApp}`
125
+ if (!parentId && !mlApp) return
126
+
127
+ // `_injectTags` only writes `x-datadog-tags` when the trace has `_dd.p.*`
128
+ // tags, so it may be undefined here — coalesce before appending.
129
+ const existing = carrier['x-datadog-tags']
130
+ let tags = existing || ''
131
+ if (parentId) tags += `${tags ? ',' : ''}${PROPAGATED_PARENT_ID_KEY}=${parentId}`
132
+ if (mlApp) tags += `${tags ? ',' : ''}${PROPAGATED_ML_APP_KEY}=${mlApp}`
133
+ if (tags !== existing) carrier['x-datadog-tags'] = tags
123
134
  }
124
135
 
125
136
  function handleFlush () {
@@ -14,7 +14,17 @@ const llmobsStore = storage('llmobs')
14
14
 
15
15
  const ENABLED_OPERATIONS = new Set(['invokeModel', 'invokeModelWithResponseStream'])
16
16
 
17
- const requestIdsToTokens = {}
17
+ /**
18
+ * @typedef {{
19
+ * inputTokensFromHeaders?: number,
20
+ * outputTokensFromHeaders?: number,
21
+ * cacheReadTokensFromHeaders?: number,
22
+ * cacheWriteTokensFromHeaders?: number,
23
+ * }} HeaderTokens
24
+ */
25
+
26
+ /** @type {Map<string, HeaderTokens>} */
27
+ const pendingTokenHeaders = new Map()
18
28
 
19
29
  class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
20
30
  constructor () {
@@ -24,33 +34,39 @@ class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
24
34
  const { response } = ctx
25
35
  const request = response.request
26
36
  const operation = request.operation
37
+
38
+ // Release the cached headers even for operations the plugin does not tag,
39
+ // so non-LLM Bedrock calls do not leak entries into pendingTokenHeaders.
40
+ const tokensFromHeaders = consumeTokenHeaders(response.$metadata?.requestId)
41
+
27
42
  // avoids instrumenting other non supported runtime operations
28
- if (!ENABLED_OPERATIONS.has(operation)) {
29
- return
30
- }
43
+ if (!ENABLED_OPERATIONS.has(operation)) return
44
+
31
45
  const { modelProvider, modelName } = parseModelId(request.params.modelId)
32
46
 
33
47
  // avoids instrumenting non llm type
34
- if (modelName.includes('embed')) {
35
- return
36
- }
48
+ if (modelName.includes('embed')) return
49
+
37
50
  const span = ctx.currentStore?.span
38
- this.setLLMObsTags({ ctx, request, span, response, modelProvider, modelName })
51
+ this.setLLMObsTags({ ctx, request, span, response, modelProvider, modelName, tokensFromHeaders })
39
52
  })
40
53
 
41
54
  this.addSub('apm:aws:response:deserialize:bedrockruntime', ({ headers }) => {
42
55
  const requestId = headers['x-amzn-requestid']
56
+ // No request id means no way to correlate with the :complete: event.
57
+ if (!requestId) return
58
+
43
59
  const inputTokenCount = headers['x-amzn-bedrock-input-token-count']
44
60
  const outputTokenCount = headers['x-amzn-bedrock-output-token-count']
45
61
  const cacheReadTokenCount = headers['x-amzn-bedrock-cache-read-input-token-count']
46
62
  const cacheWriteTokenCount = headers['x-amzn-bedrock-cache-write-input-token-count']
47
63
 
48
- requestIdsToTokens[requestId] = {
64
+ pendingTokenHeaders.set(requestId, {
49
65
  inputTokensFromHeaders: inputTokenCount && Number.parseInt(inputTokenCount),
50
66
  outputTokensFromHeaders: outputTokenCount && Number.parseInt(outputTokenCount),
51
67
  cacheReadTokensFromHeaders: cacheReadTokenCount && Number.parseInt(cacheReadTokenCount),
52
68
  cacheWriteTokensFromHeaders: cacheWriteTokenCount && Number.parseInt(cacheWriteTokenCount),
53
- }
69
+ })
54
70
  })
55
71
 
56
72
  this.addSub('apm:aws:response:streamed-chunk:bedrockruntime', ({ ctx, chunk }) => {
@@ -60,7 +76,7 @@ class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
60
76
  })
61
77
  }
62
78
 
63
- setLLMObsTags ({ ctx, request, span, response, modelProvider, modelName }) {
79
+ setLLMObsTags ({ ctx, request, span, response, modelProvider, modelName, tokensFromHeaders }) {
64
80
  const isStream = request?.operation?.toLowerCase().includes('stream')
65
81
  telemetry.incrementLLMObsSpanStartCount({ autoinstrumented: true, integration: 'bedrock' })
66
82
 
@@ -97,7 +113,7 @@ class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
97
113
 
98
114
  // add token metrics
99
115
  const { inputTokens, outputTokens, totalTokens, cacheReadTokens, cacheWriteTokens } = extractTokens({
100
- requestId: response.$metadata.requestId,
116
+ tokensFromHeaders,
101
117
  usage: textAndResponseReason.usage,
102
118
  })
103
119
  this._tagger.tagMetrics(span, {
@@ -110,14 +126,28 @@ class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
110
126
  }
111
127
  }
112
128
 
113
- function extractTokens ({ requestId, usage }) {
129
+ /**
130
+ * @param {string | undefined} requestId
131
+ * @returns {HeaderTokens | undefined}
132
+ */
133
+ function consumeTokenHeaders (requestId) {
134
+ const tokens = pendingTokenHeaders.get(requestId)
135
+ pendingTokenHeaders.delete(requestId)
136
+ return tokens
137
+ }
138
+
139
+ /**
140
+ * Combine response-body usage with header-derived counts, preferring the body.
141
+ *
142
+ * @param {{ tokensFromHeaders: HeaderTokens | undefined, usage: Record<string, number | undefined> }} options
143
+ */
144
+ function extractTokens ({ tokensFromHeaders, usage }) {
114
145
  const {
115
146
  inputTokensFromHeaders,
116
147
  outputTokensFromHeaders,
117
148
  cacheReadTokensFromHeaders,
118
149
  cacheWriteTokensFromHeaders,
119
- } = requestIdsToTokens[requestId] || {}
120
- delete requestIdsToTokens[requestId]
150
+ } = tokensFromHeaders ?? {}
121
151
 
122
152
  const inputTokens = usage.inputTokens || inputTokensFromHeaders || 0
123
153
  const outputTokens = usage.outputTokens || outputTokensFromHeaders || 0
@@ -54,7 +54,8 @@ class BaseLLMObsWriter {
54
54
 
55
55
  this._periodic = setInterval(() => {
56
56
  this.flush()
57
- }, this._interval).unref()
57
+ }, this._interval)
58
+ this._periodic.unref?.()
58
59
 
59
60
  const destroyer = this.destroy.bind(this)
60
61
  globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(destroyer)
@@ -55,7 +55,8 @@ class BaseFFEWriter {
55
55
 
56
56
  this._periodic = setInterval(() => {
57
57
  this.flush()
58
- }, this._interval).unref()
58
+ }, this._interval)
59
+ this._periodic.unref?.()
59
60
 
60
61
  const destroyer = this.destroy.bind(this)
61
62
  globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(destroyer)
@@ -167,7 +167,8 @@ class PeriodicMetricReader {
167
167
 
168
168
  this.#timer = setInterval(() => {
169
169
  this.#collectAndExport()
170
- }, this.#exportInterval).unref()
170
+ }, this.#exportInterval)
171
+ this.#timer.unref?.()
171
172
  }
172
173
 
173
174
  /**
@@ -9,6 +9,7 @@ const tags = require('../../../../../ext/tags')
9
9
  const { getConfiguredEnvName } = require('../../config/helper')
10
10
  const { setAllBaggageItems, getAllBaggageItems, removeAllBaggageItems } = require('../../baggage')
11
11
  const telemetryMetrics = require('../../telemetry/metrics')
12
+ const { DD_MAJOR } = require('../../../../../version')
12
13
 
13
14
  const { AUTO_KEEP, AUTO_REJECT, USER_KEEP } = require('../../../../../ext/priority')
14
15
  const TraceState = require('./tracestate')
@@ -78,10 +79,17 @@ class TextMapPropagator {
78
79
  constructor (config) {
79
80
  this._config = config
80
81
 
81
- // TODO: should match "b3 single header" in next major
82
- const envName = getConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE')
83
- // eslint-disable-next-line eslint-rules/eslint-env-aliases
84
- this.#extractB3Context = envName === 'OTEL_PROPAGATORS' ? this._extractB3SingleContext : this._extractB3MultiContext
82
+ // v6: `'b3'` is always single-header. v5: env-name decides — OTEL_PROPAGATORS callers expect
83
+ // single, the legacy `DD_TRACE_PROPAGATION_STYLE` callers expect multi.
84
+ if (DD_MAJOR >= 6) {
85
+ this.#extractB3Context = this._extractB3SingleContext
86
+ } else {
87
+ const envName = getConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE')
88
+ // eslint-disable-next-line eslint-rules/eslint-env-aliases
89
+ this.#extractB3Context = envName === 'OTEL_PROPAGATORS'
90
+ ? this._extractB3SingleContext
91
+ : this._extractB3MultiContext
92
+ }
85
93
  }
86
94
 
87
95
  /**
@@ -262,9 +270,10 @@ class TextMapPropagator {
262
270
  }
263
271
 
264
272
  _injectB3MultipleHeaders (spanContext, carrier) {
265
- const hasB3 = this._hasPropagationStyle('inject', 'b3')
266
- const hasB3multi = this._hasPropagationStyle('inject', 'b3multi')
267
- if (!(hasB3 || hasB3multi)) return
273
+ // v5 also accepts the legacy `'b3'` spelling for multi; v6 routes `'b3'` to single-header.
274
+ const hasB3multi = this._hasPropagationStyle('inject', 'b3multi') ||
275
+ (DD_MAJOR < 6 && this._hasPropagationStyle('inject', 'b3'))
276
+ if (!hasB3multi) return
268
277
 
269
278
  carrier[b3TraceKey] = this._getB3TraceId(spanContext)
270
279
  carrier[b3SpanKey] = spanContext._spanId.toString(16)
@@ -280,7 +289,9 @@ class TextMapPropagator {
280
289
  }
281
290
 
282
291
  _injectB3SingleHeader (spanContext, carrier) {
283
- const hasB3SingleHeader = this._hasPropagationStyle('inject', 'b3 single header')
292
+ // v6 keeps `'b3 single header'` as a back-compat alias for callers that bypass parser normalisation.
293
+ const hasB3SingleHeader = this._hasPropagationStyle('inject', 'b3 single header') ||
294
+ (DD_MAJOR >= 6 && this._hasPropagationStyle('inject', 'b3'))
284
295
  if (!hasB3SingleHeader) return null
285
296
 
286
297
  const traceId = this._getB3TraceId(spanContext)
@@ -394,7 +405,7 @@ class TextMapPropagator {
394
405
  case 'tracecontext':
395
406
  extractedContext = this._extractTraceparentContext(carrier)
396
407
  break
397
- case 'b3 single header': // TODO: delete in major after singular "b3"
408
+ case 'b3 single header':
398
409
  extractedContext = this._extractB3SingleContext(carrier)
399
410
  break
400
411
  case 'b3':
@@ -30,8 +30,8 @@ function getSDKRules (sdk, requestInput, responseInput) {
30
30
  * Appends input rules to all supported SDKs and returns a structure mapping SDK
31
31
  * names to per-service rules.
32
32
  *
33
- * @param {string[]} [requestInput=[]]
34
- * @param {string[]} [responseInput=[]]
33
+ * @param {string[]} [requestInput]
34
+ * @param {string[]} [responseInput]
35
35
  * @returns {Record<string, SDKRules>}
36
36
  */
37
37
  function appendRules (requestInput = [], responseInput = []) {