dd-trace 5.83.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 (44) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/index.d.ts +61 -1
  3. package/package.json +3 -3
  4. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +6 -0
  5. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +1 -0
  6. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +73 -16
  7. package/packages/datadog-instrumentations/src/jest.js +89 -50
  8. package/packages/datadog-instrumentations/src/playwright.js +12 -8
  9. package/packages/datadog-plugin-cucumber/src/index.js +33 -32
  10. package/packages/datadog-plugin-playwright/src/index.js +23 -23
  11. package/packages/datadog-shimmer/src/shimmer.js +2 -5
  12. package/packages/dd-trace/src/agent/info.js +57 -0
  13. package/packages/dd-trace/src/agent/url.js +28 -0
  14. package/packages/dd-trace/src/appsec/index.js +47 -7
  15. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +3 -4
  16. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +3 -3
  17. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +2 -9
  18. package/packages/dd-trace/src/ci-visibility/telemetry.js +6 -2
  19. package/packages/dd-trace/src/config/index.js +15 -1
  20. package/packages/dd-trace/src/config/remote_config.js +1 -0
  21. package/packages/dd-trace/src/config/supported-configurations.json +1 -1
  22. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -5
  23. package/packages/dd-trace/src/datastreams/writer.js +2 -8
  24. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -7
  25. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +10 -11
  26. package/packages/dd-trace/src/dogstatsd.js +3 -9
  27. package/packages/dd-trace/src/exporters/agent/index.js +4 -8
  28. package/packages/dd-trace/src/exporters/agent/writer.js +3 -2
  29. package/packages/dd-trace/src/exporters/common/{agent-info-exporter.js → buffering-exporter.js} +10 -37
  30. package/packages/dd-trace/src/exporters/span-stats/index.js +3 -10
  31. package/packages/dd-trace/src/llmobs/writers/base.js +2 -8
  32. package/packages/dd-trace/src/llmobs/writers/util.js +3 -9
  33. package/packages/dd-trace/src/log/index.js +45 -30
  34. package/packages/dd-trace/src/log/writer.js +13 -78
  35. package/packages/dd-trace/src/openfeature/writers/base.js +2 -8
  36. package/packages/dd-trace/src/openfeature/writers/util.js +3 -8
  37. package/packages/dd-trace/src/profiling/config.js +3 -6
  38. package/packages/dd-trace/src/remote_config/capabilities.js +1 -0
  39. package/packages/dd-trace/src/remote_config/index.js +2 -7
  40. package/packages/dd-trace/src/startup-log.js +2 -2
  41. package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
  42. package/vendor/dist/esquery/index.js +1 -1
  43. package/vendor/dist/meriyah/index.js +1 -1
  44. package/vendor/dist/protobufjs/index.js +1 -1
@@ -6,45 +6,45 @@ const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
6
6
  const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
7
7
 
