dd-trace 5.82.0 → 5.84.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 (150) hide show
  1. package/LICENSE-3rdparty.csv +77 -79
  2. package/ci/init.js +6 -6
  3. package/index.d.ts +213 -4
  4. package/loader-hook.mjs +1 -1
  5. package/package.json +59 -56
  6. package/packages/datadog-core/src/storage.js +7 -7
  7. package/packages/datadog-esbuild/index.js +6 -0
  8. package/packages/datadog-instrumentations/src/ai.js +7 -3
  9. package/packages/datadog-instrumentations/src/child_process.js +1 -1
  10. package/packages/datadog-instrumentations/src/cucumber.js +1 -1
  11. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  12. package/packages/datadog-instrumentations/src/helpers/instrumentations.js +4 -3
  13. package/packages/datadog-instrumentations/src/helpers/register.js +3 -7
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +6 -0
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
  16. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +73 -16
  17. package/packages/datadog-instrumentations/src/http/client.js +2 -2
  18. package/packages/datadog-instrumentations/src/jest.js +124 -64
  19. package/packages/datadog-instrumentations/src/koa.js +2 -1
  20. package/packages/datadog-instrumentations/src/light-my-request.js +2 -2
  21. package/packages/datadog-instrumentations/src/mocha/main.js +2 -2
  22. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -1
  23. package/packages/datadog-instrumentations/src/mocha.js +1 -1
  24. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  25. package/packages/datadog-instrumentations/src/mysql2.js +2 -2
  26. package/packages/datadog-instrumentations/src/net.js +13 -5
  27. package/packages/datadog-instrumentations/src/nyc.js +1 -1
  28. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +4 -4
  29. package/packages/datadog-instrumentations/src/pg.js +4 -2
  30. package/packages/datadog-instrumentations/src/playwright.js +15 -11
  31. package/packages/datadog-instrumentations/src/selenium.js +2 -2
  32. package/packages/datadog-instrumentations/src/undici.js +12 -1
  33. package/packages/datadog-plugin-aws-sdk/src/base.js +4 -4
  34. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +2 -2
  35. package/packages/datadog-plugin-azure-service-bus/src/producer.js +2 -2
  36. package/packages/datadog-plugin-cucumber/src/index.js +35 -34
  37. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
  38. package/packages/datadog-plugin-dd-trace-api/src/index.js +2 -2
  39. package/packages/datadog-plugin-express/src/code_origin.js +21 -15
  40. package/packages/datadog-plugin-fastify/src/code_origin.js +17 -4
  41. package/packages/datadog-plugin-jest/src/index.js +2 -2
  42. package/packages/datadog-plugin-mocha/src/index.js +2 -2
  43. package/packages/datadog-plugin-mongodb-core/src/index.js +2 -2
  44. package/packages/datadog-plugin-playwright/src/index.js +26 -26
  45. package/packages/datadog-plugin-undici/src/index.js +305 -2
  46. package/packages/datadog-plugin-vitest/src/index.js +5 -5
  47. package/packages/datadog-shimmer/src/shimmer.js +2 -5
  48. package/packages/dd-trace/index.js +19 -0
  49. package/packages/dd-trace/src/agent/info.js +57 -0
  50. package/packages/dd-trace/src/agent/url.js +28 -0
  51. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
  52. package/packages/dd-trace/src/appsec/index.js +47 -7
  53. package/packages/dd-trace/src/appsec/rasp/index.js +2 -4
  54. package/packages/dd-trace/src/azure_metadata.js +8 -3
  55. package/packages/dd-trace/src/baggage.js +36 -11
  56. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +5 -1
  57. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -2
  58. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +3 -4
  59. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -2
  60. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +5 -5
  61. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
  62. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -11
  63. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +3 -3
  64. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +4 -4
  65. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +1 -1
  66. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -2
  67. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +4 -4
  68. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -4
  69. package/packages/dd-trace/src/ci-visibility/telemetry.js +6 -2
  70. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +2 -2
  71. package/packages/dd-trace/src/{config_defaults.js → config/defaults.js} +3 -3
  72. package/packages/dd-trace/src/{config-helper.js → config/helper.js} +88 -15
  73. package/packages/dd-trace/src/{config.js → config/index.js} +107 -46
  74. package/packages/dd-trace/src/config/remote_config.js +188 -19
  75. package/packages/dd-trace/src/{config_stable.js → config/stable.js} +20 -32
  76. package/packages/dd-trace/src/{supported-configurations.json → config/supported-configurations.json} +3 -1
  77. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -5
  78. package/packages/dd-trace/src/datastreams/processor.js +1 -1
  79. package/packages/dd-trace/src/datastreams/writer.js +2 -8
  80. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  81. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -7
  82. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +10 -11
  83. package/packages/dd-trace/src/debugger/devtools_client/send.js +3 -3
  84. package/packages/dd-trace/src/debugger/devtools_client/snapshot/constants.js +1 -1
  85. package/packages/dd-trace/src/debugger/index.js +83 -15
  86. package/packages/dd-trace/src/dogstatsd.js +5 -11
  87. package/packages/dd-trace/src/encode/0.4.js +2 -2
  88. package/packages/dd-trace/src/exporter.js +1 -1
  89. package/packages/dd-trace/src/exporters/agent/index.js +5 -11
  90. package/packages/dd-trace/src/exporters/agent/writer.js +12 -16
  91. package/packages/dd-trace/src/exporters/common/{agent-info-exporter.js → buffering-exporter.js} +10 -37
  92. package/packages/dd-trace/src/exporters/common/docker.js +2 -2
  93. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  94. package/packages/dd-trace/src/exporters/common/util.js +2 -2
  95. package/packages/dd-trace/src/exporters/span-stats/index.js +3 -10
  96. package/packages/dd-trace/src/flare/index.js +1 -1
  97. package/packages/dd-trace/src/guardrails/telemetry.js +1 -1
  98. package/packages/dd-trace/src/index.js +4 -4
  99. package/packages/dd-trace/src/lambda/handler.js +2 -2
  100. package/packages/dd-trace/src/lambda/index.js +2 -2
  101. package/packages/dd-trace/src/lambda/runtime/patch.js +2 -2
  102. package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -2
  103. package/packages/dd-trace/src/llmobs/constants/tags.js +8 -1
  104. package/packages/dd-trace/src/llmobs/index.js +2 -2
  105. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  106. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +3 -4
  107. package/packages/dd-trace/src/llmobs/sdk.js +33 -6
  108. package/packages/dd-trace/src/llmobs/span_processor.js +17 -7
  109. package/packages/dd-trace/src/llmobs/tagger.js +175 -1
  110. package/packages/dd-trace/src/llmobs/writers/base.js +118 -45
  111. package/packages/dd-trace/src/llmobs/writers/spans.js +4 -3
  112. package/packages/dd-trace/src/llmobs/writers/util.js +3 -9
  113. package/packages/dd-trace/src/log/index.js +50 -35
  114. package/packages/dd-trace/src/log/writer.js +13 -78
  115. package/packages/dd-trace/src/noop/proxy.js +3 -3
  116. package/packages/dd-trace/src/openfeature/writers/base.js +9 -16
  117. package/packages/dd-trace/src/openfeature/writers/util.js +3 -8
  118. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +2 -2
  119. package/packages/dd-trace/src/opentelemetry/tracer.js +48 -6
  120. package/packages/dd-trace/src/opentracing/propagation/text_map.js +45 -21
  121. package/packages/dd-trace/src/opentracing/span.js +4 -4
  122. package/packages/dd-trace/src/plugin_manager.js +8 -6
  123. package/packages/dd-trace/src/plugins/util/ci.js +5 -8
  124. package/packages/dd-trace/src/plugins/util/git-cache.js +3 -3
  125. package/packages/dd-trace/src/plugins/util/test.js +1 -1
  126. package/packages/dd-trace/src/plugins/util/user-provided-git.js +41 -43
  127. package/packages/dd-trace/src/profiler.js +4 -39
  128. package/packages/dd-trace/src/profiling/config.js +74 -34
  129. package/packages/dd-trace/src/profiling/exporter_cli.js +5 -5
  130. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  131. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +9 -2
  132. package/packages/dd-trace/src/profiling/index.js +1 -1
  133. package/packages/dd-trace/src/profiling/libuv-size.js +1 -1
  134. package/packages/dd-trace/src/profiling/profiler.js +57 -2
  135. package/packages/dd-trace/src/proxy.js +34 -5
  136. package/packages/dd-trace/src/remote_config/capabilities.js +4 -0
  137. package/packages/dd-trace/src/remote_config/index.js +2 -7
  138. package/packages/dd-trace/src/ritm.js +8 -4
  139. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -2
  140. package/packages/dd-trace/src/serverless.js +2 -2
  141. package/packages/dd-trace/src/span_processor.js +2 -2
  142. package/packages/dd-trace/src/startup-log.js +7 -16
  143. package/packages/dd-trace/src/telemetry/endpoints.js +67 -5
  144. package/packages/dd-trace/src/telemetry/send-data.js +103 -4
  145. package/packages/dd-trace/src/telemetry/telemetry.js +229 -110
  146. package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
  147. package/vendor/dist/esquery/index.js +1 -1
  148. package/vendor/dist/meriyah/index.js +1 -1
  149. package/vendor/dist/protobufjs/index.js +1 -1
  150. /package/packages/dd-trace/src/{git_properties.js → config/git_properties.js} +0 -0
