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
@@ -4,28 +4,28 @@ const fs = require('fs')
4
4
  const os = require('os')
5
5
  const { URL } = require('url')
6
6
  const path = require('path')
7
- const uuid = require('../../../vendor/dist/crypto-randomuuid') // we need to keep the old uuid dep because of cypress
8
-
9
- const set = require('../../datadog-core/src/utils/src/set')
10
- const { DD_MAJOR } = require('../../../version')
11
- const log = require('./log')
12
- const tagger = require('./tagger')
13
- const { isTrue, isFalse, normalizeProfilingEnabledValue } = require('./util')
14
- const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('./plugins/util/tags')
15
- const { getGitMetadataFromGitProperties, removeUserSensitiveInfo, getRemoteOriginURL, resolveGitHeadSHA } =
16
- require('./git_properties')
17
- const { updateConfig } = require('./telemetry')
18
- const telemetryMetrics = require('./telemetry/metrics')
7
+ const uuid = require('../../../../vendor/dist/crypto-randomuuid') // we need to keep the old uuid dep because of cypress
8
+
9
+ const set = require('../../../datadog-core/src/utils/src/set')
10
+ const { DD_MAJOR } = require('../../../../version')
11
+ const log = require('../log')
12
+ const tagger = require('../tagger')
13
+ const { isTrue, isFalse, normalizeProfilingEnabledValue } = require('../util')
14
+ const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
15
+ const { updateConfig } = require('../telemetry')
16
+ const telemetryMetrics = require('../telemetry/metrics')
19
17
  const {
20
18
  isInServerlessEnvironment,
21
19
  getIsGCPFunction,
22
20
  getIsAzureFunction,
23
21
  enableGCPPubSubPushSubscription
24
- } = require('./serverless')
25
- const { ORIGIN_KEY } = require('./constants')
26
- const { appendRules } = require('./payload-tagging/config')
27
- const { getEnvironmentVariable: getEnv, getEnvironmentVariables } = require('./config-helper')
28
- const defaults = require('./config_defaults')
22
+ } = require('../serverless')
23
+ const { ORIGIN_KEY } = require('../constants')
24
+ const { appendRules } = require('../payload-tagging/config')
25
+ const { getGitMetadataFromGitProperties, removeUserSensitiveInfo, getRemoteOriginURL, resolveGitHeadSHA } =
26
+ require('./git_properties')
27
+ const { getEnvironmentVariable: getEnv, getEnvironmentVariables, getStableConfigSources } = require('./helper')
28
+ const defaults = require('./defaults')
29
29
 
30
30
  const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
31
31
 
