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
@@ -1,10 +1,313 @@
1
1
  'use strict'
2
2
 
3
- const FetchPlugin = require('../../datadog-plugin-fetch/src/index.js')
3
+ const HttpClientPlugin = require('../../datadog-plugin-http/src/client')
4
+ const { storage } = require('../../datadog-core')
5
+ const tags = require('../../../ext/tags')
6
+ const formats = require('../../../ext/formats')
7
+ const HTTP_HEADERS = formats.HTTP_HEADERS
8
+ const log = require('../../dd-trace/src/log')
9
+ const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
4
10
 
5
- class UndiciPlugin extends FetchPlugin {
11
+ const {
12
+ HTTP_STATUS_CODE,
13
+ HTTP_REQUEST_HEADERS,
14
+ HTTP_RESPONSE_HEADERS,
15
+ } = tags
16
+
17
+ // WeakMap to store span context for native undici request objects
18
+ const requestContexts = new WeakMap()
19
+
20
+ class UndiciPlugin extends HttpClientPlugin {
6
21
  static id = 'undici'
7
22
  static prefix = 'tracing:apm:undici:fetch'
23
+
24
+ constructor (...args) {
25
+ super(...args)
26
+
27
+ // Subscribe to native undici diagnostic channels for undici >= 4.7.0
28
+ // These channels fire for ALL undici requests (fetch, request, stream, etc.)
29
+ this.addSub('undici:request:create', this.#onNativeRequestCreate.bind(this))
30
+ this.addSub('undici:request:headers', this.#onNativeRequestHeaders.bind(this))
31
+ this.addSub('undici:request:trailers', this.#onNativeRequestTrailers.bind(this))
32
+ this.addSub('undici:request:error', this.#onNativeRequestError.bind(this))
33
+ }
34
+
35
+ // ===========================================
36
+ // Native undici diagnostic channel handlers
37
+ // These fire for undici >= 4.7.0 for ALL request types (fetch, request, stream, etc.)
38
+ // ===========================================
39
+
40
+ #onNativeRequestCreate ({ request }) {
41
+ if (!request) return
42
+
43
+ const store = storage('legacy').getStore()
44
+ const { origin = '', path = '/' } = request
45
+ const method = request.method?.toUpperCase() ?? 'GET'
46
+
47
+ // Parse origin to extract protocol, hostname, port
48
+ let protocol = 'http:'
49
+ let hostname = 'localhost'
50
+ let port = ''
51
+
52
+ try {
53
+ const url = new URL(origin)
54
+ protocol = url.protocol
55
+ hostname = url.hostname
56
+ port = url.port
57
+ } catch {
58
+ // If origin is not a valid URL, use defaults
59
+ }
60
+
61
+ const host = port ? `${hostname}:${port}` : hostname
62
+ const pathname = path.split(/[?#]/)[0]
63
+ const uri = `${protocol}//${host}${pathname}`
64
+
65
+ const allowed = this.config.filter(uri)
66
+ const childOf = store && allowed ? store.span : null
67
+
68
+ const span = this.startSpan(this.operationName(), {
69
+ childOf,
70
+ meta: {
71
+ 'span.kind': 'client',
72
+ 'http.method': method,
73
+ 'http.url': uri,
74
+ 'out.host': hostname
75
+ },
76
+ metrics: {
77
+ [CLIENT_PORT_KEY]: port ? Number.parseInt(port, 10) : undefined
78
+ },
79
+ service: this.serviceName({ pluginConfig: this.config, sessionDetails: { host: hostname, port } }),
80
+ resource: method,
81
+ type: 'http'
82
+ }, false)
83
+
84
+ // Disable recording if not allowed
85
+ if (!allowed) {
86
+ span._spanContext._trace.record = false
87
+ }
88
+
89
+ // Capture request headers if configured
90
+ if (request.headers && this.config.headers) {
91
+ addConfiguredHeaders(span, request.headers, this.config.headers, HTTP_REQUEST_HEADERS)
92
+ }
93
+
94
+ // Inject trace headers if propagation is allowed
95
+ if (this.config.propagationFilter(uri)) {
96
+ const headers = {}
97
+ this.tracer.inject(span, HTTP_HEADERS, headers)
98
+
99
+ // Use addHeader if available (undici provides this on the request object)
100
+ if (typeof request.addHeader === 'function') {
101
+ for (const [name, value] of Object.entries(headers)) {
102
+ request.addHeader(name, value)
103
+ }
104
+ }
105
+ }
106
+
107
+ // Store span context for request for later retrieval
108
+ requestContexts.set(request, {
109
+ span,
110
+ store,
111
+ uri
112
+ })
113
+
114
+ // Enter the span context
115
+ storage('legacy').enterWith({ ...store, span })
116
+ }
117
+
118
+ #onNativeRequestHeaders ({ request, response }) {
119
+ const ctx = requestContexts.get(request)
120
+ if (!ctx) return
121
+
122
+ const { span } = ctx
123
+ const statusCode = response?.statusCode
124
+
125
+ if (statusCode) {
126
+ span.setTag(HTTP_STATUS_CODE, statusCode)
127
+
128
+ if (!this.config.validateStatus(statusCode)) {
129
+ span.setTag('error', 1)
130
+ }
131
+ }
132
+
133
+ // Add response headers if configured
134
+ if (response?.headers && this.config.headers) {
135
+ addConfiguredHeaders(span, response.headers, this.config.headers, HTTP_RESPONSE_HEADERS)
136
+ }
137
+ }
138
+
139
+ #onNativeRequestTrailers ({ request }) {
140
+ const ctx = requestContexts.get(request)
141
+ if (!ctx) return
142
+
143
+ const { span, store } = ctx
144
+
145
+ // Call the request hook if configured
146
+ this.config.hooks.request(span, null, null)
147
+
148
+ // Finish the span
149
+ span.finish()
150
+
151
+ // Clean up
152
+ requestContexts.delete(request)
153
+
154
+ // Restore parent store
155
+ if (store) {
156
+ storage('legacy').enterWith(store)
157
+ }
158
+ }
159
+
160
+ #onNativeRequestError ({ request, error }) {
161
+ const ctx = requestContexts.get(request)
162
+ if (!ctx) return
163
+
164
+ const { span, store } = ctx
165
+
166
+ // Don't record AbortError as an error - it's user-initiated cancellation
167
+ if (error && error.name !== 'AbortError') {
168
+ span.setTag('error', error)
169
+ }
170
+
171
+ // Call the request hook if configured
172
+ this.config.hooks.request(span, null, null)
173
+
174
+ // Finish the span
175
+ span.finish()
176
+
177
+ // Clean up
178
+ requestContexts.delete(request)
179
+
180
+ // Restore parent store
181
+ if (store) {
182
+ storage('legacy').enterWith(store)
183
+ }
184
+ }
185
+
186
+ // ===========================================
187
+ // Fetch-based tracing channel handlers
188
+ // These handle fetch() for undici < 4.7.0 (before native DC was added)
189
+ // ===========================================
190
+
191
+ bindStart (ctx) {
192
+ const req = ctx.req
193
+ const options = new URL(req.url)
194
+ options.headers = Object.fromEntries(req.headers.entries())
195
+ options.method = req.method
196
+
197
+ ctx.args = { options }
198
+
199
+ const store = super.bindStart(ctx)
200
+
201
+ // Inject trace headers back into the request
202
+ for (const name of Object.keys(options.headers)) {
203
+ if (!req.headers.has(name)) {
204
+ req.headers.set(name, options.headers[name])
205
+ }
206
+ }
207
+
208
+ return store
209
+ }
210
+
211
+ error (ctx) {
212
+ // Don't record AbortError as an error - it's user-initiated cancellation
213
+ if (!ctx.error || ctx.error.name !== 'AbortError') {
214
+ return super.error(ctx)
215
+ }
216
+ }
217
+
218
+ asyncEnd (ctx) {
219
+ ctx.res = ctx.result
220
+ return this.finish(ctx)
221
+ }
222
+
223
+ configure (config) {
224
+ return super.configure(normalizeConfig(config))
225
+ }
226
+ }
227
+
228
+ // Add configured headers to span with appropriate tags
229
+ function addConfiguredHeaders (span, rawHeaders, configuredHeaders, headerType) {
230
+ const headers = normalizeHeaders(rawHeaders)
231
+
232
+ for (const [key, tag] of configuredHeaders) {
233
+ const value = headers[key]
234
+ if (value) {
235
+ span.setTag(tag || `${headerType}.${key}`, value)
236
+ }
237
+ }
8
238
  }
9
239
 
240
+ // Normalize headers to an object, handling different undici formats:
241
+ // - Array format: alternating key-value pairs (undici >= 6.0.0)
242
+ // - String format: HTTP header lines like "key: value\r\n" (undici 5.x)
243
+ // - Object format: already a headers object
244
+ function normalizeHeaders (headers) {
245
+ if (!headers) return {}
246
+
247
+ // String format (undici 5.x): "key: value\r\nkey2: value2\r\n"
248
+ if (typeof headers === 'string') {
249
+ const result = {}
250
+ const lines = headers.split('\r\n')
251
+ for (const line of lines) {
252
+ if (!line) continue
253
+ const colonIndex = line.indexOf(':')
254
+ if (colonIndex > 0) {
255
+ const key = line.slice(0, colonIndex).toLowerCase().trim()
256
+ const value = line.slice(colonIndex + 1).trim()
257
+ result[key] = value
258
+ }
259
+ }
260
+ return result
261
+ }
262
+
263
+ // Array format (undici >= 6.0.0): alternating key-value pairs
264
+ if (Array.isArray(headers)) {
265
+ const result = {}
266
+ for (let i = 0; i < headers.length; i += 2) {
267
+ const key = headers[i]
268
+ if (typeof key === 'string') {
269
+ result[key.toLowerCase()] = headers[i + 1]
270
+ } else if (Buffer.isBuffer(key)) {
271
+ result[key.toString().toLowerCase()] = headers[i + 1]?.toString?.() || headers[i + 1]
272
+ }
273
+ }
274
+ return result
275
+ }
276
+
277
+ // Object format: use as-is
278
+ return headers
279
+ }
280
+
281
+ function normalizeConfig (config) {
282
+ const validateStatus = getStatusValidator(config)
283
+ const hooks = getHooks(config)
284
+
285
+ return {
286
+ ...config,
287
+ validateStatus,
288
+ hooks
289
+ }
290
+ }
291
+
292
+ function getStatusValidator (config) {
293
+ if (typeof config.validateStatus === 'function') {
294
+ return config.validateStatus
295
+ } else if (Object.hasOwn(config, 'validateStatus')) {
296
+ log.error('Expected `validateStatus` to be a function.')
297
+ }
298
+ return defaultValidateStatus
299
+ }
300
+
301
+ function defaultValidateStatus (code) {
302
+ return code < 400 || code >= 500
303
+ }
304
+
305
+ function getHooks (config) {
306
+ const request = config.hooks?.request ?? noop
307
+
308
+ return { request }
309
+ }
310
+
311
+ function noop () {}
312
+
10
313
  module.exports = UndiciPlugin
@@ -2,7 +2,7 @@
2
2
 
3
3
  const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
4
4
  const { storage } = require('../../datadog-core')
5
- const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
5
+ const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
6
6
 
7
7
  const {
8
8
  TEST_STATUS,
@@ -281,11 +281,11 @@ class VitestPlugin extends CiPlugin {
281
281
  this.addBind('ci:vitest:test-suite:start', (ctx) => {
282
282
  const { testSuiteAbsolutePath, frameworkVersion } = ctx
283
283
 
284
- this.command = getEnvironmentVariable('DD_CIVISIBILITY_TEST_COMMAND')
284
+ this.command = getValueFromEnvSources('DD_CIVISIBILITY_TEST_COMMAND')
285
285
  this.frameworkVersion = frameworkVersion
286
286
  const testSessionSpanContext = this.tracer.extract('text_map', {
287
- 'x-datadog-trace-id': getEnvironmentVariable('DD_CIVISIBILITY_TEST_SESSION_ID'),
288
- 'x-datadog-parent-id': getEnvironmentVariable('DD_CIVISIBILITY_TEST_MODULE_ID')
287
+ 'x-datadog-trace-id': getValueFromEnvSources('DD_CIVISIBILITY_TEST_SESSION_ID'),
288
+ 'x-datadog-parent-id': getValueFromEnvSources('DD_CIVISIBILITY_TEST_MODULE_ID')
289
289
  })
290
290
 
291
291
  const trimmedCommand = DD_MAJOR < 6 ? this.command : 'vitest run'
@@ -405,7 +405,7 @@ class VitestPlugin extends CiPlugin {
405
405
  finishAllTraceSpans(this.testSessionSpan)
406
406
  this.telemetry.count(TELEMETRY_TEST_SESSION, {
407
407
  provider: this.ciProviderName,
408
- autoInjected: !!getEnvironmentVariable('DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER')
408
+ autoInjected: !!getValueFromEnvSources('DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER')
409
409
  })
410
410
  this.tracer._exporter.flush(onFinish)
411
411
  })
@@ -76,11 +76,8 @@ function wrapFunction (original, wrapper) {
76
76
  if (typeof original !== 'function') return original
77
77
 
78
78
  const wrapped = wrapper(original)
79
-
80
- if (typeof original === 'function') {
81
- assertNotClass(original)
82
- copyProperties(original, wrapped)
83
- }
79
+ assertNotClass(original)
80
+ copyProperties(original, wrapped)
84
81
 
85
82
  return wrapped
86
83
  }
@@ -10,6 +10,25 @@ if (!global._ddtrace) {
10
10
  writable: true
11
11
  })
12
12
 
13
+ const ddTraceSymbol = Symbol.for('dd-trace')
14
+
15
+ Object.defineProperty(globalThis, ddTraceSymbol, {
16
+ value: {
17
+ beforeExitHandlers: new Set(),
18
+ },
19
+ enumerable: false,
20
+ configurable: true, // Allow this to be overridden by loading the tracer
21
+ writable: false
22
+ })
23
+
24
+ process.once('beforeExit', function mainBeforeExit () {
25
+ if (globalThis[ddTraceSymbol]?.beforeExitHandlers) {
26
+ for (const handler of globalThis[ddTraceSymbol].beforeExitHandlers) {
27
+ handler()
28
+ }
29
+ }
30
+ })
31
+
13
32
  global._ddtrace.default = global._ddtrace
14
33
  global._ddtrace.tracer = global._ddtrace
15
34
  }
@@ -0,0 +1,57 @@
1
+ 'use strict'
2
+
3
+ const request = require('../exporters/common/request')
4
+
5
+ const CACHE_TTL_MS = 60_000 // 1 minute
6
+
7
+ let cachedUrl = null
8
+ let cachedData = null
9
+ let cachedTimestamp = 0
10
+
11
+ module.exports = {
12
+ fetchAgentInfo,
13
+ clearCache // For testing purposes only
14
+ }
15
+
16
+ /**
17
+ * Fetches agent information from the /info endpoint
18
+ * @param {URL} url - The agent URL
19
+ * @param {Function} callback - Callback function with signature (err, agentInfo)
20
+ */
21
+ function fetchAgentInfo (url, callback) {
22
+ const urlKey = url.href
23
+
24
+ if (cachedUrl !== null && cachedUrl !== urlKey) {
25
+ // Clear cache if URL changes
26
+ clearCache()
27
+ } else if (cachedData !== null && (Date.now() - cachedTimestamp) < CACHE_TTL_MS) {
28
+ // Return cached result if still valid
29
+ return process.nextTick(callback, null, cachedData)
30
+ }
31
+
32
+ request('', {
33
+ path: '/info',
34
+ url
35
+ }, (err, res) => {
36
+ if (err) {
37
+ return callback(err)
38
+ }
39
+
40
+ try {
41
+ cachedData = JSON.parse(res)
42
+ } catch (e) {
43
+ return callback(e)
44
+ }
45
+
46
+ cachedUrl = urlKey
47
+ cachedTimestamp = Date.now()
48
+
49
+ callback(null, cachedData)
50
+ })
51
+ }
52
+
53
+ function clearCache () {
54
+ cachedUrl = null
55
+ cachedData = null
56
+ cachedTimestamp = 0
57
+ }
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ const { URL, format } = require('url')
4
+ const defaults = require('../config/defaults')
5
+
6
+ module.exports = { getAgentUrl }
7
+
8
+ // TODO: Investigate merging with the getAgentUrl function in config/index.js which has
9
+ // additional logic for unix socket auto-detection on Linux. The config version is only used
10
+ // during config initialization, while this one is used throughout the codebase. Consider if
11
+ // the unix socket detection should be part of this general helper or remain config-specific.
12
+
13
+ /**
14
+ * Gets the agent URL from config, constructing it from hostname/port if needed
15
+ * @param {ReturnType<import('../config')>} config - Tracer configuration object
16
+ * @returns {URL} The agent URL
17
+ */
18
+ function getAgentUrl (config) {
19
+ const { url, hostname, port } = config
20
+ if (url) {
21
+ return url instanceof URL ? url : new URL(url)
22
+ }
23
+ return new URL(format({
24
+ protocol: 'http:',
25
+ hostname: hostname || defaults.hostname,
26
+ port: port || defaults.port
27
+ }))
28
+ }
@@ -12,7 +12,7 @@ const { getName } = require('../telemetry/verbosity')
12
12
  const telemetry = require('../telemetry')
13
13
  const log = require('../../../log')
14
14
  const orchestrionConfig = require('../../../../../datadog-instrumentations/src/orchestrion-config')
15
- const { getEnvironmentVariable } = require('../../../config-helper')
15
+ const { getEnvironmentVariable } = require('../../../config/helper')
16
16
  const { LOG_MESSAGE, REWRITTEN_MESSAGE } = require('./constants')
17
17
  const { incrementTelemetryIfNeeded } = require('./rewriter-telemetry')
18
18
  const { csiMethods } = require('./csi-methods')
@@ -104,6 +104,8 @@ function enable (_config) {
104
104
  }
105
105
  }
106
106
 
107
+ const analyzedBodies = new WeakSet()
108
+
107
109
  function onRequestBodyParsed ({ req, res, body, abortController }) {
108
110
  if (body === undefined || body === null) return
109
111
 
@@ -120,6 +122,12 @@ function onRequestBodyParsed ({ req, res, body, abortController }) {
120
122
  storedBodies.set(req, body)
121
123
  }
122
124
 
125
+ // eslint-disable-next-line eslint-rules/eslint-safe-typeof-object
126
+ if (typeof body === 'object') {
127
+ if (isEmptyObject(body)) return
128
+ analyzedBodies.add(body)
129
+ }
130
+
123
131
  const results = waf.run({
124
132
  persistent: {
125
133
  [addresses.HTTP_INCOMING_BODY]: body
@@ -129,12 +137,17 @@ function onRequestBodyParsed ({ req, res, body, abortController }) {
129
137
  handleResults(results?.actions, req, res, rootSpan, abortController)
130
138
  }
131
139
 
140
+ const analyzedCookies = new WeakSet()
141
+
132
142
  function onRequestCookieParser ({ req, res, abortController, cookies }) {
133
143
  if (!cookies || typeof cookies !== 'object') return
134
144
 
135
145
  const rootSpan = web.root(req)
136
146
  if (!rootSpan) return
137
147
 
148
+ if (isEmptyObject(cookies)) return
149
+ analyzedCookies.add(cookies)
150
+
138
151
  const results = waf.run({
139
152
  persistent: {
140
153
  [addresses.HTTP_INCOMING_COOKIES]: cookies
@@ -178,19 +191,34 @@ function incomingHttpEndTranslator ({ req, res }) {
178
191
  const persistent = {}
179
192
 
180
193
  // we need to keep this to support other body parsers
181
- // TODO: no need to analyze it if it was already done by the body-parser hook
182
194
  if (req.body !== undefined && req.body !== null) {
183
- persistent[addresses.HTTP_INCOMING_BODY] = req.body
195
+ // eslint-disable-next-line eslint-rules/eslint-safe-typeof-object
196
+ if (typeof req.body === 'object') {
197
+ if (!isEmptyObject(req.body) && !analyzedBodies.has(req.body)) {
198
+ persistent[addresses.HTTP_INCOMING_BODY] = req.body
199
+ }
200
+ } else {
201
+ persistent[addresses.HTTP_INCOMING_BODY] = req.body
202
+ }
184
203
  }
185
204
 
186
205
  // we need to keep this to support other cookie parsers
187
- if (req.cookies !== null && typeof req.cookies === 'object') {
206
+ if (
207
+ req.cookies !== null &&
208
+ typeof req.cookies === 'object' &&
209
+ !isEmptyObject(req.cookies) &&
210
+ !analyzedCookies.has(req.cookies)
211
+ ) {
188
212
  persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies
189
213
  }
190
214
 
191
215
  // we need to keep this to support nextjs
192
216
  const query = req.query
193
- if (query !== null && typeof query === 'object') {
217
+ if (
218
+ query !== null &&
219
+ typeof query === 'object' &&
220
+ !isEmptyObject(query)
221
+ ) {
194
222
  persistent[addresses.HTTP_INCOMING_QUERY] = query
195
223
  }
196
224
 
@@ -198,7 +226,7 @@ function incomingHttpEndTranslator ({ req, res }) {
198
226
  persistent[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
199
227
  }
200
228
 
201
- if (Object.keys(persistent).length) {
229
+ if (!isEmptyObject(persistent)) {
202
230
  waf.run({ persistent }, req)
203
231
  }
204
232
 
@@ -273,6 +301,8 @@ function onRequestQueryParsed ({ req, res, query, abortController }) {
273
301
  const rootSpan = web.root(req)
274
302
  if (!rootSpan) return
275
303
 
304
+ if (isEmptyObject(query)) return
305
+
276
306
  const results = waf.run({
277
307
  persistent: {
278
308
  [addresses.HTTP_INCOMING_QUERY]: query
@@ -286,7 +316,7 @@ function onRequestProcessParams ({ req, res, abortController, params }) {
286
316
  const rootSpan = web.root(req)
287
317
  if (!rootSpan) return
288
318
 
289
- if (!params || typeof params !== 'object' || !Object.keys(params).length) return
319
+ if (!params || typeof params !== 'object' || isEmptyObject(params)) return
290
320
 
291
321
  const results = waf.run({
292
322
  persistent: {
@@ -310,7 +340,7 @@ function onResponseBody ({ req, res, body }) {
310
340
  }
311
341
 
312
342
  function onResponseWriteHead ({ req, res, abortController, statusCode, responseHeaders }) {
313
- if (Object.keys(responseHeaders).length) {
343
+ if (!isEmptyObject(responseHeaders)) {
314
344
  storedResponseHeaders.set(req, responseHeaders)
315
345
  }
316
346
 
@@ -401,6 +431,16 @@ function disable () {
401
431
  if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseSetHeader)
402
432
  }
403
433
 
434
+ // this is faster than Object.keys().length === 0
435
+ function isEmptyObject (obj) {
436
+ // eslint-disable-next-line no-unreachable-loop
437
+ for (const _ in obj) {
438
+ return false
439
+ }
440
+
441
+ return true
442
+ }
443
+
404
444
  module.exports = {
405
445
  enable,
406
446
  disable,
@@ -73,16 +73,14 @@ function handleUncaughtExceptionMonitor (error) {
73
73
  }
74
74
  } else {
75
75
  const cleanUp = removeAllListeners(process, 'uncaughtException')
76
- const handler = () => {
77
- process.removeListener('uncaughtException', handler)
78
- }
76
+ const handler = () => {}
79
77
 
80
78
  setTimeout(() => {
81
79
  process.removeListener('uncaughtException', handler)
82
80
  cleanUp()
83
81
  })
84
82
 
85
- process.on('uncaughtException', handler)
83
+ process.once('uncaughtException', handler)
86
84
  }
87
85
  }
88
86
 
@@ -3,7 +3,11 @@
3
3
  // Modeled after https://github.com/DataDog/libdatadog/blob/f3994857a59bb5679a65967138c5a3aec418a65f/ddcommon/src/azure_app_services.rs
4
4
 
5
5
  const os = require('os')
6
- const { getEnvironmentVariable, getEnvironmentVariables } = require('../../dd-trace/src/config-helper')
6
+ const {
7
+ getEnvironmentVariable,
8
+ getEnvironmentVariables,
9
+ getValueFromEnvSources
10
+ } = require('./config/helper')
7
11
  const { getIsAzureFunction, getIsFlexConsumptionAzureFunction } = require('./serverless')
8
12
 
9
13
  function extractSubscriptionID (ownerName) {
@@ -37,8 +41,6 @@ function trimObject (obj) {
37
41
  function buildMetadata () {
38
42
  const {
39
43
  COMPUTERNAME,
40
- DD_AAS_DOTNET_EXTENSION_VERSION,
41
- DD_AZURE_RESOURCE_GROUP,
42
44
  FUNCTIONS_EXTENSION_VERSION,
43
45
  FUNCTIONS_WORKER_RUNTIME,
44
46
  FUNCTIONS_WORKER_RUNTIME_VERSION,
@@ -49,6 +51,9 @@ function buildMetadata () {
49
51
  WEBSITE_SITE_NAME
50
52
  } = getEnvironmentVariables()
51
53
 
54
+ const DD_AAS_DOTNET_EXTENSION_VERSION = getValueFromEnvSources('DD_AAS_DOTNET_EXTENSION_VERSION')
55
+ const DD_AZURE_RESOURCE_GROUP = getValueFromEnvSources('DD_AZURE_RESOURCE_GROUP')
56
+
52
57
  const subscriptionID = extractSubscriptionID(WEBSITE_OWNER_NAME)
53
58
 
54
59
  const siteName = WEBSITE_SITE_NAME