@@ -3,10 +3,8 @@
3
3
  const logger = require('../../log')
4
4
  const { EVP_PROXY_AGENT_BASE_PATH } = require('../constants/writers')
5
5
  const telemetry = require('../telemetry')
6
-
7
- const AgentInfoExporter = require('../../exporters/common/agent-info-exporter')
8
- /** @type {AgentInfoExporter} */
9
- let agentInfoExporter
6
+ const { fetchAgentInfo } = require('../../agent/info')
7
+ const { getAgentUrl } = require('../../agent/url')
10
8
 
11
9
  function setAgentStrategy (config, setWritersAgentlessValue) {
12
10
  const agentlessEnabled = config.llmobs.agentlessEnabled
@@ -16,11 +14,7 @@ function setAgentStrategy (config, setWritersAgentlessValue) {
16
14
  return
17
15
  }
18
16
 
19
- if (!agentInfoExporter) {
20
- agentInfoExporter = new AgentInfoExporter(config)
21
- }
22
-
23
- agentInfoExporter.getAgentInfo((err, agentInfo) => {
17
+ fetchAgentInfo(getAgentUrl(config), (err, agentInfo) => {
24
18
  if (err) {
25
19
  setWritersAgentlessValue(true)
26
20
  return
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
  const { inspect } = require('util')
3
3
  const { isTrue } = require('../util')
4
- const { getEnvironmentVariable } = require('../config-helper')
4
+ const { getValueFromEnvSources } = require('../config/helper')
5
5
  const { traceChannel, debugChannel, infoChannel, warnChannel, errorChannel } = require('./channels')
6
6
  const logWriter = require('./writer')
7
7
  const { Log, LogConfig, NoTransmitError } = require('./log')
@@ -31,31 +31,31 @@ const log = {
31
31
  use (logger) {
32
32
  config.logger = logger
33
33
  logWriter.use(logger)
34
- return this
34
+ return log
35
35
  },
36
36
 
37
37
  toggle (enabled, logLevel) {
38
38
  config.enabled = enabled
39
39
  config.logLevel = logLevel
40
40
  logWriter.toggle(enabled, logLevel)
41
- return this
41
+ return log
42
42
  },
43
43
 
44
44
  reset () {
45
45
  logWriter.reset()
46
- this._deprecate = memoize((code, message) => {
47
- errorChannel.publish(Log.parse(message))
46
+ log._deprecate = memoize((code, message) => {
47
+ publishFormatted(errorChannel, null, message)
48
48
  return true
49
49
  })
50
50
 
51
- return this
51
+ return log
52
52
  },
53
53
 
54
54
  trace (...args) {
55
55
  if (traceChannel.hasSubscribers) {
56
56
  const logRecord = {}
57
57
 
58
- Error.captureStackTrace(logRecord, this.trace)
58
+ Error.captureStackTrace(logRecord, log.trace)
59
59
 
60
60
  const stack = logRecord.stack.split('\n')
61
61
  const fn = stack[1].replace(/^\s+at ([^\s]+) .+/, '$1')
@@ -64,56 +64,53 @@ const log = {
64
64
 
65
65
  stack[0] = `Trace: ${fn}(${params})`
66
66
 
67
- traceChannel.publish(Log.parse(stack.join('\n')))
67
+ publishFormatted(traceChannel, null, stack.join('\n'))
68
68
  }
69
- return this
69
+ return log
70
70
  },
71
71
 
72
72
  debug (...args) {
73
- if (debugChannel.hasSubscribers) {
74
- debugChannel.publish(Log.parse(...args))
75
- }
76
- return this
73
+ publishFormatted(debugChannel, null, ...args)
74
+ return log
77
75
  },
78
76
 
79
77
  info (...args) {
80
- if (infoChannel.hasSubscribers) {
81
- infoChannel.publish(Log.parse(...args))
82
- }
83
- return this
78
+ publishFormatted(infoChannel, null, ...args)
79
+ return log
84
80
  },
85
81
 
86
82
  warn (...args) {
87
- if (warnChannel.hasSubscribers) {
88
- warnChannel.publish(Log.parse(...args))
89
- }
90
- return this
83
+ publishFormatted(warnChannel, null, ...args)
84
+ return log
91
85
  },
92
86
 
93
87
  error (...args) {
94
- if (errorChannel.hasSubscribers) {
95
- errorChannel.publish(Log.parse(...args))
96
- }
97
- return this
88
+ publishFormatted(errorChannel, formatted => {
89
+ const stackTraceLimitBackup = Error.stackTraceLimit
90
+ Error.stackTraceLimit = 0
91
+ const newError = new Error(formatted)
92
+ Error.stackTraceLimit = stackTraceLimitBackup
93
+ Error.captureStackTrace(newError, log.error)
94
+ return newError
95
+ }, ...args)
96
+ return log
98
97
  },
99
98
 
100
99
  errorWithoutTelemetry (...args) {
101
100
  args.push(NO_TRANSMIT)
102
- if (errorChannel.hasSubscribers) {
103
- errorChannel.publish(Log.parse(...args))
104
- }
105
- return this
101
+ publishFormatted(errorChannel, null, ...args)
102
+ return log
106
103
  },
107
104
 
108
105
  deprecate (code, message) {
109
- return this._deprecate(code, message)
106
+ return log._deprecate(code, message)
110
107
  },
111
108
 
112
109
  isEnabled (fleetStableConfigValue, localStableConfigValue) {
113
110
  return isTrue(
114
111
  fleetStableConfigValue ??
115
- getEnvironmentVariable('DD_TRACE_DEBUG') ??
116
- (getEnvironmentVariable('OTEL_LOG_LEVEL') === 'debug' || undefined) ??
112
+ getValueFromEnvSources('DD_TRACE_DEBUG') ??
113
+ (getValueFromEnvSources('OTEL_LOG_LEVEL') === 'debug' || undefined) ??
117
114
  localStableConfigValue ??
118
115
  config.enabled
119
116
  )
@@ -126,14 +123,32 @@ const log = {
126
123
  ) {
127
124
  return optionsValue ??
128
125
  fleetStableConfigValue ??
129
- getEnvironmentVariable('DD_TRACE_LOG_LEVEL') ??
130
- getEnvironmentVariable('OTEL_LOG_LEVEL') ??
126
+ getValueFromEnvSources('DD_TRACE_LOG_LEVEL') ??
127
+ getValueFromEnvSources('OTEL_LOG_LEVEL') ??
131
128
  localStableConfigValue ??
132
129
  config.logLevel
133
130
  }
134
131
  }
135
132
 
136
- logWriter.setStackTraceLimitFunction(log.error)
133
+ function publishFormatted (ch, formatter, ...args) {
134
+ if (ch.hasSubscribers) {
135
+ const log = Log.parse(...args)
136
+ const { formatted, cause } = getErrorLog(log)
137
+
138
+ // calling twice ch.publish() because Error cause is only available in Node.js v16.9.0
139
+ // TODO: replace it with Error(message, { cause }) when cause has broad support
140
+ if (formatted) ch.publish(formatter?.(formatted) || formatted)
141
+ if (cause) ch.publish(cause)
142
+ }
143
+ }
144
+
145
+ function getErrorLog (err) {
146
+ if (typeof err?.delegate === 'function') {
147
+ const result = err.delegate()
148
+ return Array.isArray(result) ? Log.parse(...result) : Log.parse(result)
149
+ }
150
+ return err
151
+ }
137
152
 
138
153
  log.reset()
139
154
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { storage } = require('../../../datadog-core')
4
4
  const { LogChannel } = require('./channels')
5
- const { Log } = require('./log')
6
5
  const defaultLogger = {
7
6
  debug: msg => console.debug(msg), /* eslint-disable-line no-console */
8
7
  info: msg => console.info(msg), /* eslint-disable-line no-console */
@@ -13,7 +12,6 @@ const defaultLogger = {
13
12
  let enabled = false
14
13
  let logger = defaultLogger
15
14
  let logChannel = new LogChannel()
16
- let stackTraceLimitFunction = onError
17
15
 
18
16
  function withNoop (fn) {
19
17
  const store = storage('legacy').getStore()
@@ -24,7 +22,7 @@ function withNoop (fn) {
24
22
  }
25
23
 
26
24
  function unsubscribeAll () {
27
- logChannel.unsubscribe({ trace: onTrace, debug: onDebug, info: onInfo, warn: onWarn, error: onError })
25
+ logChannel.unsubscribe({ trace, debug, info, warn, error })
28
26
  }
29
27
 
30
28
  function toggleSubscription (enable, level) {
@@ -32,7 +30,7 @@ function toggleSubscription (enable, level) {
32
30
 
33
31
  if (enable) {
34
32
  logChannel = new LogChannel(level)
35
- logChannel.subscribe({ trace: onTrace, debug: onDebug, info: onInfo, warn: onWarn, error: onError })
33
+ logChannel.subscribe({ trace, debug, info, warn, error })
36
34
  }
37
35
  }
38
36
 
@@ -53,89 +51,26 @@ function reset () {
53
51
  toggleSubscription(false)
54
52
  }
55
53
 
56
- function getErrorLog (err) {
57
- if (typeof err?.delegate === 'function') {
58
- const result = err.delegate()
59
- return Array.isArray(result) ? Log.parse(...result) : Log.parse(result)
60
- }
61
- return err
62
- }
63
-
64
- function setStackTraceLimitFunction (fn) {
65
- if (typeof fn !== 'function') {
66
- throw new TypeError('stackTraceLimitFunction must be a function')
67
- }
68
- stackTraceLimitFunction = fn
69
- }
70
-
71
- function onError (err) {
72
- const { formatted, cause } = getErrorLog(err)
73
-
74
- // calling twice logger.error() because Error cause is only available in Node.js v16.9.0
75
- // TODO: replace it with Error(message, { cause }) when cause has broad support
76
- if (formatted) {
77
- withNoop(() => {
78
- const stackTraceLimitBackup = Error.stackTraceLimit
79
- Error.stackTraceLimit = 0
80
- const newError = new Error(formatted)
81
- Error.stackTraceLimit = stackTraceLimitBackup
82
- Error.captureStackTrace(newError, stackTraceLimitFunction)
83
- logger.error(newError)
84
- })
85
- }
86
- if (cause) withNoop(() => logger.error(cause))
54
+ function error (err) {
55
+ withNoop(() => logger.error(err))
87
56
  }
88
57
 
89
- function onWarn (log) {
90
- const { formatted, cause } = getErrorLog(log)
91
- if (formatted) withNoop(() => logger.warn(formatted))
92
- if (cause) withNoop(() => logger.warn(cause))
58
+ function warn (log) {
59
+ withNoop(() => logger.warn ? logger.warn(log) : logger.debug(log))
93
60
  }
94
61
 
95
- function onInfo (log) {
96
- const { formatted, cause } = getErrorLog(log)
97
- if (formatted) withNoop(() => logger.info(formatted))
98
- if (cause) withNoop(() => logger.info(cause))
62
+ function info (log) {
63
+ withNoop(() => logger.info ? logger.info(log) : logger.debug(log))
99
64
  }
100
65
 
101
- function onDebug (log) {
102
- const { formatted, cause } = getErrorLog(log)
103
- if (formatted) withNoop(() => logger.debug(formatted))
104
- if (cause) withNoop(() => logger.debug(cause))
66
+ function debug (log) {
67
+ withNoop(() => logger.debug(log))
105
68
  }
106
69
 
107
- function onTrace (log) {
108
- const { formatted, cause } = getErrorLog(log)
70
+ function trace (log) {
109
71
  // Using logger.debug() because not all loggers have trace level,
110
72
  // and console.trace() has a completely different meaning.
111
- if (formatted) withNoop(() => logger.debug(formatted))
112
- if (cause) withNoop(() => logger.debug(cause))
113
- }
114
-
115
- function error (...args) {
116
- onError(Log.parse(...args))
117
- }
118
-
119
- function warn (...args) {
120
- const log = Log.parse(...args)
121
- if (!logger.warn) return onDebug(log)
122
-
123
- onWarn(log)
124
- }
125
-
126
- function info (...args) {
127
- const log = Log.parse(...args)
128
- if (!logger.info) return onDebug(log)
129
-
130
- onInfo(log)
131
- }
132
-
133
- function debug (...args) {
134
- onDebug(Log.parse(...args))
135
- }
136
-
137
- function trace (...args) {
138
- onTrace(Log.parse(...args))
73
+ withNoop(() => logger.debug(log))
139
74
  }
140
75
 
141
- module.exports = { use, toggle, reset, error, warn, info, debug, trace, setStackTraceLimitFunction }
76
+ module.exports = { use, toggle, reset, error, warn, info, debug, trace }
@@ -23,10 +23,10 @@ class NoopProxy {
23
23
  this.llmobs = noopLLMObs
24
24
  this.openfeature = noopOpenFeatureProvider
25
25
  this.aiguard = noopAIGuard
26
- this.setBaggageItem = () => {}
27
- this.getBaggageItem = () => {}
26
+ this.setBaggageItem = (key, value) => {}
27
+ this.getBaggageItem = (key) => {}
28
28
  this.getAllBaggageItems = () => {}
29
- this.removeBaggageItem = () => {}
29
+ this.removeBaggageItem = (keyToRemove) => {}
30
30
  this.removeAllBaggageItems = () => {}
31
31
  }
32
32
 
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const { URL, format } = require('node:url')
4
3
  const request = require('../../exporters/common/request')
5
4
  const { safeJSONStringify } = require('../../exporters/common/util')
5
+ const { getAgentUrl } = require('../../agent/url')
6
6
 
7
7
  const log = require('../../log')
8
8
 
@@ -23,6 +23,7 @@ const log = require('../../log')
23
23
  * @class BaseFFEWriter
24
24
  */
25
25
  class BaseFFEWriter {
26
+ #destroyer
26
27
  /**
27
28
  * @param {BaseFFEWriterOptions} options - Writer configuration options
28
29
  */
@@ -56,12 +57,10 @@ class BaseFFEWriter {
56
57
  this.flush()
57
58
  }, this._interval).unref()
58
59
 
59
- this._beforeExitHandler = () => {
60
- this.destroy()
61
- }
62
- process.once('beforeExit', this._beforeExitHandler)
60
+ const destroyer = this.destroy.bind(this)
61
+ globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(destroyer)
63
62
 
64
- this._destroyed = false
63
+ this.#destroyer = destroyer
65
64
  this._droppedEvents = 0
66
65
  }
67
66
 
@@ -141,12 +140,12 @@ class BaseFFEWriter {
141
140
  * Cleans up resources and flushes remaining events
142
141
  */
143
142
  destroy () {
144
- if (!this._destroyed) {
143
+ if (this.#destroyer) {
145
144
  log.debug(() => `Stopping ${this.constructor.name}`)
146
145
  clearInterval(this._periodic)
147
- process.removeListener('beforeExit', this._beforeExitHandler)
148
146
  this.flush()
149
- this._destroyed = true
147
+ globalThis[Symbol.for('dd-trace')].beforeExitHandlers.delete(this.#destroyer)
148
+ this.#destroyer = undefined
150
149
 
151
150
  if (this._droppedEvents > 0) {
152
151
  log.warn(`${this.constructor.name} dropped ${this._droppedEvents} events due to buffer overflow`)
@@ -159,13 +158,7 @@ class BaseFFEWriter {
159
158
  * @returns {URL} Constructs agent URL from config
160
159
  */
161
160
  _getAgentUrl () {
162
- const { hostname, port } = this._config
163
-
164
- return this._config.url ?? new URL(format({
165
- protocol: 'http:',
166
- hostname: hostname || 'localhost',
167
- port: port || 8126
168
- }))
161
+ return getAgentUrl(this._config)
169
162
  }
170
163
 
171
164
  /**
@@ -2,9 +2,8 @@
2
2
 
3
3
  const logger = require('../../log')
4
4
  const { EVP_PROXY_AGENT_BASE_PATH } = require('../constants/constants')
5
-
6
- const AgentInfoExporter = require('../../exporters/common/agent-info-exporter')
7
- let agentInfoExporter
5
+ const { fetchAgentInfo } = require('../../agent/info')
6
+ const { getAgentUrl } = require('../../agent/url')
8
7
 
9
8
  /**
10
9
  * Determines if the agent supports EVP proxy and sets the writer enabled state accordingly
@@ -12,11 +11,7 @@ let agentInfoExporter
12
11
  * @param {Function} setWriterEnabledValue - Callback to set the writer enabled state
13
12
  */
14
13
  function setAgentStrategy (config, setWriterEnabledValue) {
15
- if (!agentInfoExporter) {
16
- agentInfoExporter = new AgentInfoExporter(config)
17
- }
18
-
19
- agentInfoExporter.getAgentInfo((err, agentInfo) => {
14
+ fetchAgentInfo(getAgentUrl(config), (err, agentInfo) => {
20
15
  if (err) {
21
16
  logger.debug('FFE Writer disabled - error getting agent info:', err.message)
22
17
  setWriterEnabledValue(false)
@@ -90,7 +90,7 @@ class OtlpHttpExporterBase {
90
90
  data += chunk
91
91
  })
92
92
 
93
- res.on('end', () => {
93
+ res.once('end', () => {
94
94
  if (res.statusCode >= 200 && res.statusCode < 300) {
95
95
  resultCallback({ code: 0 })
96
96
  } else {
@@ -105,7 +105,7 @@ class OtlpHttpExporterBase {
105
105
  resultCallback({ code: 1, error })
106
106
  })
107
107
 
108
- req.on('timeout', () => {
108
+ req.once('timeout', () => {
109
109
  req.destroy()
110
110
  const error = new Error('Request timeout')
111
111
  resultCallback({ code: 1, error })
@@ -5,12 +5,48 @@ const { sanitizeAttributes } = require('../../../../vendor/dist/@opentelemetry/c
5
5
 
6
6
  const id = require('../id')
7
7
  const log = require('../log')
8
+ const DatadogSpanContext = require('../opentracing/span_context')
8
9
  const TextMapPropagator = require('../opentracing/propagation/text_map')
9
10
  const TraceState = require('../opentracing/propagation/tracestate')
10
11
  const SpanContext = require('./span_context')
11
12
  const Span = require('./span')
12
13
  const Sampler = require('./sampler')
13
14
 
15
+ function normalizeLinkContext (context) {
16
+ if (!context) return
17
+
18
+ // OTel API bridge SpanContext wrapper
19
+ if (context._ddContext) return context._ddContext
20
+
21
+ // Datadog span context
22
+ if (typeof context.toTraceId === 'function' && typeof context.toSpanId === 'function') {
23
+ return context
24
+ }
25
+
26
+ // Standard OTel SpanContext (traceId/spanId)
27
+ if (typeof context.traceId !== 'string' || typeof context.spanId !== 'string') {
28
+ // Invalid
29
+ return
30
+ }
31
+
32
+ let sampling
33
+ if (typeof context.traceFlags === 'number') {
34
+ sampling = { priority: context.traceFlags & 1 }
35
+ }
36
+
37
+ let tracestate
38
+ if (context.traceState?.serialize) {
39
+ tracestate = TraceState.fromString(context.traceState.serialize())
40
+ }
41
+
42
+ return new DatadogSpanContext({
43
+ traceId: id(context.traceId, 16),
44
+ spanId: id(context.spanId, 16),
45
+ sampling,
46
+ tracestate
47
+ })
48
+ }
49
+
14
50
  class Tracer {
15
51
  constructor (library, config, tracerProvider) {
16
52
  this._sampler = new Sampler()
@@ -96,7 +132,7 @@ class Tracer {
96
132
  context = api.trace.deleteSpan(context)
97
133
  }
98
134
  const parentSpan = api.trace.getSpan(context)
99
- const parentSpanContext = parentSpan && parentSpan.spanContext()
135
+ const parentSpanContext = parentSpan?.spanContext()
100
136
  let spanContext
101
137
  if (parentSpanContext && api.trace.isSpanContextValid(parentSpanContext)) {
102
138
  spanContext = parentSpanContext._ddContext
@@ -107,12 +143,18 @@ class Tracer {
107
143
  }
108
144
 
109
145
  const spanKind = options.kind || api.SpanKind.INTERNAL
110
- const links = (options.links || []).map(link => {
111
- return {
112
- context: link.context,
113
- attributes: sanitizeAttributes(link.attributes)
146
+ const links = []
147
+ if (options.links?.length) {
148
+ for (const link of options.links) {
149
+ const ddContext = normalizeLinkContext(link?.context)
150
+ if (!ddContext) continue
151
+
152
+ links.push({
153
+ context: ddContext,
154
+ attributes: sanitizeAttributes(link.attributes)
155
+ })
114
156
  }
115
- })
157
+ }
116
158
  const attributes = sanitizeAttributes(options.attributes)
117
159
 
118
160
  // TODO: sampling API is not yet supported
@@ -34,11 +34,19 @@ const b3HeaderKey = 'b3'
34
34
  const sqsdHeaderHey = 'x-aws-sqsd-attr-_datadog'
35
35
  const b3HeaderExpr = /^(([0-9a-f]{16}){1,2}-[0-9a-f]{16}(-[01d](-[0-9a-f]{16})?)?|[01d])$/i
36
36
  const baggageExpr = new RegExp(`^${baggagePrefix}(.+)$`)
37
+ // W3C Baggage key grammar: key = token (RFC 7230).
38
+ // Spec (up-to-date): "Propagation format for distributed context: Baggage" §3.3.1
39
+ // https://www.w3.org/TR/baggage/#header-content
40
+ // https://www.rfc-editor.org/rfc/rfc7230#section-3.2.6
41
+ const baggageTokenExpr = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/
37
42
  const tagKeyExpr = /^_dd\.p\.[\x21-\x2B\x2D-\x7E]+$/ // ASCII minus spaces and commas
38
43
  const tagValueExpr = /^[\x20-\x2B\x2D-\x7E]*$/ // ASCII minus commas
39
44
  // RFC7230 token (used by HTTP header field-name) and compatible with Node's header name validation.
40
45
  // See https://www.rfc-editor.org/rfc/rfc7230#section-3.2.6
41
46
  const httpHeaderNameExpr = /^[0-9A-Za-z!#$%&'*+\-.^_`|~]+$/
47
+ // Compatible with Node's internal header value validation (allows HTAB, SP-~, and \x80-\xFF only)
48
+ // https://github.com/nodejs/node/blob/main/lib/_http_common.js
49
+ const invalidHeaderValueCharExpr = /[^\t\x20-\x7E\x80-\xFF]/
42
50
  const traceparentExpr = /^([a-f0-9]{2})-([a-f0-9]{32})-([a-f0-9]{16})-([a-f0-9]{2})(-.*)?$/i
43
51
  const traceparentKey = 'traceparent'
44
52
  const tracestateKey = 'tracestate'
@@ -124,13 +132,6 @@ class TextMapPropagator {
124
132
  }
125
133
  }
126
134
 
127
- _encodeOtelBaggageKey (key) {
128
- let encoded = encodeURIComponent(key)
129
- encoded = encoded.replaceAll('(', '%28')
130
- encoded = encoded.replaceAll(')', '%29')
131
- return encoded
132
- }
133
-
134
135
  _injectBaggageItems (spanContext, carrier) {
135
136
  if (this._config.legacyBaggageEnabled) {
136
137
  const baggageItems = spanContext?._baggageItems
@@ -145,7 +146,12 @@ class TextMapPropagator {
145
146
  continue
146
147
  }
147
148
 
148
- carrier[headerName] = String(baggageItems[key])
149
+ let headerValue = String(baggageItems[key])
150
+ // Avoid Node throwing ERR_INVALID_CHAR when setting header values (e.g. newline from decoded OTEL baggage).
151
+ if (invalidHeaderValueCharExpr.test(headerValue)) {
152
+ headerValue = encodeURIComponent(headerValue)
153
+ }
154
+ carrier[headerName] = headerValue
149
155
  }
150
156
  }
151
157
  }
@@ -157,7 +163,13 @@ class TextMapPropagator {
157
163
  const baggageItems = getAllBaggageItems()
158
164
  if (!baggageItems) return
159
165
  for (const [key, value] of Object.entries(baggageItems)) {
160
- const item = `${this._encodeOtelBaggageKey(String(key).trim())}=${encodeURIComponent(String(value).trim())},`
166
+ const baggageKey = String(key).trim()
167
+ if (!baggageKey || !baggageTokenExpr.test(baggageKey)) continue
168
+
169
+ // Do not trim values. If callers include leading/trailing whitespace, it must be percent-encoded.
170
+ // W3C list-member allows optional properties after ';'.
171
+ // https://www.w3.org/TR/baggage/#header-content
172
+ const item = `${baggageKey}=${encodeURIComponent(String(value))},`
161
173
  itemCounter += 1
162
174
  byteCounter += Buffer.byteLength(item)
163
175
 
@@ -640,13 +652,6 @@ class TextMapPropagator {
640
652
  }
641
653
  }
642
654
 
643
- _decodeOtelBaggageKey (key) {
644
- let decoded = decodeURIComponent(key)
645
- decoded = decoded.replaceAll('%28', '(')
646
- decoded = decoded.replaceAll('%29', ')')
647
- return decoded
648
- }
649
-
650
655
  _extractLegacyBaggageItems (carrier, spanContext) {
651
656
  if (this._config.legacyBaggageEnabled) {
652
657
  Object.keys(carrier).forEach(key => {
@@ -668,19 +673,38 @@ class TextMapPropagator {
668
673
  ? undefined
669
674
  : new Set(this._config.baggageTagKeys.split(','))
670
675
  for (const keyValue of baggages) {
671
- if (!keyValue.includes('=')) {
676
+ if (!keyValue) continue
677
+
678
+ // Per W3C baggage, list-members can contain optional properties after `;`.
679
+ // Example: key=value;prop=1;prop2
680
+ // https://www.w3.org/TR/baggage/#header-content
681
+ const semicolonIdx = keyValue.indexOf(';')
682
+ const member = (semicolonIdx === -1 ? keyValue : keyValue.slice(0, semicolonIdx)).trim()
683
+ if (!member) continue
684
+
685
+ const eqIdx = member.indexOf('=')
686
+ if (eqIdx === -1) {
672
687
  tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
673
688
  removeAllBaggageItems()
674
689
  return
675
690
  }
676
- let [key, value] = keyValue.split('=')
677
- key = this._decodeOtelBaggageKey(key.trim())
678
- value = decodeURIComponent(value.trim())
679
- if (!key || !value) {
691
+
692
+ const key = member.slice(0, eqIdx).trim()
693
+ let value = member.slice(eqIdx + 1).trim()
694
+
695
+ if (!baggageTokenExpr.test(key) || !value) {
680
696
  tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
681
697
  removeAllBaggageItems()
682
698
  return
683
699
  }
700
+ try {
701
+ value = decodeURIComponent(value)
702
+ } catch {
703
+ tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
704
+ removeAllBaggageItems()
705
+ return
706
+ }
707
+
684
708
  if (spanContext && (tagAllKeys || keysToSpanTag?.has(key))) {
685
709
  spanContext._trace.tags['baggage.' + key] = value
686
710
  }
@@ -12,18 +12,18 @@ const runtimeMetrics = require('../runtime_metrics')
12
12
  const log = require('../log')
13
13
  const { storage } = require('../../../datadog-core')
14
14
  const telemetryMetrics = require('../telemetry/metrics')
15
- const { getEnvironmentVariable } = require('../config-helper')
15
+ const { getValueFromEnvSources } = require('../config/helper')
16
16
  const SpanContext = require('./span_context')
17
17
 
18
18
  const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
19
19
 
20
- const DD_TRACE_EXPERIMENTAL_STATE_TRACKING = getEnvironmentVariable('DD_TRACE_EXPERIMENTAL_STATE_TRACKING')
21
- const DD_TRACE_EXPERIMENTAL_SPAN_COUNTS = getEnvironmentVariable('DD_TRACE_EXPERIMENTAL_SPAN_COUNTS')
20
+ const DD_TRACE_EXPERIMENTAL_STATE_TRACKING = getValueFromEnvSources('DD_TRACE_EXPERIMENTAL_STATE_TRACKING')
21
+ const DD_TRACE_EXPERIMENTAL_SPAN_COUNTS = getValueFromEnvSources('DD_TRACE_EXPERIMENTAL_SPAN_COUNTS')
22
22
 
23
23
  const unfinishedRegistry = createRegistry('unfinished')
24
24
  const finishedRegistry = createRegistry('finished')
25
25
 
26
- const OTEL_ENABLED = !!getEnvironmentVariable('DD_TRACE_OTEL_ENABLED')
26
+ const OTEL_ENABLED = !!getValueFromEnvSources('DD_TRACE_OTEL_ENABLED')
27
27
  const ALLOWED = new Set(['string', 'number', 'boolean'])
28
28
 
29
29
  const integrationCounters = {