8
8
  const {
9
- TEST_STATUS,
10
9
  finishAllTraceSpans,
11
- getTestSuitePath,
12
10
  getTestSuiteCommonTags,
13
- TEST_SOURCE_START,
11
+ getTestSuitePath,
12
+ isModifiedTest,
13
+ TEST_BROWSER_NAME,
14
+ TEST_BROWSER_VERSION,
14
15
  TEST_CODE_OWNERS,
15
- TEST_SOURCE_FILE,
16
- TEST_PARAMETERS,
16
+ TEST_COMMAND,
17
+ TEST_EARLY_FLAKE_ABORT_REASON,
18
+ TEST_EARLY_FLAKE_ENABLED,
19
+ TEST_HAS_FAILED_ALL_RETRIES,
20
+ TEST_IS_MODIFIED,
17
21
  TEST_IS_NEW,
18
22
  TEST_IS_RETRY,
19
- TEST_EARLY_FLAKE_ENABLED,
20
- TEST_EARLY_FLAKE_ABORT_REASON,
21
- TELEMETRY_TEST_SESSION,
22
- TEST_RETRY_REASON,
23
- TEST_MANAGEMENT_IS_QUARANTINED,
23
+ TEST_IS_RUM_ACTIVE,
24
+ TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED,
24
25
  TEST_MANAGEMENT_ENABLED,
25
- TEST_BROWSER_NAME,
26
- TEST_MANAGEMENT_IS_DISABLED,
27
26
  TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX,
28
- TEST_HAS_FAILED_ALL_RETRIES,
29
- TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED,
30
- TEST_SESSION_ID,
27
+ TEST_MANAGEMENT_IS_DISABLED,
28
+ TEST_MANAGEMENT_IS_QUARANTINED,
31
29
  TEST_MODULE_ID,
32
- TEST_COMMAND,
33
30
  TEST_MODULE,
34
- TEST_SUITE,
35
- TEST_SUITE_ID,
36
31
  TEST_NAME,
37
- TEST_IS_RUM_ACTIVE,
38
- TEST_BROWSER_VERSION,
32
+ TEST_PARAMETERS,
39
33
  TEST_RETRY_REASON_TYPES,
40
- TEST_IS_MODIFIED,
41
- isModifiedTest
34
+ TEST_RETRY_REASON,
35
+ TEST_SESSION_ID,
36
+ TEST_SOURCE_FILE,
37
+ TEST_SOURCE_START,
38
+ TEST_STATUS,
39
+ TEST_SUITE_ID,
40
+ TEST_SUITE,
42
41
  } = require('../../dd-trace/src/plugins/util/test')
43
42
  const { RESOURCE_NAME } = require('../../../ext/tags')
44
43
  const { COMPONENT } = require('../../dd-trace/src/constants')
45
44
  const {
46
45
  TELEMETRY_EVENT_CREATED,
47
- TELEMETRY_EVENT_FINISHED
46
+ TELEMETRY_EVENT_FINISHED,
47
+ TELEMETRY_TEST_SESSION,
48
48
  } = require('../../dd-trace/src/ci-visibility/telemetry')
49
49
  const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetry')
50
50
  const log = require('../../dd-trace/src/log')
@@ -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
  }
@@ -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
+ }
@@ -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,
@@ -4,6 +4,7 @@ const AgentWriter = require('../../../exporters/agent/writer')
4
4
  const AgentlessWriter = require('../agentless/writer')
5
5
  const CoverageWriter = require('../agentless/coverage-writer')
6
6
  const CiVisibilityExporter = require('../ci-visibility-exporter')
7
+ const { fetchAgentInfo } = require('../../../agent/info')
7
8
 
8
9
  const AGENT_EVP_PROXY_PATH_PREFIX = '/evp_proxy/v'
9
10
  const AGENT_EVP_PROXY_PATH_REGEX = /\/evp_proxy\/v(\d+)\/?/
@@ -42,11 +43,11 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
42
43
  isTestDynamicInstrumentationEnabled
43
44
  } = config
44
45
 