@@ -276,9 +276,12 @@ class Config {
276
276
 
277
277
  constructor (options = {}) {
278
278
  if (!isInServerlessEnvironment()) {
279
- // Bail out early if we're in a serverless environment, stable config isn't supported
280
- const StableConfig = require('./config_stable')
281
- this.stableConfig = new StableConfig()
279
+ const configEnvSources = getStableConfigSources()
280
+ this.stableConfig = {
281
+ fleetEntries: configEnvSources.fleetStableConfig,
282
+ localEntries: configEnvSources.localStableConfig,
283
+ warnings: configEnvSources.stableConfigWarnings
284
+ }
282
285
  }
283
286
 
284
287
  options = {
@@ -328,7 +331,6 @@ class Config {
328
331
  this.#applyStableConfig(this.stableConfig?.fleetEntries ?? {}, this.#fleetStableConfig)
329
332
  this.#applyOptions(options)
330
333
  this.#applyCalculated()
331
- this.#applyRemote({})
332
334
  this.#merge()
333
335
 
334
336
  tagger.add(this.tags, {
@@ -353,15 +355,38 @@ class Config {
353
355
  return this.#parsedDdTags
354
356
  }
355
357
 
356
- // Supports only a subset of options for now.
357
- configure (options, remote) {
358
- if (remote) {
359
- this.#applyRemote(options)
360
- } else {
361
- this.#applyOptions(options)
358
+ /**
359
+ * Set the configuration with remote config settings.
360
+ * Applies remote configuration, recalculates derived values, and merges all configuration sources.
361
+ *
362
+ * @param {import('./config/remote_config').RemoteConfigOptions|null} options - Configurations received via Remote
363
+ * Config or null to reset all remote configuration
364
+ */
365
+ setRemoteConfig (options) {
366
+ // Clear all RC-managed fields to ensure previous values don't persist.
367
+ // State is instead managed by the `RCClientLibConfigManager` class
368
+ this.#remote = {}
369
+ this.#remoteUnprocessed = {}
370
+
371
+ // Special case: if options is null, nothing to apply
372
+ // This happens when all remote configs are removed
373
+ if (options !== null) {
374
+ this.#applyRemoteConfig(options)
362
375
  }
363
376
 
364
- // TODO: test
377
+ this.#applyCalculated()
378
+ this.#merge()
379
+ }
380
+
381
+ // TODO: Remove the `updateOptions` method. We don't want to support updating the config this way
382
+ /**
383
+ * Updates the configuration with new programmatic options.
384
+ *
385
+ * @deprecated This method should not be used and will be removed in a future version.
386
+ * @param {object} options - Configuration options to apply (same format as tracer init options)
387
+ */
388
+ updateOptions (options) {
389
+ this.#applyOptions(options)
365
390
  this.#applyCalculated()
366
391
  this.#merge()
367
392
  }
@@ -478,6 +503,7 @@ class Config {
478
503
  DD_IAST_STACK_TRACE_ENABLED,
479
504
  DD_INJECTION_ENABLED,
480
505
  DD_INJECT_FORCE,
506
+ DD_ENABLE_NX_SERVICE_NAME,
481
507
  DD_INSTRUMENTATION_TELEMETRY_ENABLED,
482
508
  DD_INSTRUMENTATION_CONFIG_ID,
483
509
  DD_LOGS_INJECTION,
@@ -574,6 +600,7 @@ class Config {
574
600
  OTEL_TRACES_SAMPLER,
575
601
  OTEL_TRACES_SAMPLER_ARG,
576
602
  DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED,
603
+ DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS,
577
604
  OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
578
605
  OTEL_EXPORTER_OTLP_LOGS_HEADERS,
579
606
  OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
@@ -591,7 +618,8 @@ class Config {
591
618
  OTEL_BSP_SCHEDULE_DELAY,
592
619
  OTEL_BSP_MAX_EXPORT_BATCH_SIZE,
593
620
  OTEL_BSP_MAX_QUEUE_SIZE,
594
- OTEL_METRIC_EXPORT_INTERVAL
621
+ OTEL_METRIC_EXPORT_INTERVAL,
622
+ NX_TASK_TARGET_PROJECT
595
623
  } = source
596
624
 
597
625
  const tags = {}
@@ -749,7 +777,15 @@ class Config {
749
777
  maybeFloat(DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS)
750
778
  unprocessedTarget['dynamicInstrumentation.uploadInterval'] = DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS
751
779
  this.#setString(target, 'env', DD_ENV || tags.env)
752
- 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
+ }
753
789
  this.#setBoolean(target, 'traceEnabled', DD_TRACE_ENABLED)
754
790
  this.#setBoolean(target, 'experimental.aiguard.enabled', DD_AI_GUARD_ENABLED)
755
791
  this.#setString(target, 'experimental.aiguard.endpoint', DD_AI_GUARD_ENDPOINT)
@@ -862,7 +898,22 @@ class Config {
862
898
  this.#setSamplingRule(target, 'sampler.rules', safeJsonParse(DD_TRACE_SAMPLING_RULES))
863
899
  unprocessedTarget['sampler.rules'] = DD_TRACE_SAMPLING_RULES
864
900
  this.#setString(target, 'scope', DD_TRACE_SCOPE)
865
- this.#setString(target, 'service', DD_SERVICE || tags.service || OTEL_SERVICE_NAME)
901
+ // Priority:
902
+ // DD_SERVICE > tags.service > OTEL_SERVICE_NAME > NX_TASK_TARGET_PROJECT (if DD_ENABLE_NX_SERVICE_NAME) > default
903
+ let serviceName = DD_SERVICE || tags.service || OTEL_SERVICE_NAME
904
+ if (!serviceName && NX_TASK_TARGET_PROJECT) {
905
+ if (isTrue(DD_ENABLE_NX_SERVICE_NAME)) {
906
+ serviceName = NX_TASK_TARGET_PROJECT
907
+ } else if (DD_MAJOR < 6) {
908
+ // Warn about v6 behavior change for Nx projects
909
+ log.warn(
910
+ 'NX_TASK_TARGET_PROJECT is set but no service name was configured. ' +
911
+ 'In v6, NX_TASK_TARGET_PROJECT will be used as the default service name. ' +
912
+ 'Set DD_ENABLE_NX_SERVICE_NAME=true to opt-in to this behavior now, or set a service name explicitly.'
913
+ )
914
+ }
915
+ }
916
+ this.#setString(target, 'service', serviceName)
866
917
  if (DD_SERVICE_MAPPING) {
867
918
  target.serviceMapping = Object.fromEntries(
868
919
  DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
@@ -1070,6 +1121,11 @@ class Config {
1070
1121
  this.#setBoolean(opts, 'experimental.enableGetRumData', options.experimental?.enableGetRumData)
1071
1122
  this.#setString(opts, 'experimental.exporter', options.experimental?.exporter)
1072
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
1073
1129
  opts.flushInterval = maybeInt(options.flushInterval)
1074
1130
  this.#optsUnprocessed.flushInterval = options.flushInterval
1075
1131
  opts.flushMinSpans = maybeInt(options.flushMinSpans)
@@ -1287,31 +1343,36 @@ class Config {
1287
1343
  }
1288
1344
  }
1289
1345
 
1290
- #applyRemote (options) {
1346
+ /**
1347
+ * Applies remote configuration options from APM_TRACING configs.
1348
+ *
1349
+ * @param {import('./config/remote_config').RemoteConfigOptions} options - Configurations received via Remote Config
1350
+ */
1351
+ #applyRemoteConfig (options) {
1291
1352
  const opts = this.#remote
1292
- const tags = {}
1293
- const headerTags = options.tracing_header_tags
1294
- ? options.tracing_header_tags.map(tag => {
1295
- return tag.tag_name ? `${tag.header}:${tag.tag_name}` : tag.header
1296
- })
1297
- : undefined
1298
-
1299
- tagger.add(tags, options.tracing_tags)
1300
- if (Object.keys(tags).length) tags['runtime-id'] = runtimeId
1301
1353
 
1354
+ this.#setBoolean(opts, 'dynamicInstrumentation.enabled', options.dynamic_instrumentation_enabled)
1355
+ this.#setBoolean(opts, 'codeOriginForSpans.enabled', options.code_origin_enabled)
1302
1356
  this.#setUnit(opts, 'sampleRate', options.tracing_sampling_rate)
1303
1357
  this.#setBoolean(opts, 'logInjection', options.log_injection_enabled)
1304
- opts.headerTags = headerTags
1305
- this.#setTags(opts, 'tags', tags)
1306
1358
  this.#setBoolean(opts, 'tracing', options.tracing_enabled)
1307
1359
  this.#remoteUnprocessed['sampler.rules'] = options.tracing_sampling_rules
1308
- this.#setSamplingRule(opts, 'sampler.rules', this.#reformatTags(options.tracing_sampling_rules))
1360
+ this.#setSamplingRule(opts, 'sampler.rules', this.#reformatTagsFromRC(options.tracing_sampling_rules))
1361
+
1362
+ opts.headerTags = options.tracing_header_tags?.map(tag => {
1363
+ return tag.tag_name ? `${tag.header}:${tag.tag_name}` : tag.header
1364
+ })
1365
+
1366
+ const tags = {}
1367
+ tagger.add(tags, options.tracing_tags)
1368
+ if (Object.keys(tags).length) tags['runtime-id'] = runtimeId
1369
+ this.#setTags(opts, 'tags', tags)
1309
1370
  }
1310
1371
 
1311
- #reformatTags (samplingRules) {
1372
+ #reformatTagsFromRC (samplingRules) {
1312
1373
  for (const rule of (samplingRules || [])) {
1313
- const reformattedTags = {}
1314
1374
  if (rule.tags) {
1375
+ const reformattedTags = {}
1315
1376
  for (const tag of rule.tags) {
1316
1377
  reformattedTags[tag.key] = tag.value_glob
1317
1378
  }
@@ -1,34 +1,203 @@
1
1
  'use strict'
2
2
 
3
3
  const RemoteConfigCapabilities = require('../remote_config/capabilities')
4
+ const log = require('../log')
5
+
6
+ module.exports = {
7
+ enable
8
+ }
9
+
10
+ /**
11
+ * @typedef {object} RemoteConfigOptions
12
+ * @property {boolean} [dynamic_instrumentation_enabled] - Enable Dynamic Instrumentation
13
+ * @property {boolean} [code_origin_enabled] - Enable code origin tagging for spans
14
+ * @property {Array<{header: string, tag_name?: string}>} [tracing_header_tags] - HTTP headers to tag
15
+ * @property {Array<string>} [tracing_tags] - Global tags (format: "key:value")
16
+ * @property {number} [tracing_sampling_rate] - Global sampling rate (0.0-1.0)
17
+ * @property {boolean} [log_injection_enabled] - Enable trace context log injection
18
+ * @property {boolean} [tracing_enabled] - Enable/disable tracing globally
19
+ * @property {Array<object>} [tracing_sampling_rules] - Trace sampling rules configuration
20
+ */
21
+
22
+ /**
23
+ * @typedef {ReturnType<import('../config')>} Config
24
+ */
25
+
26
+ /**
27
+ * Manages multiple APM_TRACING configurations with priority-based merging
28
+ */
29
+ class RCClientLibConfigManager {
30
+ /**
31
+ * @param {string} currentService - Current service name
32
+ * @param {string} currentEnv - Current environment name
33
+ */
34
+ constructor (currentService, currentEnv) {
35
+ this.configs = new Map() // config_id -> { conf, priority }
36
+ this.currentService = currentService
37
+ this.currentEnv = currentEnv
38
+ }
39
+
40
+ /**
41
+ * Calculate priority based on target specificity. Higher values take precedence.
42
+ * Priority order (highest → lowest):
43
+ * Service+Env (5) > Service (4) > Env (3) > Cluster (2) > Org (1)
44
+ *
45
+ * @param {object} conf - Remote config object with service_target and k8s_target_v2 properties
46
+ * @returns {number} Priority value from 1 (org-level) to 5 (service+env specific)
47
+ */
48
+ calculatePriority (conf) {
49
+ const serviceTarget = conf.service_target
50
+ const k8sTarget = conf.k8s_target_v2
51
+
52
+ if (serviceTarget) {
53
+ const service = serviceTarget.service
54
+ const env = serviceTarget.env
55
+
56
+ const hasSpecificService = service && service !== '*'
57
+ const hasSpecificEnv = env && env !== '*'
58
+
59
+ if (hasSpecificService && hasSpecificEnv) return 5
60
+ if (hasSpecificService) return 4
61
+ if (hasSpecificEnv) return 3
62
+ }
63
+
64
+ if (k8sTarget) return 2
65
+
66
+ return 1 // Org level
67
+ }
68
+
69
+ /**
70
+ * Check if config matches current service/env
71
+ *
72
+ * @param {object} conf - Remote config object with service_target property
73
+ * @returns {boolean} True if config matches current service/env or has no filter
74
+ */
75
+ matchesCurrentServiceEnv (conf) {
76
+ const serviceTarget = conf.service_target
77
+ if (!serviceTarget) return true // No filter means match all
78
+
79
+ const service = serviceTarget.service
80
+ const env = serviceTarget.env
81
+
82
+ // Check service match
83
+ if (service && service !== '*' && service !== this.currentService) {
84
+ log.debug('[config/remote_config] Ignoring config for service: %s (current: %s)',
85
+ service, this.currentService)
86
+ return false
87
+ }
88
+
89
+ // Check env match
90
+ if (env && env !== '*' && env !== this.currentEnv) {
91
+ log.debug('[config/remote_config] Ignoring config for env: %s (current: %s)',
92
+ env, this.currentEnv)
93
+ return false
94
+ }
95
+
96
+ return true
97
+ }
98
+
99
+ /**
100
+ * Add or update a config
101
+ *
102
+ * @param {string} configId - Unique identifier for the config
103
+ * @param {object} conf - Remote config object to add
104
+ */
105
+ addConfig (configId, conf) {
106
+ if (!this.matchesCurrentServiceEnv(conf)) {
107
+ return
108
+ }
109
+
110
+ const priority = this.calculatePriority(conf)
111
+ this.configs.set(configId, { conf, priority })
112
+
113
+ log.debug('[config/remote_config] Added config %s with priority %d', configId, priority)
114
+ }
115
+
116
+ /**
117
+ * Remove a config
118
+ *
119
+ * @param {string} configId - Unique identifier for the config to remove
120
+ */
121
+ removeConfig (configId) {
122
+ const removed = this.configs.delete(configId)
123
+ if (removed) {
124
+ log.debug('[config/remote_config] Removed config %s', configId)
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Get merged lib_config with higher priority configs overriding lower priority ones
130
+ *
131
+ * @returns {RemoteConfigOptions|null} Merged config object or null if no configs present
132
+ */
133
+ getMergedLibConfig () {
134
+ if (this.configs.size === 0) return null
135
+
136
+ let hasLibConfig = false
137
+
138
+ const merged = [...this.configs.values()]
139
+ .sort((a, b) => a.priority - b.priority)
140
+ .reduce((merged, { conf }) => {
141
+ if (conf.lib_config != null) hasLibConfig = true
142
+ return Object.assign(merged, conf.lib_config)
143
+ }, {})
144
+
145
+ return hasLibConfig ? merged : null
146
+ }
147
+ }
4
148
 
5
149
  /**
6
150
  * Configures remote config for core APM tracing functionality
7
151
  *
8
- * @param {object} rc - RemoteConfig instance
9
- * @param {object} config - Tracer config
10
- * @param {Function} enableOrDisableTracing - Function to enable/disable tracing based on config
152
+ * @param {import('../remote_config')} rc - RemoteConfig instance
153
+ * @param {Config} config - Tracer config
154
+ * @param {() => void} onConfigUpdated - Function to call when config is updated
11
155
  */
12
- function enable (rc, config, enableOrDisableTracing) {
13
- // Register core APM tracing capabilities
156
+ function enable (rc, config, onConfigUpdated) {
157
+ // This tracer supports receiving config subsets via the APM_TRACING product handler.
158
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_MULTICONFIG, true)
159
+
160
+ // Tracing
161
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_ENABLED, true)
162
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_SAMPLE_RATE, true)
163
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_SAMPLE_RULES, true)
14
164
  rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_CUSTOM_TAGS, true)
15
165
  rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_HTTP_HEADER_TAGS, true)
166
+
167
+ // Log Management
16
168
  rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_LOGS_INJECTION, true)
17
- rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_SAMPLE_RATE, true)
18
- rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_ENABLED, true)
19
- rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_SAMPLE_RULES, true)
20
169
 
21
- // APM_TRACING product handler - manages tracer configuration
22
- rc.setProductHandler('APM_TRACING', (action, conf) => {
23
- if (action === 'unapply') {
24
- config.configure({}, true)
25
- } else {
26
- config.configure(conf.lib_config, true)
170
+ // Debugger
171
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_ENABLE_DYNAMIC_INSTRUMENTATION, true)
172
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_ENABLE_LIVE_DEBUGGING, true)
173
+
174
+ // Code Origin
175
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_ENABLE_CODE_ORIGIN, true)
176
+
177
+ const rcClientLibConfigManager = new RCClientLibConfigManager(config.service, config.env)
178
+
179
+ // Subscribe to APM_TRACING product (setBatchHandler used below doesn't automatically subscribe)
180
+ rc.subscribeProducts('APM_TRACING')
181
+
182
+ // Use a batch handler to process all changes before updating the config. This is important in case there's
183
+ // conflicting configs between, for example, the org and service level.
184
+ rc.setBatchHandler(['APM_TRACING'], (transaction) => {
185
+ const { toUnapply, toApply, toModify } = transaction
186
+
187
+ for (const item of toUnapply) {
188
+ rcClientLibConfigManager.removeConfig(item.id)
189
+ transaction.ack(item.path)
27
190
  }
28
- enableOrDisableTracing(config, rc)
29
- })
30
- }
31
191
 
32
- module.exports = {
33
- enable
192
+ for (const item of [...toApply, ...toModify]) {
193
+ rcClientLibConfigManager.addConfig(item.id, item.file)
194
+ transaction.ack(item.path)
195
+ }
196
+
197
+ // Get merged config and apply it
198
+ const mergedLibConfig = rcClientLibConfigManager.getMergedLibConfig()
199
+ config.setRemoteConfig(mergedLibConfig)
200
+
201
+ onConfigUpdated()
202
+ })
34
203
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  const os = require('os')
4
4
  const fs = require('fs')
5
- const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
5
+ const { getEnvironmentVariable } = require('./helper')
6
6
 
7
7
  class StableConfig {
8
8
  constructor () {
@@ -11,14 +11,10 @@ class StableConfig {
11
11
  this.fleetEntries = {}
12
12
  this.wasm_loaded = false
13
13
 
14
- const { localConfigPath, fleetConfigPath } = this._getStableConfigPaths()
15
- if (!fs.existsSync(localConfigPath) && !fs.existsSync(fleetConfigPath)) {
16
- // Bail out early if files don't exist to avoid unnecessary library loading
17
- return
18
- }
14
+ const { localConfigPath, fleetConfigPath } = this.#getStableConfigPaths()
19
15
 
20
- const localConfig = this._readConfigFromPath(localConfigPath)
21
- const fleetConfig = this._readConfigFromPath(fleetConfigPath)
16
+ const localConfig = this.#readConfigFromPath(localConfigPath)
17
+ const fleetConfig = this.#readConfigFromPath(fleetConfigPath)
22
18
  if (!localConfig && !fleetConfig) {
23
19
  // Bail out early if files are empty or we can't read them to avoid unnecessary library loading
24
20
  return
@@ -47,7 +43,7 @@ class StableConfig {
47
43
  // eslint-disable-next-line eslint-rules/eslint-process-env
48
44
  configurator.set_envp(Object.entries(process.env).map(([key, value]) => `${key}=${value}`))
49
45
  configurator.set_args(process.argv)
50
- configurator.get_configuration(localConfig.toString(), fleetConfig.toString()).forEach((entry) => {
46
+ configurator.get_configuration(localConfig, fleetConfig).forEach((entry) => {
51
47
  if (entry.source === 'local_stable_config') {
52
48
  this.localEntries[entry.name] = entry.value
53
49
  } else if (entry.source === 'fleet_stable_config') {
@@ -59,43 +55,35 @@ class StableConfig {
59
55
  }
60
56
  }
61
57
 
62
- _readConfigFromPath (path) {
58
+ #readConfigFromPath (path) {
63
59
  try {
64
60
  return fs.readFileSync(path, 'utf8')
65
61
  } catch (err) {
66
62
  if (err.code !== 'ENOENT') {
67
63
  this.warnings.push(`Error reading config file at ${path}. ${err.code}: ${err.message}`)
68
64
  }
69
- return '' // Always return a string to avoid undefined.toString() errors
65
+ return '' // Always return a string for configurator.get_configuration()
70
66
  }
71
67
  }
72
68
 
73
- _getStableConfigPaths () {
74
- let localConfigPath = ''
75
- let fleetConfigPath = ''
76
- switch (os.type().toLowerCase()) {
77
- case 'linux':
78
- localConfigPath = '/etc/datadog-agent/application_monitoring.yaml'
79
- fleetConfigPath = '/etc/datadog-agent/managed/datadog-agent/stable/application_monitoring.yaml'
80
- break
69
+ #getStableConfigPaths () {
70
+ // TODO(BridgeAR): Remove these environment variables once we have a proper way to test the stable config.
71
+ // Allow overriding the paths for testing
72
+ let localConfigPath = getEnvironmentVariable('DD_TEST_LOCAL_CONFIG_PATH')
73
+ let fleetConfigPath = getEnvironmentVariable('DD_TEST_FLEET_CONFIG_PATH')
74
+ switch (os.platform()) {
81
75
  case 'darwin':
82
- localConfigPath = '/opt/datadog-agent/etc/application_monitoring.yaml'
83
- fleetConfigPath = '/opt/datadog-agent/etc/managed/datadog-agent/stable/application_monitoring.yaml'
76
+ localConfigPath ??= '/opt/datadog-agent/etc/application_monitoring.yaml'
77
+ fleetConfigPath ??= '/opt/datadog-agent/etc/managed/datadog-agent/stable/application_monitoring.yaml'
84
78
  break
85
79
  case 'win32':
86
- localConfigPath = String.raw`C:\ProgramData\Datadog\application_monitoring.yaml`
87
- fleetConfigPath = String.raw`C:\ProgramData\Datadog\managed\datadog-agent\stable\application_monitoring.yaml`
80
+ localConfigPath ??= String.raw`C:\ProgramData\Datadog\application_monitoring.yaml`
81
+ fleetConfigPath ??= String.raw`C:\ProgramData\Datadog\managed\datadog-agent\stable\application_monitoring.yaml`
88
82
  break
89
83
  default:
90
- break
91
- }
92
-
93
- // Allow overriding the paths for testing
94
- if (getEnvironmentVariable('DD_TEST_LOCAL_CONFIG_PATH') !== undefined) {
95
- localConfigPath = getEnvironmentVariable('DD_TEST_LOCAL_CONFIG_PATH')
96
- }
97
- if (getEnvironmentVariable('DD_TEST_FLEET_CONFIG_PATH') !== undefined) {
98
- fleetConfigPath = getEnvironmentVariable('DD_TEST_FLEET_CONFIG_PATH')
84
+ // Linux and other platforms as fallback
85
+ localConfigPath ??= '/etc/datadog-agent/application_monitoring.yaml'
86
+ fleetConfigPath ??= '/etc/datadog-agent/managed/datadog-agent/stable/application_monitoring.yaml'
99
87
  }
100
88
 
101
89
  return { localConfigPath, fleetConfigPath }
@@ -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"],
@@ -93,6 +93,7 @@
93
93
  "DD_GIT_TAG": ["A"],
94
94
  "DD_GIT_PULL_REQUEST_BASE_BRANCH": ["A"],
95
95
  "DD_GIT_PULL_REQUEST_BASE_BRANCH_SHA": ["A"],
96
+ "DD_GIT_COMMIT_HEAD_SHA": ["A"],
96
97
  "DD_GRPC_CLIENT_ERROR_STATUSES": ["A"],
97
98
  "DD_GRPC_SERVER_ERROR_STATUSES": ["A"],
98
99
  "DD_HEAP_SNAPSHOT_COUNT": ["A"],
@@ -112,6 +113,7 @@
112
113
  "DD_IAST_TELEMETRY_VERBOSITY": ["A"],
113
114
  "DD_INJECT_FORCE": ["A"],
114
115
  "DD_INJECTION_ENABLED": ["A"],
116
+ "DD_ENABLE_NX_SERVICE_NAME": ["A"],
115
117
  "DD_INSTRUMENTATION_CONFIG_ID": ["A"],
116
118
  "DD_INSTRUMENTATION_INSTALL_ID": ["A"],
117
119
  "DD_INSTRUMENTATION_INSTALL_TIME": ["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: [],
@@ -156,7 +156,7 @@ class DataStreamsProcessor {
156
156
  this.timer = setInterval(this.onInterval.bind(this), flushInterval)
157
157
  this.timer.unref()
158
158
  }
159
- process.once('beforeExit', () => this.onInterval())
159
+ globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(this.onInterval.bind(this))
160
160
  }
161
161
 
162
162
  onInterval () {
@@ -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) {
@@ -206,7 +206,7 @@ function isTypedArray (variable) {
206
206
  }
207
207
 
208
208
  function isInstanceOfCoreType (type, variable, fallback = `${variable} instanceof ${type}`) {
209
- return `(process[Symbol.for('datadog:node:util:types')]?.is${type}?.(${variable}) ?? ${fallback})`
209
+ return `(globalThis[Symbol.for('dd-trace')].utilTypes?.is${type}?.(${variable}) ?? ${fallback})`
210
210
  }
211
211
 
212
212
  function getSize (variable) {