45
- this.getAgentInfo((err, agentInfo) => {
46
+ fetchAgentInfo(this._url, (err, agentInfo) => {
46
47
  this._isInitialized = true
47
48
  let latestEvpProxyVersion = getLatestEvpProxyVersion(err, agentInfo)
48
49
  const isEvpCompatible = latestEvpProxyVersion >= 2
49
- const isGzipCompatible = latestEvpProxyVersion >= 4
50
+ this._isGzipCompatible = latestEvpProxyVersion >= 4
50
51
 
51
52
  // v3 does not work well citestcycle, so we downgrade to v2
52
53
  if (latestEvpProxyVersion === 3) {
@@ -72,7 +73,6 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
72
73
  const DynamicInstrumentationLogsWriter = require('../agentless/di-logs-writer')
73
74
  this._logsWriter = new DynamicInstrumentationLogsWriter({
74
75
  url: this._url,
75
- tags,
76
76
  isAgentProxy: true
77
77
  })
78
78
  this._canForwardLogs = true
@@ -92,7 +92,6 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
92
92
  this._resolveCanUseCiVisProtocol(isEvpCompatible)
93
93
  this.exportUncodedTraces()
94
94
  this.exportUncodedCoverages()
95
- this._isGzipCompatible = isGzipCompatible
96
95
  })
97
96
  }
98
97
 
@@ -11,7 +11,8 @@ const BaseWriter = require('../../../exporters/common/writer')
11
11
  // It is used to encode and send logs to both the logs intake directly and the
12
12
  // `/debugger/v1/input` endpoint in the agent, which is a proxy to the logs intake.
13
13
  class DynamicInstrumentationLogsWriter extends BaseWriter {
14
- constructor ({ url, timeout, isAgentProxy = false }) {
14
+ // TODO: what's a good value for timeout for the logs intake?
15
+ constructor ({ url, timeout = 15_000, isAgentProxy = false }) {
15
16
  super(...arguments)
16
17
  this._url = url
17
18
  this._encoder = new JSONEncoder()
@@ -27,8 +28,7 @@ class DynamicInstrumentationLogsWriter extends BaseWriter {
27
28
  'dd-api-key': getValueFromEnvSources('DD_API_KEY'),
28
29
  'Content-Type': 'application/json'
29
30
  },
30
- // TODO: what's a good value for timeout for the logs intake?
31
- timeout: this.timeout || 15_000,
31
+ timeout: this.timeout,
32
32
  url: this._url
33
33
  }
34
34
 
@@ -8,7 +8,7 @@ const { getKnownTests: getKnownTestsRequest } = require('../early-flake-detectio
8
8
  const { getTestManagementTests: getTestManagementTestsRequest } =
9
9
  require('../test-management/get-test-management-tests')
10
10
  const log = require('../../log')
11
- const AgentInfoExporter = require('../../exporters/common/agent-info-exporter')
11
+ const BufferingExporter = require('../../exporters/common/buffering-exporter')
12
12
  const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../../plugins/util/tags')
13
13
  const { sendGitMetadata: sendGitMetadataRequest } = require('./git/git_metadata')
14
14
 
@@ -34,7 +34,7 @@ function getIsTestSessionTrace (trace) {
34
34
  const GIT_UPLOAD_TIMEOUT = 60_000 // 60 seconds
35
35
  const CAN_USE_CI_VIS_PROTOCOL_TIMEOUT = GIT_UPLOAD_TIMEOUT
36
36
 
37
- class CiVisibilityExporter extends AgentInfoExporter {
37
+ class CiVisibilityExporter extends BufferingExporter {
38
38
  constructor (config) {
39
39
  super(config)
40
40
  this._timer = undefined
@@ -103,10 +103,6 @@ class CiVisibilityExporter extends AgentInfoExporter {
103
103
  )
104
104
  }
105
105
 
106
- shouldRequestLibraryConfiguration () {
107
- return this._config.isIntelligentTestRunnerEnabled
108
- }
109
-
110
106
  canReportSessionTraces () {
111
107
  return this._canUseCiVisProtocol
112
108
  }
@@ -163,9 +159,6 @@ class CiVisibilityExporter extends AgentInfoExporter {
163
159
  getLibraryConfiguration (testConfiguration, callback) {
164
160
  const { repositoryUrl } = testConfiguration
165
161
  this.sendGitMetadata(repositoryUrl)
166
- if (!this.shouldRequestLibraryConfiguration()) {
167
- return callback(null, {})
168
- }
169
162
  this._canUseCiVisProtocolPromise.then((canUseCiVisProtocol) => {
170
163
  if (!canUseCiVisProtocol) {
171
164
  return callback(null, {})
@@ -21,8 +21,12 @@ const formattedTags = {
21
21
 
22
22
  // Transform tags dictionary to array of strings.
23
23
  // If tag value is true, then only tag key is added to the array.
24
+ /**
25
+ * @param {Record<string, unknown>} tagsDictionary
26
+ * @returns {string[]}
27
+ */
24
28
  function formatMetricTags (tagsDictionary) {
25
- return Object.keys(tagsDictionary).reduce((acc, tagKey) => {
29
+ return Object.keys(tagsDictionary).reduce((/** @type {string[]} */ acc, tagKey) => {
26
30
  if (tagKey === 'statusCode') {
27
31
  const statusCode = tagsDictionary[tagKey]
28
32
  if (isStatusCode400(statusCode)) {
@@ -31,7 +35,7 @@ function formatMetricTags (tagsDictionary) {
31
35
  acc.push(`error_type:${getErrorTypeFromStatusCode(statusCode)}`)
32
36
  return acc
33
37
  }
34
- const formattedTagKey = formattedTags[tagKey] || tagKey
38
+ const formattedTagKey = /** @type {string} */(formattedTags[tagKey] || tagKey)
35
39
  if (tagsDictionary[tagKey] === true) {
36
40
  acc.push(formattedTagKey)
37
41
  } else if (tagsDictionary[tagKey] !== undefined && tagsDictionary[tagKey] !== null) {
@@ -600,6 +600,7 @@ class Config {
600
600
  OTEL_TRACES_SAMPLER,
601
601
  OTEL_TRACES_SAMPLER_ARG,
602
602
  DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED,
603
+ DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS,
603
604
  OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
604
605
  OTEL_EXPORTER_OTLP_LOGS_HEADERS,
605
606
  OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
@@ -776,7 +777,15 @@ class Config {
776
777
  maybeFloat(DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS)
777
778
  unprocessedTarget['dynamicInstrumentation.uploadInterval'] = DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS
778
779
  this.#setString(target, 'env', DD_ENV || tags.env)
779
- this.#setBoolean(target, 'experimental.flaggingProvider.enabled', DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED)
780
+ this.#setBoolean(
781
+ target,
782
+ 'experimental.flaggingProvider.enabled',
783
+ DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED
784
+ )
785
+ if (DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS != null) {
786
+ target['experimental.flaggingProvider.initializationTimeoutMs'] =
787
+ maybeInt(DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS)
788
+ }
780
789
  this.#setBoolean(target, 'traceEnabled', DD_TRACE_ENABLED)
781
790
  this.#setBoolean(target, 'experimental.aiguard.enabled', DD_AI_GUARD_ENABLED)
782
791
  this.#setString(target, 'experimental.aiguard.endpoint', DD_AI_GUARD_ENDPOINT)
@@ -1112,6 +1121,11 @@ class Config {
1112
1121
  this.#setBoolean(opts, 'experimental.enableGetRumData', options.experimental?.enableGetRumData)
1113
1122
  this.#setString(opts, 'experimental.exporter', options.experimental?.exporter)
1114
1123
  this.#setBoolean(opts, 'experimental.flaggingProvider.enabled', options.experimental?.flaggingProvider?.enabled)
1124
+ opts['experimental.flaggingProvider.initializationTimeoutMs'] = maybeInt(
1125
+ options.experimental?.flaggingProvider?.initializationTimeoutMs
1126
+ )
1127
+ this.#optsUnprocessed['experimental.flaggingProvider.initializationTimeoutMs'] =
1128
+ options.experimental?.flaggingProvider?.initializationTimeoutMs
1115
1129
  opts.flushInterval = maybeInt(options.flushInterval)
1116
1130
  this.#optsUnprocessed.flushInterval = options.flushInterval
1117
1131
  opts.flushMinSpans = maybeInt(options.flushMinSpans)
@@ -169,6 +169,7 @@ function enable (rc, config, onConfigUpdated) {
169
169
 
170
170
  // Debugger
171
171
  rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_ENABLE_DYNAMIC_INSTRUMENTATION, true)
172
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_ENABLE_LIVE_DEBUGGING, true)
172
173
 
173
174
  // Code Origin
174
175
  rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_ENABLE_CODE_ORIGIN, true)
@@ -73,11 +73,11 @@
73
73
  "DD_ENV": ["A"],
74
74
  "DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED": ["A"],
75
75
  "DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED": ["A"],
76
+ "DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS": ["A"],
76
77
  "DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED": ["A"],
77
78
  "DD_EXPERIMENTAL_TEST_OPT_GIT_CACHE_ENABLED": ["A"],
78
79
  "DD_EXPERIMENTAL_TEST_OPT_GIT_CACHE_DIR": ["A"],
79
80
  "DD_EXTERNAL_ENV": ["A"],
80
- "DD_FLAGGING_PROVIDER_ENABLED": ["A"],
81
81
  "DD_GIT_BRANCH": ["A"],
82
82
  "DD_GIT_COMMIT_AUTHOR_DATE": ["A"],
83
83
  "DD_GIT_COMMIT_AUTHOR_EMAIL": ["A"],
@@ -1,13 +1,11 @@
1
1
  'use strict'
2
2
 
3
- const { URL } = require('url')
4
-
5
3
  // Load binding first to not import other modules if it throws
6
4
  const libdatadog = require('@datadog/libdatadog')
7
5
  const binding = libdatadog.load('crashtracker')
8
6
 
9
7
  const log = require('../log')
10
- const defaults = require('../config/defaults')
8
+ const { getAgentUrl } = require('../agent/url')
11
9
  const pkg = require('../../../../package.json')
12
10
  const processTags = require('../process-tags')
13
11
 
@@ -52,8 +50,7 @@ class Crashtracker {
52
50
 
53
51
  // TODO: Send only configured values when defaults are fixed.
54
52
  #getConfig (config) {
55
- const { hostname = defaults.hostname, port = defaults.port } = config
56
- const url = config.url || new URL(`http://${hostname}:${port}`)
53
+ const url = getAgentUrl(config)
57
54
 
58
55
  return {
59
56
  additional_files: [],
@@ -1,12 +1,11 @@
1
1
  'use strict'
2
2
 
3
- const { URL, format } = require('url')
4
3
  const zlib = require('zlib')
5
4
  const pkg = require('../../../../package.json')
6
5
  const log = require('../log')
7
6
  const request = require('../exporters/common/request')
8
7
  const { MsgpackEncoder } = require('../msgpack')
9
- const defaults = require('../config/defaults')
8
+ const { getAgentUrl } = require('../agent/url')
10
9
 
11
10
  const msgpack = new MsgpackEncoder()
12
11
 
@@ -32,12 +31,7 @@ function makeRequest (data, url, cb) {
32
31
 
33
32
  class DataStreamsWriter {
34
33
  constructor (config) {
35
- const { hostname = defaults.hostname, port = defaults.port, url } = config
36
- this._url = url || new URL(format({
37
- protocol: 'http:',
38
- hostname,
39
- port
40
- }))
34
+ this._url = getAgentUrl(config)
41
35
  }
42
36
 
43
37
  flush (payload) {
@@ -1,8 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { workerData: { config: parentConfig, parentThreadId, configPort } } = require('node:worker_threads')
4
- const { format } = require('node:url')
5
- const defaults = require('../../config/defaults')
4
+ const { getAgentUrl } = require('../../agent/url')
6
5
  const log = require('./log')
7
6
 
8
7
  const config = module.exports = {
@@ -19,11 +18,7 @@ configPort.on('messageerror', (err) =>
19
18
  )
20
19
 
21
20
  function updateUrl (updates) {
22
- config.url = updates.url || format({
23
- protocol: 'http:',
24
- hostname: updates.hostname || defaults.hostname,
25
- port: updates.port
26
- })
21
+ config.url = getAgentUrl(updates)
27
22
 
28
23
  config.dynamicInstrumentation.captureTimeoutNs = BigInt(updates.dynamicInstrumentation.captureTimeoutMs) * 1_000_000n
29
24
  }
@@ -11,26 +11,25 @@ class JSONBuffer {
11
11
  this.#maxSize = size
12
12
  this.#timeout = timeout
13
13
  this.#onFlush = onFlush
14
- this.#reset()
15
- }
16
-
17
- #reset () {
18
- clearTimeout(this.#timer)
19
- this.#timer = undefined
20
- this.#partialJson = undefined
21
14
  }
22
15
 
23
16
  #flush () {
24
17
  const json = `${this.#partialJson}]`
25
- this.#reset()
18
+ this.#partialJson = undefined
26
19
  this.#onFlush(json)
27
20
  }
28
21
 
29
22
  write (str, size = Buffer.byteLength(str)) {
30
- if (this.#timer === undefined) {
23
+ if (this.#partialJson === undefined) {
31
24
  this.#partialJson = `[${str}`
32
- this.#timer = setTimeout(() => this.#flush(), this.#timeout)
33
- } else if (Buffer.byteLength(/** @type {string} */ (this.#partialJson)) + size + 2 > this.#maxSize) {
25
+ if (this.#timer === undefined) {
26
+ this.#timer = setTimeout(() => this.#flush(), this.#timeout)
27
+ } else {
28
+ this.#timer.refresh()
29
+ }
30
+ } else if (Buffer.byteLength(this.#partialJson) + size + 2 > this.#maxSize) {
31
+ clearTimeout(this.#timer)
32
+ this.#timer = undefined
34
33
  this.#flush()
35
34
  this.write(str, size)
36
35
  } else {
@@ -3,12 +3,12 @@
3
3
  const lookup = require('dns').lookup // cache to avoid instrumentation
4
4
  const dgram = require('dgram')
5
5
  const isIP = require('net').isIP
6
- const { URL, format } = require('url')
7
6
 
8
7
  const request = require('./exporters/common/request')
9
8
  const log = require('./log')
10
9
  const Histogram = require('./histogram')
11
10
  const defaults = require('./config/defaults')
11
+ const { getAgentUrl } = require('./agent/url')
12
12
 
13
13
  const MAX_BUFFER_SIZE = 1024 // limit from the agent
14
14
 
@@ -179,14 +179,8 @@ class DogStatsDClient {
179
179
  tags
180
180
  }
181
181
 
182
- if (config.url) {
183
- clientConfig.metricsProxyUrl = config.url
184
- } else if (config.port) {
185
- clientConfig.metricsProxyUrl = new URL(format({
186
- protocol: 'http:',
187
- hostname: config.hostname || defaults.hostname,
188
- port: config.port
189
- }))
182
+ if (config.url || config.port) {
183
+ clientConfig.metricsProxyUrl = getAgentUrl(config)
190
184
  }
191
185
 
192
186
  return clientConfig
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const { URL, format } = require('url')
3
+ const { URL } = require('url')
4
4
  const log = require('../../log')
5
- const defaults = require('../../config/defaults')
5
+ const { getAgentUrl } = require('../../agent/url')
6
6
  const Writer = require('./writer')
7
7
 
8
8
  class AgentExporter {
@@ -10,12 +10,8 @@ class AgentExporter {
10
10
 
11
11
  constructor (config, prioritySampler) {
12
12
  this._config = config
13
- const { url, hostname = defaults.hostname, port, lookup, protocolVersion, stats = {}, apmTracingEnabled } = config
14
- this._url = url || new URL(format({
15
- protocol: 'http:',
16
- hostname,
17
- port
18
- }))
13
+ const { lookup, protocolVersion, stats = {}, apmTracingEnabled } = config
14
+ this._url = getAgentUrl(config)
19
15
 
20
16
  const headers = {}
21
17
  if (stats.enabled || apmTracingEnabled === false) {
@@ -12,8 +12,9 @@ const BaseWriter = require('../common/writer')
12
12
  const METRIC_PREFIX = 'datadog.tracer.node.exporter.agent'
13
13
 
14
14
  class AgentWriter extends BaseWriter {
15
- constructor ({ prioritySampler, lookup, protocolVersion, headers, config = {} }) {
16
- super(...arguments)
15
+ constructor (...args) {
16
+ super(...args)
17
+ const { prioritySampler, lookup, protocolVersion, headers, config = {} } = args[0]
17
18
  const AgentEncoder = getEncoder(protocolVersion)
18
19
 
19
20
  this._prioritySampler = prioritySampler