dd-trace 5.95.0 → 5.97.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 (124) hide show
  1. package/index.d.ts +43 -0
  2. package/package.json +10 -8
  3. package/packages/datadog-esbuild/index.js +20 -9
  4. package/packages/datadog-instrumentations/src/ai.js +112 -0
  5. package/packages/datadog-instrumentations/src/child_process.js +7 -17
  6. package/packages/datadog-instrumentations/src/crypto.js +1 -2
  7. package/packages/datadog-instrumentations/src/cucumber.js +4 -1
  8. package/packages/datadog-instrumentations/src/cypress-config.js +324 -0
  9. package/packages/datadog-instrumentations/src/cypress.js +86 -4
  10. package/packages/datadog-instrumentations/src/dns.js +1 -2
  11. package/packages/datadog-instrumentations/src/express.js +4 -4
  12. package/packages/datadog-instrumentations/src/fs.js +27 -29
  13. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  14. package/packages/datadog-instrumentations/src/helpers/ai-messages.js +182 -0
  15. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +41 -13
  16. package/packages/datadog-instrumentations/src/helpers/hook.js +31 -6
  17. package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -19
  18. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -13
  19. package/packages/datadog-instrumentations/src/helpers/register.js +103 -142
  20. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +25 -0
  21. package/packages/datadog-instrumentations/src/http/client.js +2 -3
  22. package/packages/datadog-instrumentations/src/http/server.js +2 -5
  23. package/packages/datadog-instrumentations/src/http2/client.js +1 -3
  24. package/packages/datadog-instrumentations/src/http2/server.js +1 -3
  25. package/packages/datadog-instrumentations/src/jest.js +13 -4
  26. package/packages/datadog-instrumentations/src/limitd-client.js +1 -1
  27. package/packages/datadog-instrumentations/src/mocha/utils.js +14 -1
  28. package/packages/datadog-instrumentations/src/net.js +2 -8
  29. package/packages/datadog-instrumentations/src/pino.js +1 -1
  30. package/packages/datadog-instrumentations/src/playwright.js +4 -1
  31. package/packages/datadog-instrumentations/src/prisma.js +1 -2
  32. package/packages/datadog-instrumentations/src/selenium.js +4 -1
  33. package/packages/datadog-instrumentations/src/sequelize.js +1 -1
  34. package/packages/datadog-instrumentations/src/url.js +1 -3
  35. package/packages/datadog-instrumentations/src/vitest.js +5 -1
  36. package/packages/datadog-instrumentations/src/vm.js +1 -3
  37. package/packages/datadog-plugin-aws-sdk/src/base.js +4 -3
  38. package/packages/datadog-plugin-cucumber/src/index.js +7 -3
  39. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +57 -5
  40. package/packages/datadog-plugin-graphql/src/resolve.js +1 -1
  41. package/packages/datadog-plugin-jest/src/index.js +4 -2
  42. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +31 -4
  43. package/packages/datadog-plugin-mocha/src/index.js +5 -2
  44. package/packages/datadog-plugin-next/src/index.js +2 -14
  45. package/packages/datadog-plugin-openai/src/services.js +1 -0
  46. package/packages/datadog-webpack/index.js +3 -3
  47. package/packages/dd-trace/index.js +12 -10
  48. package/packages/dd-trace/src/agent/url.js +2 -2
  49. package/packages/dd-trace/src/aiguard/index.js +64 -0
  50. package/packages/dd-trace/src/aiguard/sdk.js +4 -0
  51. package/packages/dd-trace/src/appsec/blocking.js +3 -0
  52. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  53. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +1 -1
  54. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  55. package/packages/dd-trace/src/appsec/remote_config.js +1 -0
  56. package/packages/dd-trace/src/appsec/sdk/index.js +4 -0
  57. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +6 -1
  58. package/packages/dd-trace/src/ci-visibility/lage.js +39 -0
  59. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
  60. package/packages/dd-trace/src/config/defaults.js +316 -146
  61. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -1
  62. package/packages/dd-trace/src/config/helper.js +59 -10
  63. package/packages/dd-trace/src/config/index.js +570 -1503
  64. package/packages/dd-trace/src/config/parsers.js +256 -0
  65. package/packages/dd-trace/src/config/remote_config.js +59 -2
  66. package/packages/dd-trace/src/config/supported-configurations.json +367 -433
  67. package/packages/dd-trace/src/constants.js +1 -0
  68. package/packages/dd-trace/src/crashtracking/crashtracker.js +7 -1
  69. package/packages/dd-trace/src/crashtracking/index.js +1 -7
  70. package/packages/dd-trace/src/debugger/index.js +1 -1
  71. package/packages/dd-trace/src/dogstatsd.js +12 -9
  72. package/packages/dd-trace/src/encode/0.4.js +1 -1
  73. package/packages/dd-trace/src/exporter.js +5 -2
  74. package/packages/dd-trace/src/exporters/agent/writer.js +7 -1
  75. package/packages/dd-trace/src/exporters/common/request.js +9 -0
  76. package/packages/dd-trace/src/exporters/common/writer.js +12 -2
  77. package/packages/dd-trace/src/heap_snapshots.js +3 -0
  78. package/packages/dd-trace/src/index.js +5 -2
  79. package/packages/dd-trace/src/lambda/runtime/ritm.js +6 -6
  80. package/packages/dd-trace/src/llmobs/constants/text.js +3 -0
  81. package/packages/dd-trace/src/llmobs/index.js +13 -5
  82. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -1
  83. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +60 -12
  84. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +4 -2
  85. package/packages/dd-trace/src/llmobs/sdk.js +12 -8
  86. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  87. package/packages/dd-trace/src/llmobs/tagger.js +9 -6
  88. package/packages/dd-trace/src/llmobs/writers/base.js +2 -0
  89. package/packages/dd-trace/src/llmobs/writers/util.js +3 -0
  90. package/packages/dd-trace/src/log/index.js +26 -55
  91. package/packages/dd-trace/src/log/writer.js +7 -19
  92. package/packages/dd-trace/src/noop/proxy.js +8 -0
  93. package/packages/dd-trace/src/opentelemetry/logs/index.js +1 -1
  94. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  95. package/packages/dd-trace/src/opentracing/propagation/text_map.js +9 -4
  96. package/packages/dd-trace/src/payload-tagging/config/index.js +6 -5
  97. package/packages/dd-trace/src/plugin_manager.js +8 -6
  98. package/packages/dd-trace/src/plugins/ci_plugin.js +4 -0
  99. package/packages/dd-trace/src/plugins/plugin.js +7 -4
  100. package/packages/dd-trace/src/plugins/util/test.js +5 -0
  101. package/packages/dd-trace/src/process-tags/index.js +3 -0
  102. package/packages/dd-trace/src/profiler.js +27 -2
  103. package/packages/dd-trace/src/profiling/config.js +73 -241
  104. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -4
  105. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +6 -2
  106. package/packages/dd-trace/src/profiling/profiler.js +56 -44
  107. package/packages/dd-trace/src/profiling/profilers/events.js +2 -3
  108. package/packages/dd-trace/src/profiling/profilers/wall.js +89 -6
  109. package/packages/dd-trace/src/profiling/ssi-heuristics.js +4 -1
  110. package/packages/dd-trace/src/propagation-hash/index.js +2 -1
  111. package/packages/dd-trace/src/proxy.js +36 -3
  112. package/packages/dd-trace/src/remote_config/index.js +3 -0
  113. package/packages/dd-trace/src/require-package-json.js +8 -4
  114. package/packages/dd-trace/src/ritm.js +58 -26
  115. package/packages/dd-trace/src/runtime_metrics/index.js +3 -0
  116. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +3 -0
  117. package/packages/dd-trace/src/sampler.js +1 -1
  118. package/packages/dd-trace/src/standalone/index.js +3 -0
  119. package/packages/dd-trace/src/startup-log.js +9 -0
  120. package/packages/dd-trace/src/telemetry/index.js +2 -3
  121. package/packages/dd-trace/src/telemetry/send-data.js +5 -19
  122. package/packages/dd-trace/src/telemetry/session-propagation.js +19 -44
  123. package/packages/dd-trace/src/telemetry/telemetry.js +28 -171
  124. package/packages/dd-trace/src/util.js +0 -9
@@ -5,1682 +5,749 @@ const os = require('node:os')
5
5
  const { URL } = require('node:url')
6
6
  const path = require('node:path')
7
7
 
8
+ const rfdc = require('../../../../vendor/dist/rfdc')({ proto: false, circles: false })
8
9
  const uuid = require('../../../../vendor/dist/crypto-randomuuid') // we need to keep the old uuid dep because of cypress
9
-
10
10
  const set = require('../../../datadog-core/src/utils/src/set')
11
11
  const { DD_MAJOR } = require('../../../../version')
12
12
  const log = require('../log')
13
- const tagger = require('../tagger')
14
- const { isTrue, isFalse, normalizeProfilingEnabledValue } = require('../util')
13
+ const pkg = require('../pkg')
14
+ const { isTrue } = require('../util')
15
15
  const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
16
- const { updateConfig } = require('../telemetry')
16
+ const telemetry = require('../telemetry')
17
17
  const telemetryMetrics = require('../telemetry/metrics')
18
18
  const {
19
19
  IS_SERVERLESS,
20
20
  getIsGCPFunction,
21
21
  getIsAzureFunction,
22
- enableGCPPubSubPushSubscription,
23
22
  } = require('../serverless')
24
- const { ORIGIN_KEY } = require('../constants')
23
+ const { ORIGIN_KEY, DATADOG_MINI_AGENT_PATH } = require('../constants')
25
24
  const { appendRules } = require('../payload-tagging/config')
26
25
  const { getGitMetadataFromGitProperties, removeUserSensitiveInfo, getRemoteOriginURL, resolveGitHeadSHA } =
27
26
  require('./git_properties')
28
- const { getEnvironmentVariable: getEnv, getEnvironmentVariables, getStableConfigSources } = require('./helper')
29
- const defaults = require('./defaults')
30
-
31
- const TELEMETRY_COUNTERS = new Map([
32
- ['otel.env.hiding', {}],
33
- ['otel.env.invalid', {}],
34
- ])
35
- const OTEL_DD_ENV_MAPPING = new Map([
36
- ['OTEL_LOG_LEVEL', 'DD_TRACE_LOG_LEVEL'],
37
- ['OTEL_PROPAGATORS', 'DD_TRACE_PROPAGATION_STYLE'],
38
- ['OTEL_SERVICE_NAME', 'DD_SERVICE'],
39
- ['OTEL_TRACES_SAMPLER', 'DD_TRACE_SAMPLE_RATE'],
40
- ['OTEL_TRACES_SAMPLER_ARG', 'DD_TRACE_SAMPLE_RATE'],
41
- ['OTEL_TRACES_EXPORTER', 'DD_TRACE_ENABLED'],
42
- ['OTEL_METRICS_EXPORTER', 'DD_RUNTIME_METRICS_ENABLED'],
43
- ['OTEL_RESOURCE_ATTRIBUTES', 'DD_TAGS'],
44
- ['OTEL_SDK_DISABLED', 'DD_TRACE_OTEL_ENABLED'],
45
- ['OTEL_LOGS_EXPORTER', undefined],
46
- ])
47
- const VALID_PROPAGATION_STYLES = new Set(['datadog', 'tracecontext', 'b3', 'b3 single header', 'none'])
48
- const VALID_PROPAGATION_BEHAVIOR_EXTRACT = new Set(['continue', 'restart', 'ignore'])
49
- const VALID_LOG_LEVELS = new Set(['debug', 'info', 'warn', 'error'])
50
- const DEFAULT_OTLP_PORT = 4318
27
+ const ConfigBase = require('./config-base')
28
+ const {
29
+ getEnvironmentVariable,
30
+ getEnvironmentVariables,
31
+ getStableConfigSources,
32
+ getValueFromEnvSources,
33
+ } = require('./helper')
34
+ const {
35
+ defaults,
36
+ fallbackConfigurations,
37
+ configurationsTable,
38
+ optionsTable,
39
+ configWithOrigin,
40
+ parseErrors,
41
+ generateTelemetry,
42
+ } = require('./defaults')
43
+ const { transformers } = require('./parsers')
44
+
51
45
  const RUNTIME_ID = uuid()
52
- // eslint-disable-next-line eslint-rules/eslint-process-env -- internal propagation, not user config
53
- const ROOT_SESSION_ID = process.env.DD_ROOT_JS_SESSION_ID || RUNTIME_ID
54
- const NAMING_VERSIONS = new Set(['v0', 'v1'])
55
- const DEFAULT_NAMING_VERSION = 'v0'
56
46
 
57
47
  const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
58
- const changeTracker = {}
59
48
 
49
+ /**
50
+ * @typedef {'default'
51
+ * | 'code'
52
+ * | 'remote_config'
53
+ * | 'calculated'
54
+ * | 'env_var'
55
+ * | 'local_stable_config'
56
+ * | 'fleet_stable_config'} TelemetrySource
57
+ * @typedef {'remote_config' | 'calculated'} RevertibleTelemetrySource
58
+ * @typedef {import('../../../../index').TracerOptions} TracerOptions
59
+ * @typedef {import('./config-types').ConfigKey} ConfigKey
60
+ * @typedef {import('./config-types').ConfigPath} ConfigPath
61
+ * @typedef {{
62
+ * value: import('./config-types').ConfigPathValue<ConfigPath>,
63
+ * source: TelemetrySource
64
+ * }} TrackedConfigEntry
65
+ * @typedef {{
66
+ * baseValuesByPath: Partial<Record<ConfigPath, TrackedConfigEntry>>,
67
+ * remote_config: Set<ConfigPath>,
68
+ * calculated: Set<ConfigPath>,
69
+ * }} ChangeTracker
70
+ */
71
+
72
+ /** @type {Config | null} */
60
73
  let configInstance = null
61
74
 
75
+ // An entry that is undefined means it is the default value.
76
+ /** @type {Map<ConfigPath, TelemetrySource>} */
77
+ const trackedConfigOrigins = new Map()
78
+
79
+ // ChangeTracker tracks the changes to the config up to programmatic options (code).
80
+ /** @type {ChangeTracker} */
81
+ const changeTracker = {
82
+ baseValuesByPath: {},
83
+ remote_config: new Set(),
84
+ calculated: new Set(),
85
+ }
86
+
87
+ /**
88
+ * @param {Config} config
89
+ * @param {RevertibleTelemetrySource} source
90
+ */
91
+ function undo (config, source) {
92
+ for (const name of changeTracker[source]) {
93
+ const entry = changeTracker.baseValuesByPath[name] ?? { source: 'default', value: defaults[name] }
94
+ setAndTrack(config, name, entry.value, undefined, entry.source)
95
+ }
96
+ }
97
+
98
+ function get (object, path) {
99
+ // Fast path for simple property access.
100
+ if (object[path] !== undefined) {
101
+ return object[path]
102
+ }
103
+ let index = 0
104
+ while (true) {
105
+ const nextIndex = path.indexOf('.', index)
106
+ if (nextIndex === -1) {
107
+ return object[path.slice(index)]
108
+ }
109
+ object = object[path.slice(index, nextIndex)]
110
+ index = nextIndex + 1
111
+ }
112
+ }
113
+
114
+ /**
115
+ * @param {Config} config
116
+ * @template {ConfigPath} TPath
117
+ * @param {TPath} name
118
+ * @param {import('./config-types').ConfigPathValue<TPath>} value
119
+ * @param {unknown} [rawValue]
120
+ * @param {TelemetrySource} [source]
121
+ */
122
+ function setAndTrack (config, name, value, rawValue = value, source = 'calculated') {
123
+ // envs can not be undefined
124
+ if (value == null) {
125
+ // TODO: This works as before while ignoring undefined programmatic options is not ideal.
126
+ if (source !== 'default') {
127
+ return
128
+ }
129
+ } else if (source === 'calculated' || source === 'remote_config') {
130
+ if (source === 'calculated' && value === get(config, name)) {
131
+ return
132
+ }
133
+ changeTracker[source].add(name)
134
+ } else {
135
+ const copy = typeof value === 'object' && value !== null ? rfdc(value) : value
136
+ changeTracker.baseValuesByPath[name] = { value: copy, source }
137
+ }
138
+ set(config, name, value)
139
+
140
+ generateTelemetry(rawValue, source, name)
141
+ if (source === 'default') {
142
+ trackedConfigOrigins.delete(name)
143
+ } else {
144
+ trackedConfigOrigins.set(name, source)
145
+ }
146
+ }
147
+
62
148
  module.exports = getConfig
63
149
 
64
- class Config {
150
+ // We extend from ConfigBase to make our types work
151
+ class Config extends ConfigBase {
65
152
  /**
66
153
  * parsed DD_TAGS, usable as a standalone tag set across products
67
154
  * @type {Record<string, string>}
68
155
  */
69
- #parsedDdTags = {}
70
- #envUnprocessed = {}
71
- #optsUnprocessed = {}
72
- #remoteUnprocessed = {}
73
- #env = {}
74
- #options = {}
75
- #remote = {}
76
- #defaults = {}
77
- #optionsArg = {}
78
- #localStableConfig = {}
79
- #fleetStableConfig = {}
80
- #calculated = {}
156
+ #parsedDdTags
81
157
 
158
+ /**
159
+ * @type {Record<string, string>}
160
+ */
161
+ get parsedDdTags () {
162
+ return this.#parsedDdTags
163
+ }
164
+
165
+ /**
166
+ * @param {TracerOptions} [options={}]
167
+ */
82
168
  constructor (options = {}) {
83
- if (!IS_SERVERLESS) {
84
- const configEnvSources = getStableConfigSources()
85
- this.stableConfig = {
86
- fleetEntries: configEnvSources.fleetStableConfig,
87
- localEntries: configEnvSources.localStableConfig,
88
- warnings: configEnvSources.stableConfigWarnings,
89
- }
90
- }
169
+ super()
91
170
 
92
- options = {
93
- ...options,
94
- // TODO(BridgeAR): Remove the experimental prefix once we have a major version.
95
- // That also applies to index.d.ts
96
- appsec: options.appsec == null ? options.experimental?.appsec : options.appsec,
97
- iast: options.iast == null ? options.experimental?.iast : options.iast,
171
+ const configEnvSources = getStableConfigSources()
172
+ this.stableConfig = {
173
+ fleetEntries: configEnvSources.fleetStableConfig ?? {},
174
+ localEntries: configEnvSources.localStableConfig ?? {},
175
+ warnings: configEnvSources.stableConfigWarnings,
98
176
  }
99
177
 
100
178
  // Configure the logger first so it can be used to warn about other configs
101
- const logConfig = log.getConfig()
102
- this.debug = log.isEnabled(
103
- this.stableConfig?.fleetEntries?.DD_TRACE_DEBUG,
104
- this.stableConfig?.localEntries?.DD_TRACE_DEBUG
105
- )
106
- this.logger = options.logger ?? logConfig.logger
107
- this.logLevel = log.getLogLevel(
108
- options.logLevel,
109
- this.stableConfig?.fleetEntries?.DD_TRACE_LOG_LEVEL,
110
- this.stableConfig?.localEntries?.DD_TRACE_LOG_LEVEL
111
- )
112
- log.use(this.logger)
113
- log.toggle(this.debug, this.logLevel)
179
+ // TODO: Implement auto buffering of inside of log module before first
180
+ // configure call. That way the logger is always available and the
181
+ // application doesn't need to configure it first and the configuration
182
+ // happens inside of config instead of inside of log module. If the logger
183
+ // is not deactivated, the buffered logs would be discarded. That way stable
184
+ // config warnings can also be logged directly and do not need special
185
+ // handling.
186
+ this.debug = log.configure(options)
114
187
 
115
188
  // Process stable config warnings, if any
116
189
  for (const warning of this.stableConfig?.warnings ?? []) {
117
190
  log.warn(warning)
118
191
  }
119
192
 
120
- checkIfBothOtelAndDdEnvVarSet()
121
-
122
- if (typeof options.appsec === 'boolean') {
123
- options.appsec = {
124
- enabled: options.appsec,
125
- }
126
- }
127
-
128
- if (typeof options.runtimeMetrics === 'boolean') {
129
- options.runtimeMetrics = {
130
- enabled: options.runtimeMetrics,
131
- }
132
- }
133
-
134
- this.#defaults = defaults
135
193
  this.#applyDefaults()
136
- this.#applyStableConfig(this.stableConfig?.localEntries ?? {}, this.#localStableConfig)
137
- this.#applyEnvironment()
138
- this.#applyStableConfig(this.stableConfig?.fleetEntries ?? {}, this.#fleetStableConfig)
139
- this.#applyOptions(options)
194
+ // TODO: Update origin documentation to list all valid sources. Add local_stable_config and fleet_stable_config.
195
+ this.#applyEnvs(getEnvironmentVariables(this.stableConfig.localEntries, true), 'local_stable_config')
196
+ this.#applyEnvs(getEnvironmentVariables(undefined, true), 'env_var')
197
+ this.#applyEnvs(getEnvironmentVariables(this.stableConfig.fleetEntries, true), 'fleet_stable_config')
198
+
199
+ // Experimental options are applied first, so they can be overridden by non-experimental options.
200
+ // TODO: When using programmatic options, check if there is a higher
201
+ // priority name in the same options object. Use the highest priority name.
202
+ const { experimental, ...rest } = options
203
+ if (experimental) {
204
+ // @ts-expect-error - Difficult to type this correctly.
205
+ this.#applyOptions(experimental, 'code', 'experimental')
206
+ }
207
+ this.#applyOptions(rest, 'code')
140
208
  this.#applyCalculated()
141
- this.#merge()
142
209
 
143
- tagger.add(this.tags, {
144
- service: this.service,
145
- env: this.env,
146
- version: this.version,
147
- 'runtime-id': RUNTIME_ID,
148
- })
210
+ warnWrongOtelSettings()
211
+
212
+ if (this.gitMetadataEnabled) {
213
+ this.#loadGitMetadata()
214
+ }
149
215
 
150
- this.rootSessionId = ROOT_SESSION_ID
216
+ parseErrors.clear()
217
+ }
151
218
 
152
- if (this.isCiVisibility) {
153
- tagger.add(this.tags, {
154
- [ORIGIN_KEY]: 'ciapp-test',
155
- })
219
+ #applyDefaults () {
220
+ for (const [name, value] of Object.entries(defaults)) {
221
+ set(this, name, value)
156
222
  }
223
+ }
157
224
 
158
- if (this.gitMetadataEnabled) {
159
- this.#loadGitMetadata()
225
+ /**
226
+ * @param {import('./helper').TracerEnv} envs
227
+ * @param {'env_var' | 'local_stable_config' | 'fleet_stable_config'} source
228
+ */
229
+ #applyEnvs (envs, source) {
230
+ for (const [name, value] of Object.entries(envs)) {
231
+ const entry = configurationsTable[name]
232
+ // TracePropagationStyle is a special case. It is a single option that is used to set both inject and extract.
233
+ // TODO: Consider what to do with this later
234
+ if (name === 'DD_TRACE_PROPAGATION_STYLE') {
235
+ if (
236
+ getValueFromEnvSources('DD_TRACE_PROPAGATION_STYLE_INJECT') !== undefined ||
237
+ getValueFromEnvSources('DD_TRACE_PROPAGATION_STYLE_EXTRACT') !== undefined
238
+ ) {
239
+ log.warn(
240
+ // eslint-disable-next-line @stylistic/max-len
241
+ 'Use either DD_TRACE_PROPAGATION_STYLE or separate DD_TRACE_PROPAGATION_STYLE_INJECT and DD_TRACE_PROPAGATION_STYLE_EXTRACT environment variables'
242
+ )
243
+ continue
244
+ }
245
+ this.#applyEnvs({ DD_TRACE_PROPAGATION_STYLE_INJECT: value, DD_TRACE_PROPAGATION_STYLE_EXTRACT: value }, source)
246
+ continue
247
+ }
248
+ const parsed = entry.parser(value, name, source)
249
+ const transformed = parsed !== undefined && entry.transformer ? entry.transformer(parsed, name, source) : parsed
250
+ const rawValue = transformed !== null && typeof transformed === 'object' ? value : parsed
251
+ setAndTrack(this, entry.property ?? name, transformed, rawValue, source)
160
252
  }
161
253
  }
162
254
 
163
- get parsedDdTags () {
164
- return this.#parsedDdTags
255
+ /**
256
+ * @param {TracerOptions} options
257
+ * @param {'code' | 'remote_config'} source
258
+ * @param {string} [root]
259
+ */
260
+ #applyOptions (options, source, root = '') {
261
+ for (const [name, value] of Object.entries(options)) {
262
+ const fullName = root ? `${root}.${name}` : name
263
+ let entry = optionsTable[fullName]
264
+ if (!entry) {
265
+ // TODO: Fix this by by changing remote config to use env styles.
266
+ if (name !== 'tracing' || source !== 'remote_config') {
267
+ log.warn('Unknown option %s with value %o', fullName, value)
268
+ continue
269
+ }
270
+ // @ts-expect-error - The entry is defined in the configurationsTable.
271
+ entry = configurationsTable.tracing
272
+ }
273
+
274
+ if (entry.nestedProperties) {
275
+ let matched = false
276
+ if (typeof value === 'object' && value !== null) {
277
+ for (const nestedProperty of entry.nestedProperties) {
278
+ // WARNING: if the property name might be part of the value we look at, this could conflict!
279
+ // Defining an option that receives an object as value may not contain a property that is also
280
+ // potentially a nested property!
281
+ if (Object.hasOwn(value, nestedProperty)) {
282
+ this.#applyOptions(value, source, fullName)
283
+ matched = true
284
+ break
285
+ }
286
+ }
287
+ }
288
+ if (matched) {
289
+ continue
290
+ }
291
+ if (entry.option) {
292
+ entry = entry.option
293
+ } else {
294
+ if (fullName === 'tracePropagationStyle') {
295
+ // TracePropagationStyle is special. It is a single option that is used to set both inject and extract.
296
+ // @ts-expect-error - Difficult to type this correctly.
297
+ this.#applyOptions({ inject: value, extract: value }, source, 'tracePropagationStyle')
298
+ } else {
299
+ log.warn('Unknown option %s with value %o', fullName, value)
300
+ }
301
+ continue
302
+ }
303
+ }
304
+ // TODO: Coerce mismatched types to the expected type, if possible. E.g., strings <> numbers
305
+ const transformed = value !== undefined && entry.transformer ? entry.transformer(value, fullName, source) : value
306
+ setAndTrack(this, entry.property, transformed, value, source)
307
+ }
165
308
  }
166
309
 
167
310
  /**
168
311
  * Set the configuration with remote config settings.
169
312
  * Applies remote configuration, recalculates derived values, and merges all configuration sources.
170
313
  *
171
- * @param {import('./remote_config').RemoteConfigOptions|null} options - Configurations received via Remote
314
+ * @param {TracerOptions|null} options - Configurations received via Remote
172
315
  * Config or null to reset all remote configuration
173
316
  */
174
317
  setRemoteConfig (options) {
175
318
  // Clear all RC-managed fields to ensure previous values don't persist.
176
319
  // State is instead managed by the `RCClientLibConfigManager` class
177
- this.#remote = {}
178
- this.#remoteUnprocessed = {}
320
+ undo(this, 'remote_config')
179
321
 
180
322
  // Special case: if options is null, nothing to apply
181
323
  // This happens when all remote configs are removed
182
324
  if (options !== null) {
183
- this.#applyRemoteConfig(options)
325
+ this.#applyOptions(options, 'remote_config')
184
326
  }
185
327
 
186
328
  this.#applyCalculated()
187
- this.#merge()
188
329
  }
189
330
 
190
- // TODO: Remove the `updateOptions` method. We don't want to support updating the config this way
191
331
  /**
192
- * Updates the configuration with new programmatic options.
193
- *
194
- * @deprecated This method should not be used and will be removed in a future version.
195
- * @param {object} options - Configuration options to apply (same format as tracer init options)
332
+ * @param {ConfigPath} name
196
333
  */
197
- updateOptions (options) {
198
- this.#applyOptions(options)
199
- this.#applyCalculated()
200
- this.#merge()
201
- }
202
-
203
334
  getOrigin (name) {
204
- for (const { container, origin } of this.#getSourcesInOrder()) {
205
- const value = container[name]
206
- if (value != null || container === this.#defaults) {
207
- return origin
208
- }
209
- }
210
- }
211
-
212
- #getSourcesInOrder () {
213
- return [
214
- { container: this.#remote, origin: 'remote_config', unprocessed: this.#remoteUnprocessed },
215
- { container: this.#options, origin: 'code', unprocessed: this.#optsUnprocessed },
216
- { container: this.#fleetStableConfig, origin: 'fleet_stable_config' },
217
- { container: this.#env, origin: 'env_var', unprocessed: this.#envUnprocessed },
218
- { container: this.#localStableConfig, origin: 'local_stable_config' },
219
- { container: this.#calculated, origin: 'calculated' },
220
- { container: this.#defaults, origin: 'default' },
221
- ]
222
- }
223
-
224
- #applyStableConfig (config, obj) {
225
- this.#applyConfigValues(config, obj, {})
335
+ return trackedConfigOrigins.get(name) ?? 'default'
226
336
  }
227
337
 
228
- // Set environment-dependent defaults that can be overridden by users
229
- #applyDefaults () {
230
- const defaults = this.#defaults
231
-
232
- if (IS_SERVERLESS) {
233
- setBoolean(defaults, 'crashtracking.enabled', false)
234
- setString(defaults, 'profiling.enabled', 'false')
235
- setBoolean(defaults, 'telemetry.enabled', false)
236
- setBoolean(defaults, 'remoteConfig.enabled', false)
237
- } else {
238
- setBoolean(defaults, 'crashtracking.enabled', true)
338
+ // Handles values calculated from a mixture of options and env vars
339
+ #applyCalculated () {
340
+ undo(this, 'calculated')
341
+
342
+ if (this.DD_CIVISIBILITY_AGENTLESS_URL ||
343
+ this.url ||
344
+ os.type() !== 'Windows_NT' &&
345
+ !trackedConfigOrigins.has('hostname') &&
346
+ !trackedConfigOrigins.has('port') &&
347
+ !this.DD_CIVISIBILITY_AGENTLESS_ENABLED &&
348
+ fs.existsSync('/var/run/datadog/apm.socket')) {
349
+ setAndTrack(
350
+ this,
351
+ 'url',
352
+ new URL(this.DD_CIVISIBILITY_AGENTLESS_URL || this.url || 'unix:///var/run/datadog/apm.socket')
353
+ )
239
354
  }
240
355
 
241
- if (getEnv('JEST_WORKER_ID')) {
242
- setBoolean(defaults, 'telemetry.enabled', false)
356
+ if (this.isCiVisibility) {
357
+ setAndTrack(this, 'isServiceUserProvided', trackedConfigOrigins.has('service'))
358
+ this.tags[ORIGIN_KEY] = 'ciapp-test'
243
359
  }
244
- }
360
+ // Compute OTLP logs and metrics URLs to send payloads to the active Datadog Agent
361
+ const agentHostname = this.hostname || /** @type {URL} */ (this.url).hostname
245
362
 
246
- #applyEnvironment () {
247
- this.#applyConfigValues(getEnvironmentVariables(), this.#env, this.#envUnprocessed)
248
- }
363
+ if (!trackedConfigOrigins.has('dogstatsd.hostname')) {
364
+ setAndTrack(this, 'dogstatsd.hostname', agentHostname)
365
+ }
366
+ // Disable log injection when OTEL logs are enabled
367
+ // OTEL logs and DD log injection are mutually exclusive
368
+ if (this.otelLogsEnabled) {
369
+ setAndTrack(this, 'logInjection', false)
370
+ }
371
+ if (this.otelMetricsEnabled &&
372
+ trackedConfigOrigins.has('OTEL_METRICS_EXPORTER') &&
373
+ this.OTEL_METRICS_EXPORTER === 'none') {
374
+ setAndTrack(this, 'otelMetricsEnabled', false)
375
+ }
249
376
 
250
- #applyConfigValues (source, target, unprocessedTarget) {
251
- const {
252
- AWS_LAMBDA_FUNCTION_NAME,
253
- DD_AGENT_HOST,
254
- DD_AI_GUARD_ENABLED,
255
- DD_AI_GUARD_ENDPOINT,
256
- DD_AI_GUARD_MAX_CONTENT_SIZE,
257
- DD_AI_GUARD_MAX_MESSAGES_LENGTH,
258
- DD_AI_GUARD_TIMEOUT,
259
- DD_API_KEY,
260
- DD_API_SECURITY_ENABLED,
261
- DD_API_SECURITY_SAMPLE_DELAY,
262
- DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED,
263
- DD_API_SECURITY_ENDPOINT_COLLECTION_MESSAGE_LIMIT,
264
- DD_API_SECURITY_DOWNSTREAM_BODY_ANALYSIS_SAMPLE_RATE,
265
- DD_API_SECURITY_MAX_DOWNSTREAM_REQUEST_BODY_ANALYSIS,
266
- DD_APM_TRACING_ENABLED,
267
- DD_APP_KEY,
268
- DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE,
269
- DD_APPSEC_COLLECT_ALL_HEADERS,
270
- DD_APPSEC_ENABLED,
271
- DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON,
272
- DD_APPSEC_HEADER_COLLECTION_REDACTION_ENABLED,
273
- DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML,
274
- DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON,
275
- DD_APPSEC_MAX_COLLECTED_HEADERS,
276
- DD_APPSEC_MAX_STACK_TRACES,
277
- DD_APPSEC_MAX_STACK_TRACE_DEPTH,
278
- DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP,
279
- DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP,
280
- DD_APPSEC_RULES,
281
- DD_APPSEC_SCA_ENABLED,
282
- DD_APPSEC_STACK_TRACE_ENABLED,
283
- DD_APPSEC_RASP_ENABLED,
284
- DD_APPSEC_RASP_COLLECT_REQUEST_BODY,
285
- DD_APPSEC_TRACE_RATE_LIMIT,
286
- DD_APPSEC_WAF_TIMEOUT,
287
- DD_CRASHTRACKING_ENABLED,
288
- DD_CODE_ORIGIN_FOR_SPANS_ENABLED,
289
- DD_CODE_ORIGIN_FOR_SPANS_EXPERIMENTAL_EXIT_SPANS_ENABLED,
290
- DD_DATA_STREAMS_ENABLED,
291
- DD_DBM_PROPAGATION_MODE,
292
- DD_DBM_INJECT_SQL_BASEHASH,
293
- DD_DOGSTATSD_HOST,
294
- DD_DOGSTATSD_PORT,
295
- DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS,
296
- DD_DYNAMIC_INSTRUMENTATION_ENABLED,
297
- DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE,
298
- DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS,
299
- DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS,
300
- DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS,
301
- DD_ENV,
302
- DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED,
303
- DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED,
304
- DD_PROFILING_ENABLED,
305
- DD_GRPC_CLIENT_ERROR_STATUSES,
306
- DD_GRPC_SERVER_ERROR_STATUSES,
307
- DD_HEAP_SNAPSHOT_COUNT,
308
- DD_HEAP_SNAPSHOT_DESTINATION,
309
- DD_HEAP_SNAPSHOT_INTERVAL,
310
- DD_IAST_DB_ROWS_TO_TAINT,
311
- DD_IAST_DEDUPLICATION_ENABLED,
312
- DD_IAST_ENABLED,
313
- DD_IAST_MAX_CONCURRENT_REQUESTS,
314
- DD_IAST_MAX_CONTEXT_OPERATIONS,
315
- DD_IAST_REDACTION_ENABLED,
316
- DD_IAST_REDACTION_NAME_PATTERN,
317
- DD_IAST_REDACTION_VALUE_PATTERN,
318
- DD_IAST_REQUEST_SAMPLING,
319
- DD_IAST_SECURITY_CONTROLS_CONFIGURATION,
320
- DD_IAST_TELEMETRY_VERBOSITY,
321
- DD_IAST_STACK_TRACE_ENABLED,
322
- DD_INJECTION_ENABLED,
323
- DD_INJECT_FORCE,
324
- DD_ENABLE_NX_SERVICE_NAME,
325
- DD_INSTRUMENTATION_TELEMETRY_ENABLED,
326
- DD_INSTRUMENTATION_CONFIG_ID,
327
- DD_LOGS_INJECTION,
328
- DD_LOGS_OTEL_ENABLED,
329
- DD_METRICS_OTEL_ENABLED,
330
- DD_LANGCHAIN_SPAN_CHAR_LIMIT,
331
- DD_LANGCHAIN_SPAN_PROMPT_COMPLETION_SAMPLE_RATE,
332
- DD_LLMOBS_AGENTLESS_ENABLED,
333
- DD_LLMOBS_ENABLED,
334
- DD_LLMOBS_ML_APP,
335
- DD_OPENAI_LOGS_ENABLED,
336
- DD_OPENAI_SPAN_CHAR_LIMIT,
337
- DD_PROFILING_EXPORTERS,
338
- DD_PROFILING_SOURCE_MAP,
339
- DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD,
340
- DD_INSTRUMENTATION_INSTALL_ID,
341
- DD_INSTRUMENTATION_INSTALL_TIME,
342
- DD_INSTRUMENTATION_INSTALL_TYPE,
343
- DD_REMOTE_CONFIGURATION_ENABLED,
344
- DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS,
345
- DD_RUNTIME_METRICS_ENABLED,
346
- DD_RUNTIME_METRICS_EVENT_LOOP_ENABLED,
347
- DD_RUNTIME_METRICS_GC_ENABLED,
348
- DD_SERVICE,
349
- DD_SERVICE_MAPPING,
350
- DD_SITE,
351
- DD_SPAN_SAMPLING_RULES,
352
- DD_SPAN_SAMPLING_RULES_FILE,
353
- DD_TAGS,
354
- DD_TELEMETRY_DEBUG,
355
- DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED,
356
- DD_TELEMETRY_HEARTBEAT_INTERVAL,
357
- DD_TELEMETRY_LOG_COLLECTION_ENABLED,
358
- DD_TELEMETRY_METRICS_ENABLED,
359
- DD_TEST_TIA_KEEP_COV_CONFIG,
360
- DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED,
361
- DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED,
362
- DD_TRACE_AGENT_PORT,
363
- DD_TRACE_AGENT_PROTOCOL_VERSION,
364
- DD_TRACE_AWS_ADD_SPAN_POINTERS,
365
- DD_TRACE_BAGGAGE_MAX_BYTES,
366
- DD_TRACE_BAGGAGE_MAX_ITEMS,
367
- DD_TRACE_BAGGAGE_TAG_KEYS,
368
- DD_TRACE_CLIENT_IP_ENABLED,
369
- DD_TRACE_CLIENT_IP_HEADER,
370
- DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING,
371
- DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING,
372
- DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH,
373
- DD_TRACE_DYNAMODB_TABLE_PRIMARY_KEYS,
374
- DD_TRACE_ENABLED,
375
- DD_TRACE_EXPERIMENTAL_EXPORTER,
376
- DD_TRACE_EXPERIMENTAL_GET_RUM_DATA_ENABLED,
377
- DD_RUNTIME_METRICS_RUNTIME_ID_ENABLED,
378
- DD_TRACE_GIT_METADATA_ENABLED,
379
- DD_TRACE_GRAPHQL_ERROR_EXTENSIONS,
380
- DD_TRACE_HEADER_TAGS,
381
- DD_TRACE_LEGACY_BAGGAGE_ENABLED,
382
- DD_TRACE_MEMCACHED_COMMAND_ENABLED,
383
- DD_TRACE_MIDDLEWARE_TRACING_ENABLED,
384
- DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP,
385
- DD_TRACE_PARTIAL_FLUSH_MIN_SPANS,
386
- DD_TRACE_FLUSH_INTERVAL,
387
- DD_TRACE_PEER_SERVICE_MAPPING,
388
- DD_TRACE_PROPAGATION_EXTRACT_FIRST,
389
- DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT,
390
- DD_TRACE_PROPAGATION_STYLE,
391
- DD_TRACE_PROPAGATION_STYLE_INJECT,
392
- DD_TRACE_PROPAGATION_STYLE_EXTRACT,
393
- DD_TRACE_RATE_LIMIT,
394
- DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED,
395
- DD_TRACE_REPORT_HOSTNAME,
396
- DD_TRACE_RESOURCE_RENAMING_ENABLED,
397
- DD_TRACE_SAMPLE_RATE,
398
- DD_TRACE_SAMPLING_RULES,
399
- DD_TRACE_SCOPE,
400
- DD_TRACE_SPAN_ATTRIBUTE_SCHEMA,
401
- DD_TRACE_SPAN_LEAK_DEBUG,
402
- DD_TRACE_STARTUP_LOGS,
403
- DD_TRACE_TAGS,
404
- DD_TRACE_WEBSOCKET_MESSAGES_ENABLED,
405
- DD_TRACE_WEBSOCKET_MESSAGES_INHERIT_SAMPLING,
406
- DD_TRACE_WEBSOCKET_MESSAGES_SEPARATE_TRACES,
407
- DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH,
408
- DD_TRACING_ENABLED,
409
- DD_VERSION,
410
- DD_VERTEXAI_SPAN_PROMPT_COMPLETION_SAMPLE_RATE,
411
- DD_VERTEXAI_SPAN_CHAR_LIMIT,
412
- DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED,
413
- DD_TRACE_NATIVE_SPAN_EVENTS,
414
- OTEL_METRICS_EXPORTER,
415
- OTEL_PROPAGATORS,
416
- OTEL_RESOURCE_ATTRIBUTES,
417
- OTEL_SERVICE_NAME,
418
- OTEL_TRACES_SAMPLER,
419
- OTEL_TRACES_SAMPLER_ARG,
420
- DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED,
421
- DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS,
422
- OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
423
- OTEL_EXPORTER_OTLP_LOGS_HEADERS,
424
- OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
425
- OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
426
- OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
427
- OTEL_EXPORTER_OTLP_METRICS_HEADERS,
428
- OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
429
- OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
430
- OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE,
431
- OTEL_METRIC_EXPORT_TIMEOUT,
432
- OTEL_EXPORTER_OTLP_PROTOCOL,
433
- OTEL_EXPORTER_OTLP_ENDPOINT,
434
- OTEL_EXPORTER_OTLP_HEADERS,
435
- OTEL_EXPORTER_OTLP_TIMEOUT,
436
- OTEL_BSP_SCHEDULE_DELAY,
437
- OTEL_BSP_MAX_EXPORT_BATCH_SIZE,
438
- OTEL_BSP_MAX_QUEUE_SIZE,
439
- OTEL_METRIC_EXPORT_INTERVAL,
440
- NX_TASK_TARGET_PROJECT,
441
- } = source
442
-
443
- const tags = {}
444
-
445
- tagger.add(tags, parseSpaceSeparatedTags(handleOtel(OTEL_RESOURCE_ATTRIBUTES)))
446
- tagger.add(tags, parseSpaceSeparatedTags(DD_TAGS))
447
- tagger.add(tags, DD_TRACE_TAGS)
448
-
449
- Object.assign(this.#parsedDdTags, tags)
450
-
451
- setString(target, 'apiKey', DD_API_KEY)
452
- setBoolean(target, 'otelLogsEnabled', DD_LOGS_OTEL_ENABLED)
453
- // Set OpenTelemetry logs configuration with specific _LOGS_ vars taking precedence over generic _EXPORTERS_ vars
454
- if (OTEL_EXPORTER_OTLP_ENDPOINT) {
455
- // Only set if there's a custom URL, otherwise let calc phase handle the default
456
- setString(target, 'otelUrl', OTEL_EXPORTER_OTLP_ENDPOINT)
377
+ if (this.telemetry.heartbeatInterval) {
378
+ setAndTrack(this, 'telemetry.heartbeatInterval', Math.floor(this.telemetry.heartbeatInterval * 1000))
457
379
  }
458
- if (OTEL_EXPORTER_OTLP_ENDPOINT || OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) {
459
- setString(target, 'otelLogsUrl', OTEL_EXPORTER_OTLP_LOGS_ENDPOINT || target.otelUrl)
380
+ if (this.telemetry.extendedHeartbeatInterval) {
381
+ setAndTrack(this, 'telemetry.extendedHeartbeatInterval',
382
+ Math.floor(this.telemetry.extendedHeartbeatInterval * 1000))
460
383
  }
461
- setString(target, 'otelHeaders', OTEL_EXPORTER_OTLP_HEADERS)
462
- setString(target, 'otelLogsHeaders', OTEL_EXPORTER_OTLP_LOGS_HEADERS || target.otelHeaders)
463
- setString(target, 'otelProtocol', OTEL_EXPORTER_OTLP_PROTOCOL)
464
- setString(target, 'otelLogsProtocol', OTEL_EXPORTER_OTLP_LOGS_PROTOCOL || target.otelProtocol)
465
- const otelTimeout = nonNegInt(OTEL_EXPORTER_OTLP_TIMEOUT, 'OTEL_EXPORTER_OTLP_TIMEOUT')
466
- if (otelTimeout !== undefined) {
467
- target.otelTimeout = otelTimeout
384
+
385
+ // Enable resourceRenamingEnabled when appsec is enabled and only
386
+ // if DD_TRACE_RESOURCE_RENAMING_ENABLED is not explicitly set
387
+ if (!trackedConfigOrigins.has('resourceRenamingEnabled')) {
388
+ setAndTrack(this, 'resourceRenamingEnabled', this.appsec.enabled ?? false)
468
389
  }
469
- const otelLogsTimeout = nonNegInt(OTEL_EXPORTER_OTLP_LOGS_TIMEOUT, 'OTEL_EXPORTER_OTLP_LOGS_TIMEOUT')
470
- target.otelLogsTimeout = otelLogsTimeout === undefined ? target.otelTimeout : otelLogsTimeout
471
- const otelBatchTimeout = nonNegInt(OTEL_BSP_SCHEDULE_DELAY, 'OTEL_BSP_SCHEDULE_DELAY', false)
472
- if (otelBatchTimeout !== undefined) {
473
- target.otelBatchTimeout = otelBatchTimeout
390
+
391
+ if (!trackedConfigOrigins.has('spanComputePeerService') && this.spanAttributeSchema !== 'v0') {
392
+ setAndTrack(this, 'spanComputePeerService', true)
474
393
  }
475
- target.otelMaxExportBatchSize = nonNegInt(OTEL_BSP_MAX_EXPORT_BATCH_SIZE, 'OTEL_BSP_MAX_EXPORT_BATCH_SIZE', false)
476
- target.otelMaxQueueSize = nonNegInt(OTEL_BSP_MAX_QUEUE_SIZE, 'OTEL_BSP_MAX_QUEUE_SIZE', false)
477
-
478
- const otelMetricsExporterEnabled = OTEL_METRICS_EXPORTER?.toLowerCase() !== 'none'
479
- setBoolean(
480
- target,
481
- 'otelMetricsEnabled',
482
- DD_METRICS_OTEL_ENABLED && isTrue(DD_METRICS_OTEL_ENABLED) && otelMetricsExporterEnabled
483
- )
484
- // Set OpenTelemetry metrics configuration with specific _METRICS_ vars
485
- // taking precedence over generic _EXPORTERS_ vars
486
- if (OTEL_EXPORTER_OTLP_ENDPOINT || OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) {
487
- setString(target, 'otelMetricsUrl', OTEL_EXPORTER_OTLP_METRICS_ENDPOINT || target.otelUrl)
394
+
395
+ if (!this.apmTracingEnabled) {
396
+ setAndTrack(this, 'stats.enabled', false)
397
+ } else if (!trackedConfigOrigins.has('stats.enabled')) {
398
+ setAndTrack(this, 'stats.enabled', getIsGCPFunction() || getIsAzureFunction())
488
399
  }
489
- setString(target, 'otelMetricsHeaders', OTEL_EXPORTER_OTLP_METRICS_HEADERS || target.otelHeaders)
490
- setString(target, 'otelMetricsProtocol', OTEL_EXPORTER_OTLP_METRICS_PROTOCOL || target.otelProtocol)
491
- const otelMetricsTimeout = nonNegInt(OTEL_EXPORTER_OTLP_METRICS_TIMEOUT, 'OTEL_EXPORTER_OTLP_METRICS_TIMEOUT')
492
- target.otelMetricsTimeout = otelMetricsTimeout === undefined ? target.otelTimeout : otelMetricsTimeout
493
- target.otelMetricsExportTimeout = nonNegInt(OTEL_METRIC_EXPORT_TIMEOUT, 'OTEL_METRIC_EXPORT_TIMEOUT')
494
- target.otelMetricsExportInterval = nonNegInt(OTEL_METRIC_EXPORT_INTERVAL, 'OTEL_METRIC_EXPORT_INTERVAL', false)
495
-
496
- // Parse temporality preference (default to DELTA for Datadog)
497
- if (OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE) {
498
- const temporalityPref = OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE.toUpperCase()
499
- if (['DELTA', 'CUMULATIVE', 'LOWMEMORY'].includes(temporalityPref)) {
500
- setString(target, 'otelMetricsTemporalityPreference', temporalityPref)
400
+
401
+ // TODO: Remove the experimental env vars as a major or deprecate the option?
402
+ if (this.experimental?.b3) {
403
+ if (!this.tracePropagationStyle.inject.includes('b3')) {
404
+ this.tracePropagationStyle.inject.push('b3')
501
405
  }
502
- }
503
- setBoolean(
504
- target,
505
- 'apmTracingEnabled',
506
- DD_APM_TRACING_ENABLED ??
507
- (DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED && isFalse(DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED))
508
- )
509
- setBoolean(target, 'propagateProcessTags.enabled', DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED)
510
- setString(target, 'appKey', DD_APP_KEY)
511
- setBoolean(target, 'appsec.apiSecurity.enabled', DD_API_SECURITY_ENABLED && isTrue(DD_API_SECURITY_ENABLED))
512
- target['appsec.apiSecurity.sampleDelay'] = maybeFloat(DD_API_SECURITY_SAMPLE_DELAY)
513
- setBoolean(target, 'appsec.apiSecurity.endpointCollectionEnabled',
514
- DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED)
515
- target['appsec.apiSecurity.endpointCollectionMessageLimit'] =
516
- maybeInt(DD_API_SECURITY_ENDPOINT_COLLECTION_MESSAGE_LIMIT)
517
- target['appsec.blockedTemplateGraphql'] = maybeFile(DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON)
518
- target['appsec.blockedTemplateHtml'] = maybeFile(DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML)
519
- unprocessedTarget['appsec.blockedTemplateHtml'] = DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML
520
- target['appsec.blockedTemplateJson'] = maybeFile(DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON)
521
- unprocessedTarget['appsec.blockedTemplateJson'] = DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON
522
- setBoolean(target, 'appsec.enabled', DD_APPSEC_ENABLED)
523
- setString(target, 'appsec.eventTracking.mode', DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE)
524
- // TODO appsec.extendedHeadersCollection are deprecated, to delete in a major
525
- setBoolean(target, 'appsec.extendedHeadersCollection.enabled', DD_APPSEC_COLLECT_ALL_HEADERS)
526
- setBoolean(
527
- target,
528
- 'appsec.extendedHeadersCollection.redaction',
529
- DD_APPSEC_HEADER_COLLECTION_REDACTION_ENABLED
530
- )
531
- target['appsec.extendedHeadersCollection.maxHeaders'] = maybeInt(DD_APPSEC_MAX_COLLECTED_HEADERS)
532
- unprocessedTarget['appsec.extendedHeadersCollection.maxHeaders'] = DD_APPSEC_MAX_COLLECTED_HEADERS
533
- setString(target, 'appsec.obfuscatorKeyRegex', DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP)
534
- setString(target, 'appsec.obfuscatorValueRegex', DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP)
535
- setBoolean(target, 'appsec.rasp.enabled', DD_APPSEC_RASP_ENABLED)
536
- // TODO Deprecated, to delete in a major
537
- setBoolean(target, 'appsec.rasp.bodyCollection', DD_APPSEC_RASP_COLLECT_REQUEST_BODY)
538
- target['appsec.rateLimit'] = maybeInt(DD_APPSEC_TRACE_RATE_LIMIT)
539
- unprocessedTarget['appsec.rateLimit'] = DD_APPSEC_TRACE_RATE_LIMIT
540
- setString(target, 'appsec.rules', DD_APPSEC_RULES)
541
- // DD_APPSEC_SCA_ENABLED is never used locally, but only sent to the backend
542
- setBoolean(target, 'appsec.sca.enabled', DD_APPSEC_SCA_ENABLED)
543
- setBoolean(target, 'appsec.stackTrace.enabled', DD_APPSEC_STACK_TRACE_ENABLED)
544
- target['appsec.stackTrace.maxDepth'] = maybeInt(DD_APPSEC_MAX_STACK_TRACE_DEPTH)
545
- unprocessedTarget['appsec.stackTrace.maxDepth'] = DD_APPSEC_MAX_STACK_TRACE_DEPTH
546
- target['appsec.stackTrace.maxStackTraces'] = maybeInt(DD_APPSEC_MAX_STACK_TRACES)
547
- unprocessedTarget['appsec.stackTrace.maxStackTraces'] = DD_APPSEC_MAX_STACK_TRACES
548
- target['appsec.wafTimeout'] = maybeInt(DD_APPSEC_WAF_TIMEOUT)
549
- unprocessedTarget['appsec.wafTimeout'] = DD_APPSEC_WAF_TIMEOUT
550
- target['appsec.apiSecurity.downstreamBodyAnalysisSampleRate'] =
551
- maybeFloat(DD_API_SECURITY_DOWNSTREAM_BODY_ANALYSIS_SAMPLE_RATE)
552
- target['appsec.apiSecurity.maxDownstreamRequestBodyAnalysis'] =
553
- maybeInt(DD_API_SECURITY_MAX_DOWNSTREAM_REQUEST_BODY_ANALYSIS)
554
- target.baggageMaxBytes = DD_TRACE_BAGGAGE_MAX_BYTES
555
- target.baggageMaxItems = DD_TRACE_BAGGAGE_MAX_ITEMS
556
- setArray(target, 'baggageTagKeys', DD_TRACE_BAGGAGE_TAG_KEYS)
557
- setBoolean(target, 'clientIpEnabled', DD_TRACE_CLIENT_IP_ENABLED)
558
- setString(target, 'clientIpHeader', DD_TRACE_CLIENT_IP_HEADER?.toLowerCase())
559
- if (DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING || DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING) {
560
- if (DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING) {
561
- setBoolean(target, 'cloudPayloadTagging.requestsEnabled', true)
406
+ if (!this.tracePropagationStyle.extract.includes('b3')) {
407
+ this.tracePropagationStyle.extract.push('b3')
562
408
  }
563
- if (DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING) {
564
- setBoolean(target, 'cloudPayloadTagging.responsesEnabled', true)
409
+ if (!this.tracePropagationStyle.inject.includes('b3 single header')) {
410
+ this.tracePropagationStyle.inject.push('b3 single header')
565
411
  }
566
- target['cloudPayloadTagging.rules'] = appendRules(
567
- splitJSONPathRules(DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING),
568
- splitJSONPathRules(DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING)
569
- )
570
- }
571
- if (DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH) {
572
- target['cloudPayloadTagging.maxDepth'] = maybeInt(DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH)
573
- }
574
- setBoolean(target, 'crashtracking.enabled', DD_CRASHTRACKING_ENABLED)
575
- setBoolean(target, 'codeOriginForSpans.enabled', DD_CODE_ORIGIN_FOR_SPANS_ENABLED)
576
- setBoolean(
577
- target,
578
- 'codeOriginForSpans.experimental.exit_spans.enabled',
579
- DD_CODE_ORIGIN_FOR_SPANS_EXPERIMENTAL_EXIT_SPANS_ENABLED
580
- )
581
- setString(target, 'dbmPropagationMode', DD_DBM_PROPAGATION_MODE)
582
- setBoolean(target, 'dbm.injectSqlBaseHash', DD_DBM_INJECT_SQL_BASEHASH)
583
- setString(target, 'dogstatsd.hostname', DD_DOGSTATSD_HOST)
584
- setString(target, 'dogstatsd.port', DD_DOGSTATSD_PORT)
585
- setBoolean(target, 'dsmEnabled', DD_DATA_STREAMS_ENABLED)
586
- target['dynamicInstrumentation.captureTimeoutMs'] = maybeInt(DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS)
587
- unprocessedTarget['dynamicInstrumentation.captureTimeoutMs'] = DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS
588
- setBoolean(target, 'dynamicInstrumentation.enabled', DD_DYNAMIC_INSTRUMENTATION_ENABLED)
589
- setString(target, 'dynamicInstrumentation.probeFile', DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE)
590
- setArray(target, 'dynamicInstrumentation.redactedIdentifiers',
591
- DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS)
592
- setArray(
593
- target,
594
- 'dynamicInstrumentation.redactionExcludedIdentifiers',
595
- DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS
596
- )
597
- target['dynamicInstrumentation.uploadIntervalSeconds'] =
598
- maybeFloat(DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS)
599
- unprocessedTarget['dynamicInstrumentation.uploadInterval'] = DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS
600
- setString(target, 'env', DD_ENV || tags.env)
601
- setBoolean(
602
- target,
603
- 'experimental.flaggingProvider.enabled',
604
- DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED
605
- )
606
- if (DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS != null) {
607
- target['experimental.flaggingProvider.initializationTimeoutMs'] =
608
- maybeInt(DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS)
412
+ if (!this.tracePropagationStyle.extract.includes('b3 single header')) {
413
+ this.tracePropagationStyle.extract.push('b3 single header')
414
+ }
415
+ setAndTrack(this, 'tracePropagationStyle.inject', this.tracePropagationStyle.inject)
416
+ setAndTrack(this, 'tracePropagationStyle.extract', this.tracePropagationStyle.extract)
609
417
  }
610
- setBoolean(target, 'traceEnabled', DD_TRACE_ENABLED)
611
- setBoolean(target, 'experimental.aiguard.enabled', DD_AI_GUARD_ENABLED)
612
- setString(target, 'experimental.aiguard.endpoint', DD_AI_GUARD_ENDPOINT)
613
- target['experimental.aiguard.maxContentSize'] = maybeInt(DD_AI_GUARD_MAX_CONTENT_SIZE)
614
- unprocessedTarget['experimental.aiguard.maxContentSize'] = DD_AI_GUARD_MAX_CONTENT_SIZE
615
- target['experimental.aiguard.maxMessagesLength'] = maybeInt(DD_AI_GUARD_MAX_MESSAGES_LENGTH)
616
- unprocessedTarget['experimental.aiguard.maxMessagesLength'] = DD_AI_GUARD_MAX_MESSAGES_LENGTH
617
- target['experimental.aiguard.timeout'] = maybeInt(DD_AI_GUARD_TIMEOUT)
618
- unprocessedTarget['experimental.aiguard.timeout'] = DD_AI_GUARD_TIMEOUT
619
- setBoolean(target, 'experimental.enableGetRumData', DD_TRACE_EXPERIMENTAL_GET_RUM_DATA_ENABLED)
620
- setString(target, 'experimental.exporter', DD_TRACE_EXPERIMENTAL_EXPORTER)
621
- if (AWS_LAMBDA_FUNCTION_NAME) {
622
- target.flushInterval = 0
623
- } else if (DD_TRACE_FLUSH_INTERVAL) {
624
- target.flushInterval = maybeInt(DD_TRACE_FLUSH_INTERVAL)
418
+
419
+ if (getEnvironmentVariable('AWS_LAMBDA_FUNCTION_NAME') && !fs.existsSync(DATADOG_MINI_AGENT_PATH)) {
420
+ setAndTrack(this, 'flushInterval', 0)
625
421
  }
626
- target.flushMinSpans = maybeInt(DD_TRACE_PARTIAL_FLUSH_MIN_SPANS)
627
- unprocessedTarget.flushMinSpans = DD_TRACE_PARTIAL_FLUSH_MIN_SPANS
628
- setBoolean(target, 'gitMetadataEnabled', DD_TRACE_GIT_METADATA_ENABLED)
629
- setIntegerRangeSet(target, 'grpc.client.error.statuses', DD_GRPC_CLIENT_ERROR_STATUSES)
630
- setIntegerRangeSet(target, 'grpc.server.error.statuses', DD_GRPC_SERVER_ERROR_STATUSES)
631
- setArray(target, 'headerTags', DD_TRACE_HEADER_TAGS)
632
- target['heapSnapshot.count'] = maybeInt(DD_HEAP_SNAPSHOT_COUNT)
633
- setString(target, 'heapSnapshot.destination', DD_HEAP_SNAPSHOT_DESTINATION)
634
- target['heapSnapshot.interval'] = maybeInt(DD_HEAP_SNAPSHOT_INTERVAL)
635
- setString(target, 'hostname', DD_AGENT_HOST)
636
- target['iast.dbRowsToTaint'] = maybeInt(DD_IAST_DB_ROWS_TO_TAINT)
637
- setBoolean(target, 'iast.deduplicationEnabled', DD_IAST_DEDUPLICATION_ENABLED)
638
- setBoolean(target, 'iast.enabled', DD_IAST_ENABLED)
639
- target['iast.maxConcurrentRequests'] = maybeInt(DD_IAST_MAX_CONCURRENT_REQUESTS)
640
- unprocessedTarget['iast.maxConcurrentRequests'] = DD_IAST_MAX_CONCURRENT_REQUESTS
641
- target['iast.maxContextOperations'] = maybeInt(DD_IAST_MAX_CONTEXT_OPERATIONS)
642
- unprocessedTarget['iast.maxContextOperations'] = DD_IAST_MAX_CONTEXT_OPERATIONS
643
- setBoolean(target, 'iast.redactionEnabled', DD_IAST_REDACTION_ENABLED && !isFalse(DD_IAST_REDACTION_ENABLED))
644
- setString(target, 'iast.redactionNamePattern', DD_IAST_REDACTION_NAME_PATTERN)
645
- setString(target, 'iast.redactionValuePattern', DD_IAST_REDACTION_VALUE_PATTERN)
646
- const iastRequestSampling = maybeInt(DD_IAST_REQUEST_SAMPLING)
647
- if (iastRequestSampling !== undefined && iastRequestSampling > -1 && iastRequestSampling < 101) {
648
- target['iast.requestSampling'] = iastRequestSampling
422
+
423
+ if (!trackedConfigOrigins.has('apmTracingEnabled') &&
424
+ trackedConfigOrigins.has('experimental.appsec.standalone.enabled')) {
425
+ setAndTrack(this, 'apmTracingEnabled', !this.experimental.appsec.standalone.enabled)
649
426
  }
650
- unprocessedTarget['iast.requestSampling'] = DD_IAST_REQUEST_SAMPLING
651
- setString(target, 'iast.securityControlsConfiguration', DD_IAST_SECURITY_CONTROLS_CONFIGURATION)
652
- setString(target, 'iast.telemetryVerbosity', DD_IAST_TELEMETRY_VERBOSITY)
653
- setBoolean(target, 'iast.stackTrace.enabled', DD_IAST_STACK_TRACE_ENABLED)
654
- setString(target, 'installSignature.id', DD_INSTRUMENTATION_INSTALL_ID)
655
- setString(target, 'installSignature.time', DD_INSTRUMENTATION_INSTALL_TIME)
656
- setString(target, 'installSignature.type', DD_INSTRUMENTATION_INSTALL_TYPE)
657
- // TODO: Why is DD_INJECTION_ENABLED a comma separated list?
658
- setArray(target, 'injectionEnabled', DD_INJECTION_ENABLED)
659
- if (DD_INJECTION_ENABLED !== undefined) {
660
- setString(target, 'instrumentationSource', DD_INJECTION_ENABLED ? 'ssi' : 'manual')
427
+
428
+ if (this.cloudPayloadTagging?.request || this.cloudPayloadTagging?.response) {
429
+ setAndTrack(this, 'cloudPayloadTagging.rules', appendRules(
430
+ this.cloudPayloadTagging.request,
431
+ this.cloudPayloadTagging.response
432
+ ))
661
433
  }
662
- setBoolean(target, 'injectForce', DD_INJECT_FORCE)
663
- setBoolean(target, 'isAzureFunction', getIsAzureFunction())
664
- setBoolean(target, 'isGCPFunction', getIsGCPFunction())
665
- setBoolean(target, 'gcpPubSubPushSubscriptionEnabled', enableGCPPubSubPushSubscription())
666
- target['langchain.spanCharLimit'] = maybeInt(DD_LANGCHAIN_SPAN_CHAR_LIMIT)
667
- target['langchain.spanPromptCompletionSampleRate'] = maybeFloat(DD_LANGCHAIN_SPAN_PROMPT_COMPLETION_SAMPLE_RATE)
668
- setBoolean(target, 'legacyBaggageEnabled', DD_TRACE_LEGACY_BAGGAGE_ENABLED)
669
- setBoolean(target, 'llmobs.agentlessEnabled', DD_LLMOBS_AGENTLESS_ENABLED)
670
- setBoolean(target, 'llmobs.enabled', DD_LLMOBS_ENABLED)
671
- setString(target, 'llmobs.mlApp', DD_LLMOBS_ML_APP)
672
- setBoolean(target, 'logInjection', DD_LOGS_INJECTION)
673
- // Requires an accompanying DD_APM_OBFUSCATION_MEMCACHED_KEEP_COMMAND=true in the agent
674
- setBoolean(target, 'memcachedCommandEnabled', DD_TRACE_MEMCACHED_COMMAND_ENABLED)
675
- setBoolean(target, 'middlewareTracingEnabled', DD_TRACE_MIDDLEWARE_TRACING_ENABLED)
676
- setBoolean(target, 'openAiLogsEnabled', DD_OPENAI_LOGS_ENABLED)
677
- target['openai.spanCharLimit'] = maybeInt(DD_OPENAI_SPAN_CHAR_LIMIT)
678
- unprocessedTarget.openaiSpanCharLimit = DD_OPENAI_SPAN_CHAR_LIMIT
679
- if (DD_TRACE_PEER_SERVICE_MAPPING) {
680
- target.peerServiceMapping = Object.fromEntries(
681
- DD_TRACE_PEER_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
682
- )
683
- unprocessedTarget.peerServiceMapping = DD_TRACE_PEER_SERVICE_MAPPING
434
+
435
+ if (this.injectionEnabled) {
436
+ setAndTrack(this, 'instrumentationSource', 'ssi')
684
437
  }
685
- setString(target, 'port', DD_TRACE_AGENT_PORT)
686
- const profilingEnabled = normalizeProfilingEnabledValue(DD_PROFILING_ENABLED)
687
- setString(target, 'profiling.enabled', profilingEnabled)
688
- setString(target, 'profiling.exporters', DD_PROFILING_EXPORTERS)
689
- setBoolean(target, 'profiling.sourceMap', DD_PROFILING_SOURCE_MAP && !isFalse(DD_PROFILING_SOURCE_MAP))
690
- if (DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD) {
691
- // This is only used in testing to not have to wait 30s
692
- target['profiling.longLivedThreshold'] = Number(DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD)
438
+
439
+ if (!trackedConfigOrigins.has('runtimeMetrics.enabled') && this.OTEL_METRICS_EXPORTER === 'none') {
440
+ setAndTrack(this, 'runtimeMetrics.enabled', false)
693
441
  }
694
442
 
695
- setString(target, 'protocolVersion', DD_TRACE_AGENT_PROTOCOL_VERSION)
696
- setString(target, 'queryStringObfuscation', DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP)
697
- setBoolean(target, 'remoteConfig.enabled', DD_REMOTE_CONFIGURATION_ENABLED)
698
- target['remoteConfig.pollInterval'] = maybeFloat(DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS)
699
- unprocessedTarget['remoteConfig.pollInterval'] = DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS
700
- setBoolean(target, 'reportHostname', DD_TRACE_REPORT_HOSTNAME)
701
- if (DD_TRACE_RESOURCE_RENAMING_ENABLED !== undefined) {
702
- setBoolean(target, 'resourceRenamingEnabled', DD_TRACE_RESOURCE_RENAMING_ENABLED)
443
+ if (!trackedConfigOrigins.has('sampleRate') && trackedConfigOrigins.has('OTEL_TRACES_SAMPLER')) {
444
+ setAndTrack(this, 'sampleRate', getFromOtelSamplerMap(this.OTEL_TRACES_SAMPLER, this.OTEL_TRACES_SAMPLER_ARG))
703
445
  }
704
- // only used to explicitly set runtimeMetrics to false
705
- const otelSetRuntimeMetrics = String(OTEL_METRICS_EXPORTER).toLowerCase() === 'none'
706
- ? false
707
- : undefined
708
- setBoolean(target, 'runtimeMetrics.enabled', DD_RUNTIME_METRICS_ENABLED ||
709
- otelSetRuntimeMetrics)
710
- setBoolean(target, 'runtimeMetrics.eventLoop', DD_RUNTIME_METRICS_EVENT_LOOP_ENABLED)
711
- setBoolean(target, 'runtimeMetrics.gc', DD_RUNTIME_METRICS_GC_ENABLED)
712
- setBoolean(target, 'runtimeMetricsRuntimeId', DD_RUNTIME_METRICS_RUNTIME_ID_ENABLED)
713
- setArray(target, 'sampler.spanSamplingRules', reformatSpanSamplingRules(
714
- maybeJsonFile(DD_SPAN_SAMPLING_RULES_FILE) ??
715
- safeJsonParse(DD_SPAN_SAMPLING_RULES)
716
- ))
717
- setUnit(
718
- target,
719
- 'sampleRate',
720
- DD_TRACE_SAMPLE_RATE || getFromOtelSamplerMap(OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG)
721
- )
722
- target['sampler.rateLimit'] = DD_TRACE_RATE_LIMIT
723
- setSamplingRule(target, 'sampler.rules', safeJsonParse(DD_TRACE_SAMPLING_RULES))
724
- unprocessedTarget['sampler.rules'] = DD_TRACE_SAMPLING_RULES
725
- setString(target, 'scope', DD_TRACE_SCOPE)
726
- // Priority:
727
- // DD_SERVICE > tags.service > OTEL_SERVICE_NAME > NX_TASK_TARGET_PROJECT (if DD_ENABLE_NX_SERVICE_NAME) > default
728
- let serviceName = DD_SERVICE || tags.service || OTEL_SERVICE_NAME
729
- let isServiceNameInferred
730
- if (!serviceName && NX_TASK_TARGET_PROJECT) {
731
- if (isTrue(DD_ENABLE_NX_SERVICE_NAME)) {
732
- isServiceNameInferred = true
733
- serviceName = NX_TASK_TARGET_PROJECT
734
- } else if (DD_MAJOR < 6) {
735
- // Warn about v6 behavior change for Nx projects
736
- log.warn(
737
- // eslint-disable-next-line @stylistic/max-len
738
- 'NX_TASK_TARGET_PROJECT is set but no service name was configured. In v6, NX_TASK_TARGET_PROJECT will be used as the default service name. Set DD_ENABLE_NX_SERVICE_NAME=true to opt-in to this behavior now, or set a service name explicitly.'
739
- )
446
+
447
+ if (this.DD_SPAN_SAMPLING_RULES_FILE) {
448
+ try {
449
+ // TODO: Should we log a warning in case this is defined next to spanSamplingRules?
450
+ setAndTrack(this, 'spanSamplingRules', transformers.toCamelCase(JSON.parse(this.DD_SPAN_SAMPLING_RULES_FILE)))
451
+ } catch (error) {
452
+ log.warn('Error reading span sampling rules file %s; %o', this.DD_SPAN_SAMPLING_RULES_FILE, error)
740
453
  }
741
454
  }
742
- setString(target, 'service', serviceName)
743
- if (serviceName) setBoolean(target, 'isServiceNameInferred', isServiceNameInferred ?? false)
744
- if (DD_SERVICE_MAPPING) {
745
- target.serviceMapping = Object.fromEntries(
746
- DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
747
- )
748
- }
749
- setString(target, 'site', DD_SITE)
750
- if (DD_TRACE_SPAN_ATTRIBUTE_SCHEMA) {
751
- setString(target, 'spanAttributeSchema', validateNamingVersion(DD_TRACE_SPAN_ATTRIBUTE_SCHEMA))
752
- unprocessedTarget.spanAttributeSchema = DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
753
- }
754
- // 0: disabled, 1: logging, 2: garbage collection + logging
755
- target.spanLeakDebug = maybeInt(DD_TRACE_SPAN_LEAK_DEBUG)
756
- setBoolean(target, 'spanRemoveIntegrationFromService', DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED)
757
- setBoolean(target, 'startupLogs', DD_TRACE_STARTUP_LOGS)
758
- setTags(target, 'tags', tags)
759
- target.tagsHeaderMaxLength = DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH
760
- setBoolean(target, 'telemetry.enabled', DD_INSTRUMENTATION_TELEMETRY_ENABLED)
761
- setString(target, 'instrumentation_config_id', DD_INSTRUMENTATION_CONFIG_ID)
762
- setBoolean(target, 'telemetry.debug', DD_TELEMETRY_DEBUG)
763
- setBoolean(target, 'telemetry.dependencyCollection', DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED)
764
- target['telemetry.heartbeatInterval'] = maybeInt(Math.floor(DD_TELEMETRY_HEARTBEAT_INTERVAL * 1000))
765
- unprocessedTarget['telemetry.heartbeatInterval'] = DD_TELEMETRY_HEARTBEAT_INTERVAL
766
- setBoolean(target, 'telemetry.logCollection', DD_TELEMETRY_LOG_COLLECTION_ENABLED)
767
- setBoolean(target, 'telemetry.metrics', DD_TELEMETRY_METRICS_ENABLED)
768
- setBoolean(target, 'isKeepingCoverageConfiguration', DD_TEST_TIA_KEEP_COV_CONFIG)
769
- setBoolean(target, 'traceId128BitGenerationEnabled', DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED)
770
- setBoolean(target, 'traceId128BitLoggingEnabled', DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED)
771
- warnIfPropagationStyleConflict(
772
- DD_TRACE_PROPAGATION_STYLE,
773
- DD_TRACE_PROPAGATION_STYLE_INJECT,
774
- DD_TRACE_PROPAGATION_STYLE_EXTRACT
775
- )
776
- if (DD_TRACE_PROPAGATION_STYLE !== undefined) {
777
- setArray(target, 'tracePropagationStyle.inject', normalizePropagationStyle(DD_TRACE_PROPAGATION_STYLE))
778
- setArray(target, 'tracePropagationStyle.extract', normalizePropagationStyle(DD_TRACE_PROPAGATION_STYLE))
779
- }
780
- if (DD_TRACE_PROPAGATION_STYLE_INJECT !== undefined) {
781
- setArray(target, 'tracePropagationStyle.inject',
782
- normalizePropagationStyle(DD_TRACE_PROPAGATION_STYLE_INJECT))
455
+
456
+ // All sampler options are tracked as individual values. No need to track the sampler object as a whole.
457
+ this.sampler = {
458
+ rules: this.samplingRules,
459
+ rateLimit: this.rateLimit,
460
+ sampleRate: this.sampleRate,
461
+ spanSamplingRules: this.spanSamplingRules,
783
462
  }
784
- if (DD_TRACE_PROPAGATION_STYLE_EXTRACT !== undefined) {
785
- setArray(target, 'tracePropagationStyle.extract',
786
- normalizePropagationStyle(DD_TRACE_PROPAGATION_STYLE_EXTRACT))
463
+
464
+ // For LLMObs, we want to auto enable it when other llmobs options are defined.
465
+ if (!this.llmobs.enabled &&
466
+ !trackedConfigOrigins.has('llmobs.enabled') &&
467
+ (trackedConfigOrigins.has('llmobs.agentlessEnabled') ||
468
+ trackedConfigOrigins.has('llmobs.mlApp'))) {
469
+ setAndTrack(this, 'llmobs.enabled', true)
787
470
  }
788
- setBoolean(target, 'tracePropagationExtractFirst', DD_TRACE_PROPAGATION_EXTRACT_FIRST)
789
- if (DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT !== undefined) {
790
- const stringPropagationBehaviorExtract = String(DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT)
791
- target.tracePropagationBehaviorExtract =
792
- VALID_PROPAGATION_BEHAVIOR_EXTRACT.has(stringPropagationBehaviorExtract)
793
- ? stringPropagationBehaviorExtract
794
- : 'continue'
471
+
472
+ if (this.OTEL_RESOURCE_ATTRIBUTES) {
473
+ for (const [key, value] of Object.entries(this.OTEL_RESOURCE_ATTRIBUTES)) {
474
+ // Not replacing existing tags keeps the order of the tags as before.
475
+ if (!this.tags[key]) {
476
+ this.tags[key] = value
477
+ }
478
+ }
795
479
  }
796
- if (DD_TRACE_PROPAGATION_STYLE !== undefined ||
797
- DD_TRACE_PROPAGATION_STYLE_INJECT !== undefined ||
798
- DD_TRACE_PROPAGATION_STYLE_EXTRACT !== undefined ||
799
- OTEL_PROPAGATORS !== undefined) {
800
- // At least one var is defined, calculate value using truthy logic
801
- const useDdStyle = DD_TRACE_PROPAGATION_STYLE ||
802
- DD_TRACE_PROPAGATION_STYLE_INJECT ||
803
- DD_TRACE_PROPAGATION_STYLE_EXTRACT
804
- setBoolean(target, 'tracePropagationStyle.otelPropagators',
805
- useDdStyle ? false : !!OTEL_PROPAGATORS)
806
-
807
- // Use OTEL_PROPAGATORS if no DD-specific vars are set
808
- if (!useDdStyle && OTEL_PROPAGATORS) {
809
- const otelStyles = normalizePropagationStyle(OTEL_PROPAGATORS)
810
- // Validate OTEL propagators
811
- for (const style of otelStyles || []) {
812
- if (!VALID_PROPAGATION_STYLES.has(style)) {
813
- log.warn('unexpected value %s for OTEL_PROPAGATORS environment variable', style)
814
- getCounter('otel.env.invalid', 'DD_TRACE_PROPAGATION_STYLE', 'OTEL_PROPAGATORS').inc()
480
+ if (this.DD_TRACE_TAGS) {
481
+ // TODO: This is a hack to keep the order of the tags as before.
482
+ // That hack is not sufficient, since it does not handle other cases where the tags are set by the user.
483
+ if (trackedConfigOrigins.get('tags') === 'code') {
484
+ for (const [key, value] of Object.entries(this.DD_TRACE_TAGS)) {
485
+ // Not replacing existing tags keeps the order of the tags as before.
486
+ if (!this.tags[key]) {
487
+ this.tags[key] = value
815
488
  }
816
489
  }
817
- // Set inject/extract from OTEL_PROPAGATORS
818
- if (otelStyles) {
819
- setArray(target, 'tracePropagationStyle.inject', otelStyles)
820
- setArray(target, 'tracePropagationStyle.extract', otelStyles)
821
- }
490
+ } else {
491
+ Object.assign(this.tags, this.DD_TRACE_TAGS)
822
492
  }
823
493
  }
824
- setBoolean(target, 'traceWebsocketMessagesEnabled', DD_TRACE_WEBSOCKET_MESSAGES_ENABLED)
825
- setBoolean(target, 'traceWebsocketMessagesInheritSampling', DD_TRACE_WEBSOCKET_MESSAGES_INHERIT_SAMPLING)
826
- setBoolean(target, 'traceWebsocketMessagesSeparateTraces', DD_TRACE_WEBSOCKET_MESSAGES_SEPARATE_TRACES)
827
- setBoolean(target, 'tracing', DD_TRACING_ENABLED)
828
- setString(target, 'version', DD_VERSION || tags.version)
829
- setBoolean(target, 'inferredProxyServicesEnabled', DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED)
830
- setBoolean(target, 'trace.aws.addSpanPointers', DD_TRACE_AWS_ADD_SPAN_POINTERS)
831
- setString(target, 'trace.dynamoDb.tablePrimaryKeys', DD_TRACE_DYNAMODB_TABLE_PRIMARY_KEYS)
832
- setArray(target, 'graphqlErrorExtensions', DD_TRACE_GRAPHQL_ERROR_EXTENSIONS)
833
- setBoolean(target, 'trace.nativeSpanEvents', DD_TRACE_NATIVE_SPAN_EVENTS)
834
- target['vertexai.spanPromptCompletionSampleRate'] = maybeFloat(DD_VERTEXAI_SPAN_PROMPT_COMPLETION_SAMPLE_RATE)
835
- target['vertexai.spanCharLimit'] = maybeInt(DD_VERTEXAI_SPAN_CHAR_LIMIT)
836
- }
837
494
 
838
- #applyOptions (options) {
839
- const opts = this.#options
840
- const tags = {}
841
-
842
- options = this.#optionsArg = { ingestion: {}, ...options, ...opts }
843
-
844
- tagger.add(tags, options.tags)
845
-
846
- setBoolean(opts, 'apmTracingEnabled', options.apmTracingEnabled ??
847
- (options.experimental?.appsec?.standalone && !options.experimental.appsec.standalone.enabled)
848
- )
849
- setBoolean(opts, 'appsec.apiSecurity.enabled', options.appsec?.apiSecurity?.enabled)
850
- setBoolean(opts, 'appsec.apiSecurity.endpointCollectionEnabled',
851
- options.appsec?.apiSecurity?.endpointCollectionEnabled)
852
- opts['appsec.apiSecurity.endpointCollectionMessageLimit'] =
853
- maybeInt(options.appsec?.apiSecurity?.endpointCollectionMessageLimit)
854
- opts['appsec.blockedTemplateGraphql'] = maybeFile(options.appsec?.blockedTemplateGraphql)
855
- opts['appsec.blockedTemplateHtml'] = maybeFile(options.appsec?.blockedTemplateHtml)
856
- this.#optsUnprocessed['appsec.blockedTemplateHtml'] = options.appsec?.blockedTemplateHtml
857
- opts['appsec.blockedTemplateJson'] = maybeFile(options.appsec?.blockedTemplateJson)
858
- this.#optsUnprocessed['appsec.blockedTemplateJson'] = options.appsec?.blockedTemplateJson
859
- setBoolean(opts, 'appsec.enabled', options.appsec?.enabled)
860
- setString(opts, 'appsec.eventTracking.mode', options.appsec?.eventTracking?.mode)
861
- setBoolean(
862
- opts,
863
- 'appsec.extendedHeadersCollection.enabled',
864
- options.appsec?.extendedHeadersCollection?.enabled
865
- )
866
- setBoolean(
867
- opts,
868
- 'appsec.extendedHeadersCollection.redaction',
869
- options.appsec?.extendedHeadersCollection?.redaction
870
- )
871
- opts['appsec.extendedHeadersCollection.maxHeaders'] = options.appsec?.extendedHeadersCollection?.maxHeaders
872
- setString(opts, 'appsec.obfuscatorKeyRegex', options.appsec?.obfuscatorKeyRegex)
873
- setString(opts, 'appsec.obfuscatorValueRegex', options.appsec?.obfuscatorValueRegex)
874
- setBoolean(opts, 'appsec.rasp.enabled', options.appsec?.rasp?.enabled)
875
- setBoolean(opts, 'appsec.rasp.bodyCollection', options.appsec?.rasp?.bodyCollection)
876
- opts['appsec.rateLimit'] = maybeInt(options.appsec?.rateLimit)
877
- this.#optsUnprocessed['appsec.rateLimit'] = options.appsec?.rateLimit
878
- setString(opts, 'appsec.rules', options.appsec?.rules)
879
- setBoolean(opts, 'appsec.stackTrace.enabled', options.appsec?.stackTrace?.enabled)
880
- opts['appsec.stackTrace.maxDepth'] = maybeInt(options.appsec?.stackTrace?.maxDepth)
881
- this.#optsUnprocessed['appsec.stackTrace.maxDepth'] = options.appsec?.stackTrace?.maxDepth
882
- opts['appsec.stackTrace.maxStackTraces'] = maybeInt(options.appsec?.stackTrace?.maxStackTraces)
883
- this.#optsUnprocessed['appsec.stackTrace.maxStackTraces'] = options.appsec?.stackTrace?.maxStackTraces
884
- opts['appsec.wafTimeout'] = maybeInt(options.appsec?.wafTimeout)
885
- this.#optsUnprocessed['appsec.wafTimeout'] = options.appsec?.wafTimeout
886
- setBoolean(opts, 'clientIpEnabled', options.clientIpEnabled)
887
- setString(opts, 'clientIpHeader', options.clientIpHeader?.toLowerCase())
888
- if (options.cloudPayloadTagging?.request || options.cloudPayloadTagging?.response) {
889
- if (options.cloudPayloadTagging.request) {
890
- setBoolean(opts, 'cloudPayloadTagging.requestsEnabled', true)
891
- }
892
- if (options.cloudPayloadTagging.response) {
893
- setBoolean(opts, 'cloudPayloadTagging.responsesEnabled', true)
894
- }
895
- opts['cloudPayloadTagging.rules'] = appendRules(
896
- splitJSONPathRules(options.cloudPayloadTagging.request),
897
- splitJSONPathRules(options.cloudPayloadTagging.response)
898
- )
899
- }
900
- if (options.cloudPayloadTagging?.requestsEnabled !== undefined) {
901
- setBoolean(opts, 'cloudPayloadTagging.requestsEnabled', options.cloudPayloadTagging.requestsEnabled)
902
- }
903
- if (options.cloudPayloadTagging?.responsesEnabled !== undefined) {
904
- setBoolean(opts, 'cloudPayloadTagging.responsesEnabled', options.cloudPayloadTagging.responsesEnabled)
905
- }
906
- opts['cloudPayloadTagging.maxDepth'] = maybeInt(options.cloudPayloadTagging?.maxDepth)
907
- opts.baggageMaxBytes = options.baggageMaxBytes
908
- opts.baggageMaxItems = options.baggageMaxItems
909
- setArray(opts, 'baggageTagKeys', options.baggageTagKeys)
910
- setBoolean(opts, 'codeOriginForSpans.enabled', options.codeOriginForSpans?.enabled)
911
- setBoolean(
912
- opts,
913
- 'codeOriginForSpans.experimental.exit_spans.enabled',
914
- options.codeOriginForSpans?.experimental?.exit_spans?.enabled
915
- )
916
- setString(opts, 'dbmPropagationMode', options.dbmPropagationMode)
917
- setBoolean(opts, 'dbm.injectSqlBaseHash', options.dbm?.injectSqlBaseHash)
918
- if (options.dogstatsd) {
919
- setString(opts, 'dogstatsd.hostname', options.dogstatsd.hostname)
920
- setString(opts, 'dogstatsd.port', options.dogstatsd.port)
495
+ if (!this.#parsedDdTags) {
496
+ this.#parsedDdTags = rfdc(this.tags)
921
497
  }
922
- setBoolean(opts, 'dsmEnabled', options.dsmEnabled)
923
- opts['dynamicInstrumentation.captureTimeoutMs'] = maybeInt(options.dynamicInstrumentation?.captureTimeoutMs)
924
- this.#optsUnprocessed['dynamicInstrumentation.captureTimeoutMs'] = options.dynamicInstrumentation?.captureTimeoutMs
925
- setBoolean(opts, 'dynamicInstrumentation.enabled', options.dynamicInstrumentation?.enabled)
926
- setString(opts, 'dynamicInstrumentation.probeFile', options.dynamicInstrumentation?.probeFile)
927
- setArray(
928
- opts,
929
- 'dynamicInstrumentation.redactedIdentifiers',
930
- options.dynamicInstrumentation?.redactedIdentifiers
931
- )
932
- setArray(
933
- opts,
934
- 'dynamicInstrumentation.redactionExcludedIdentifiers',
935
- options.dynamicInstrumentation?.redactionExcludedIdentifiers
936
- )
937
- opts['dynamicInstrumentation.uploadIntervalSeconds'] =
938
- maybeFloat(options.dynamicInstrumentation?.uploadIntervalSeconds)
939
- this.#optsUnprocessed['dynamicInstrumentation.uploadIntervalSeconds'] =
940
- options.dynamicInstrumentation?.uploadIntervalSeconds
941
- setString(opts, 'env', options.env || tags.env)
942
- setBoolean(opts, 'experimental.aiguard.enabled', options.experimental?.aiguard?.enabled)
943
- setString(opts, 'experimental.aiguard.endpoint', options.experimental?.aiguard?.endpoint)
944
- opts['experimental.aiguard.maxMessagesLength'] = maybeInt(options.experimental?.aiguard?.maxMessagesLength)
945
- this.#optsUnprocessed['experimental.aiguard.maxMessagesLength'] = options.experimental?.aiguard?.maxMessagesLength
946
- opts['experimental.aiguard.maxContentSize'] = maybeInt(options.experimental?.aiguard?.maxContentSize)
947
- this.#optsUnprocessed['experimental.aiguard.maxContentSize'] = options.experimental?.aiguard?.maxContentSize
948
- opts['experimental.aiguard.timeout'] = maybeInt(options.experimental?.aiguard?.timeout)
949
- this.#optsUnprocessed['experimental.aiguard.timeout'] = options.experimental?.aiguard?.timeout
950
- setBoolean(opts, 'experimental.enableGetRumData', options.experimental?.enableGetRumData)
951
- setString(opts, 'experimental.exporter', options.experimental?.exporter)
952
- setBoolean(opts, 'experimental.flaggingProvider.enabled', options.experimental?.flaggingProvider?.enabled)
953
- opts['experimental.flaggingProvider.initializationTimeoutMs'] = maybeInt(
954
- options.experimental?.flaggingProvider?.initializationTimeoutMs
955
- )
956
- this.#optsUnprocessed['experimental.flaggingProvider.initializationTimeoutMs'] =
957
- options.experimental?.flaggingProvider?.initializationTimeoutMs
958
- opts.flushInterval = maybeInt(options.flushInterval)
959
- this.#optsUnprocessed.flushInterval = options.flushInterval
960
- opts.flushMinSpans = maybeInt(options.flushMinSpans)
961
- this.#optsUnprocessed.flushMinSpans = options.flushMinSpans
962
- setArray(opts, 'headerTags', options.headerTags)
963
- setString(opts, 'hostname', options.hostname)
964
- opts['iast.dbRowsToTaint'] = maybeInt(options.iast?.dbRowsToTaint)
965
- setBoolean(opts, 'iast.deduplicationEnabled', options.iast && options.iast.deduplicationEnabled)
966
- setBoolean(opts, 'iast.enabled',
967
- options.iast && (options.iast === true || options.iast.enabled === true))
968
- opts['iast.maxConcurrentRequests'] = maybeInt(options.iast?.maxConcurrentRequests)
969
- this.#optsUnprocessed['iast.maxConcurrentRequests'] = options.iast?.maxConcurrentRequests
970
- opts['iast.maxContextOperations'] = maybeInt(options.iast?.maxContextOperations)
971
- this.#optsUnprocessed['iast.maxContextOperations'] = options.iast?.maxContextOperations
972
- setBoolean(opts, 'iast.redactionEnabled', options.iast?.redactionEnabled)
973
- setString(opts, 'iast.redactionNamePattern', options.iast?.redactionNamePattern)
974
- setString(opts, 'iast.redactionValuePattern', options.iast?.redactionValuePattern)
975
- const iastRequestSampling = maybeInt(options.iast?.requestSampling)
976
- if (iastRequestSampling !== undefined && iastRequestSampling > -1 && iastRequestSampling < 101) {
977
- opts['iast.requestSampling'] = iastRequestSampling
978
- this.#optsUnprocessed['iast.requestSampling'] = options.iast?.requestSampling
979
- }
980
- if (DD_MAJOR < 6) {
981
- opts['iast.securityControlsConfiguration'] = options.iast?.securityControlsConfiguration
498
+
499
+ if (!this.env && this.tags.env !== undefined) {
500
+ setAndTrack(this, 'env', this.tags.env)
982
501
  }
983
- setBoolean(opts, 'iast.stackTrace.enabled', options.iast?.stackTrace?.enabled)
984
- setString(opts, 'iast.telemetryVerbosity', options.iast && options.iast.telemetryVerbosity)
985
- setBoolean(opts, 'isCiVisibility', options.isCiVisibility)
986
- setBoolean(opts, 'legacyBaggageEnabled', options.legacyBaggageEnabled)
987
- setBoolean(opts, 'llmobs.agentlessEnabled', options.llmobs?.agentlessEnabled)
988
- setString(opts, 'llmobs.mlApp', options.llmobs?.mlApp)
989
- setBoolean(opts, 'logInjection', options.logInjection)
990
- opts.lookup = options.lookup
991
- setBoolean(opts, 'middlewareTracingEnabled', options.middlewareTracingEnabled)
992
- setBoolean(opts, 'openAiLogsEnabled', options.openAiLogsEnabled)
993
- opts.peerServiceMapping = options.peerServiceMapping
994
- setBoolean(opts, 'plugins', options.plugins)
995
- setString(opts, 'port', options.port)
996
- const strProfiling = String(options.profiling)
997
- if (['true', 'false', 'auto'].includes(strProfiling)) {
998
- setString(opts, 'profiling.enabled', strProfiling)
502
+
503
+ if (!this.version) {
504
+ setAndTrack(this, 'version', this.tags.version || pkg.version)
505
+ this.tags.version ??= pkg.version
999
506
  }
1000
- setString(opts, 'protocolVersion', options.protocolVersion)
1001
- if (options.remoteConfig) {
1002
- opts['remoteConfig.pollInterval'] = maybeFloat(options.remoteConfig.pollInterval)
1003
- this.#optsUnprocessed['remoteConfig.pollInterval'] = options.remoteConfig.pollInterval
507
+
508
+ let isServiceNameInferred = false
509
+ if (!trackedConfigOrigins.has('service')) {
510
+ if (this.tags.service) {
511
+ setAndTrack(this, 'service', this.tags.service)
512
+ } else {
513
+ const NX_TASK_TARGET_PROJECT = getEnvironmentVariable('NX_TASK_TARGET_PROJECT')
514
+ if (NX_TASK_TARGET_PROJECT) {
515
+ if (this.DD_ENABLE_NX_SERVICE_NAME) {
516
+ setAndTrack(this, 'service', NX_TASK_TARGET_PROJECT)
517
+ isServiceNameInferred = true
518
+ } else if (DD_MAJOR < 6) {
519
+ log.warn(
520
+ // eslint-disable-next-line eslint-rules/eslint-log-printf-style
521
+ 'NX_TASK_TARGET_PROJECT is set but no service name was configured. In v6, NX_TASK_TARGET_PROJECT will ' +
522
+ 'be used as the default service name. Set DD_ENABLE_NX_SERVICE_NAME=true to opt-in to this behavior ' +
523
+ 'now, or set a service name explicitly.'
524
+ )
525
+ }
526
+ }
527
+ }
528
+
529
+ if (!this.service) {
530
+ const serverlessName = IS_SERVERLESS
531
+ ? (
532
+ getEnvironmentVariable('AWS_LAMBDA_FUNCTION_NAME') ||
533
+ getEnvironmentVariable('FUNCTION_NAME') || // Google Cloud Function Name set by deprecated runtimes
534
+ getEnvironmentVariable('K_SERVICE') || // Google Cloud Function Name set by newer runtimes
535
+ getEnvironmentVariable('WEBSITE_SITE_NAME') // set by Azure Functions
536
+ )
537
+ : undefined
538
+
539
+ setAndTrack(this, 'service', serverlessName || pkg.name || 'node')
540
+ this.tags.service ??= /** @type {string} */ (this.service)
541
+ isServiceNameInferred = true
542
+ }
1004
543
  }
1005
- setBoolean(opts, 'reportHostname', options.reportHostname)
1006
- setBoolean(opts, 'runtimeMetrics.enabled', options.runtimeMetrics?.enabled)
1007
- setBoolean(opts, 'runtimeMetrics.eventLoop', options.runtimeMetrics?.eventLoop)
1008
- setBoolean(opts, 'runtimeMetrics.gc', options.runtimeMetrics?.gc)
1009
- setBoolean(opts, 'runtimeMetricsRuntimeId', options.runtimeMetricsRuntimeId)
1010
- setArray(opts, 'sampler.spanSamplingRules', reformatSpanSamplingRules(options.spanSamplingRules))
1011
- setUnit(opts, 'sampleRate', options.sampleRate ?? options.ingestion.sampleRate)
1012
- opts['sampler.rateLimit'] = maybeInt(options.rateLimit ?? options.ingestion.rateLimit)
1013
- setSamplingRule(opts, 'sampler.rules', options.samplingRules)
1014
- const optService = options.service || tags.service
1015
- setString(opts, 'service', optService)
1016
- if (optService) {
1017
- setBoolean(opts, 'isServiceNameInferred', false)
544
+ setAndTrack(this, 'isServiceNameInferred', isServiceNameInferred)
545
+
546
+ // Add missing tags, in case they are defined otherwise.
547
+ if (this.service) {
548
+ this.tags.service = this.service
1018
549
  }
1019
- opts.serviceMapping = options.serviceMapping
1020
- setString(opts, 'site', options.site)
1021
- if (options.spanAttributeSchema) {
1022
- setString(opts, 'spanAttributeSchema', validateNamingVersion(options.spanAttributeSchema))
1023
- this.#optsUnprocessed.spanAttributeSchema = options.spanAttributeSchema
550
+ if (this.env) {
551
+ this.tags.env = this.env
1024
552
  }
1025
- setBoolean(opts, 'spanRemoveIntegrationFromService', options.spanRemoveIntegrationFromService)
1026
- setBoolean(opts, 'startupLogs', options.startupLogs)
1027
- setTags(opts, 'tags', tags)
1028
- setBoolean(opts, 'traceId128BitGenerationEnabled', options.traceId128BitGenerationEnabled)
1029
- setBoolean(opts, 'traceId128BitLoggingEnabled', options.traceId128BitLoggingEnabled)
1030
- setBoolean(opts, 'traceWebsocketMessagesEnabled', options.traceWebsocketMessagesEnabled)
1031
- setBoolean(opts, 'traceWebsocketMessagesInheritSampling', options.traceWebsocketMessagesInheritSampling)
1032
- setBoolean(opts, 'traceWebsocketMessagesSeparateTraces', options.traceWebsocketMessagesSeparateTraces)
1033
- setString(opts, 'version', options.version || tags.version)
1034
- setBoolean(opts, 'inferredProxyServicesEnabled', options.inferredProxyServicesEnabled)
1035
- setBoolean(opts, 'graphqlErrorExtensions', options.graphqlErrorExtensions)
1036
- setBoolean(opts, 'trace.nativeSpanEvents', options.trace?.nativeSpanEvents)
1037
- if (options.tracePropagationStyle) {
1038
- setArray(opts, 'tracePropagationStyle.inject',
1039
- normalizePropagationStyle(options.tracePropagationStyle.inject ?? options.tracePropagationStyle))
1040
- setArray(opts, 'tracePropagationStyle.extract',
1041
- normalizePropagationStyle(options.tracePropagationStyle.extract ?? options.tracePropagationStyle))
553
+ if (this.version) {
554
+ this.tags.version = this.version
1042
555
  }
556
+ this.tags['runtime-id'] = RUNTIME_ID
1043
557
 
1044
- // For LLMObs, we want the environment variable to take precedence over the options.
1045
- // This is reliant on environment config being set before options.
1046
- // This is to make sure the origins of each value are tracked appropriately for telemetry.
1047
- // We'll only set `llmobs.enabled` on the opts when it's not set on the environment, and options.llmobs is provided.
1048
- if (this.#env['llmobs.enabled'] == null && options.llmobs) {
1049
- setBoolean(opts, 'llmobs.enabled', true)
558
+ if (IS_SERVERLESS) {
559
+ setAndTrack(this, 'telemetry.enabled', false)
560
+ setAndTrack(this, 'crashtracking.enabled', false)
561
+ setAndTrack(this, 'remoteConfig.enabled', false)
1050
562
  }
1051
- }
1052
-
1053
- #isCiVisibility () {
1054
- return this.#optionsArg.isCiVisibility ?? this.#defaults.isCiVisibility
1055
- }
1056
-
1057
- #getHostname () {
1058
- const DD_CIVISIBILITY_AGENTLESS_URL = getEnv('DD_CIVISIBILITY_AGENTLESS_URL')
1059
- const url = DD_CIVISIBILITY_AGENTLESS_URL
1060
- ? new URL(DD_CIVISIBILITY_AGENTLESS_URL)
1061
- : getAgentUrl(this.#getTraceAgentUrl(), this.#optionsArg)
1062
- const DD_AGENT_HOST = this.#optionsArg.hostname ??
1063
- getEnv('DD_AGENT_HOST') ??
1064
- defaults.hostname
1065
- return DD_AGENT_HOST || url?.hostname
1066
- }
1067
-
1068
- #getSpanComputePeerService () {
1069
- const DD_TRACE_SPAN_ATTRIBUTE_SCHEMA = validateNamingVersion(
1070
- this.#optionsArg.spanAttributeSchema ??
1071
- getEnv('DD_TRACE_SPAN_ATTRIBUTE_SCHEMA')
1072
- )
1073
-
1074
- const peerServiceSet = (
1075
- this.#optionsArg.hasOwnProperty('spanComputePeerService') ||
1076
- getEnv('DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED') !== undefined
1077
- )
1078
- const peerServiceValue = this.#optionsArg.spanComputePeerService ??
1079
- getEnv('DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED')
1080
-
1081
- const spanComputePeerService = (
1082
- DD_TRACE_SPAN_ATTRIBUTE_SCHEMA === 'v0'
1083
- // In v0, peer service is computed only if it is explicitly set to true
1084
- ? peerServiceSet && isTrue(peerServiceValue)
1085
- // In >v0, peer service is false only if it is explicitly set to false
1086
- : (peerServiceSet ? !isFalse(peerServiceValue) : true)
1087
- )
1088
-
1089
- return spanComputePeerService
1090
- }
1091
563
 
1092
- #isTraceStatsComputationEnabled () {
1093
- const apmTracingEnabled = this.#options.apmTracingEnabled !== false &&
1094
- this.#env.apmTracingEnabled !== false
1095
-
1096
- return apmTracingEnabled && (
1097
- this.#optionsArg.stats ??
1098
- getEnv('DD_TRACE_STATS_COMPUTATION_ENABLED') ??
1099
- (getIsGCPFunction() || getIsAzureFunction())
1100
- )
1101
- }
1102
-
1103
- #getTraceAgentUrl () {
1104
- return this.#optionsArg.url ??
1105
- getEnv('DD_TRACE_AGENT_URL') ??
1106
- null
1107
- }
1108
-
1109
- // handles values calculated from a mixture of options and env vars
1110
- #applyCalculated () {
1111
- const calc = this.#calculated
1112
-
1113
- const DD_CIVISIBILITY_AGENTLESS_URL = getEnv('DD_CIVISIBILITY_AGENTLESS_URL')
1114
-
1115
- calc.url = DD_CIVISIBILITY_AGENTLESS_URL
1116
- ? new URL(DD_CIVISIBILITY_AGENTLESS_URL)
1117
- : getAgentUrl(this.#getTraceAgentUrl(), this.#optionsArg)
564
+ // TODO: Should this unconditionally be disabled?
565
+ if (getEnvironmentVariable('JEST_WORKER_ID') && !trackedConfigOrigins.has('telemetry.enabled')) {
566
+ setAndTrack(this, 'telemetry.enabled', false)
567
+ }
1118
568
 
1119
569
  // Experimental agentless APM span intake
1120
570
  // When enabled, sends spans directly to Datadog intake without an agent
1121
- const agentlessEnabled = isTrue(getEnv('_DD_APM_TRACING_AGENTLESS_ENABLED'))
571
+ // TODO: Replace this with a proper configuration
572
+ const agentlessEnabled = isTrue(getEnvironmentVariable('_DD_APM_TRACING_AGENTLESS_ENABLED'))
1122
573
  if (agentlessEnabled) {
1123
- setString(calc, 'experimental.exporter', 'agentless')
1124
- // Disable rate limiting - server-side sampling will be used
1125
- calc['sampler.rateLimit'] = -1
574
+ setAndTrack(this, 'experimental.exporter', 'agentless')
1126
575
  // Disable client-side stats computation
1127
- setBoolean(calc, 'stats.enabled', false)
576
+ setAndTrack(this, 'stats.enabled', false)
1128
577
  // Enable hostname reporting
1129
- setBoolean(calc, 'reportHostname', true)
578
+ setAndTrack(this, 'reportHostname', true)
579
+ // Disable rate limiting - server-side sampling will be used
580
+ setAndTrack(this, 'sampler.rateLimit', -1)
1130
581
  // Clear sampling rules - server-side sampling handles this
1131
- calc['sampler.rules'] = []
582
+ setAndTrack(this, 'sampler.rules', [])
1132
583
  // Agentless intake only accepts 64-bit trace IDs; disable 128-bit generation
1133
- setBoolean(calc, 'traceId128BitGenerationEnabled', false)
1134
- }
1135
-
1136
- if (this.#isCiVisibility()) {
1137
- setBoolean(calc, 'isEarlyFlakeDetectionEnabled',
1138
- getEnv('DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED') ?? true)
1139
- setBoolean(calc, 'isFlakyTestRetriesEnabled', getEnv('DD_CIVISIBILITY_FLAKY_RETRY_ENABLED') ?? true)
1140
- calc.flakyTestRetriesCount = maybeInt(getEnv('DD_CIVISIBILITY_FLAKY_RETRY_COUNT')) ?? 5
1141
- setBoolean(calc, 'isIntelligentTestRunnerEnabled', isTrue(isCiVisibilityItrEnabled()))
1142
- setBoolean(calc, 'isManualApiEnabled', !isFalse(getEnv('DD_CIVISIBILITY_MANUAL_API_ENABLED')))
1143
- setString(calc, 'ciVisibilityTestSessionName', getEnv('DD_TEST_SESSION_NAME'))
1144
- setBoolean(calc, 'ciVisAgentlessLogSubmissionEnabled',
1145
- isTrue(getEnv('DD_AGENTLESS_LOG_SUBMISSION_ENABLED')))
1146
- setBoolean(calc, 'isTestDynamicInstrumentationEnabled',
1147
- !isFalse(getEnv('DD_TEST_FAILED_TEST_REPLAY_ENABLED')))
1148
- setBoolean(calc, 'isServiceUserProvided', !!this.#env.service)
1149
- setBoolean(calc, 'isTestManagementEnabled', !isFalse(getEnv('DD_TEST_MANAGEMENT_ENABLED')))
1150
- calc.testManagementAttemptToFixRetries = maybeInt(getEnv('DD_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES')) ?? 20
1151
- setBoolean(calc, 'isImpactedTestsEnabled',
1152
- !isFalse(getEnv('DD_CIVISIBILITY_IMPACTED_TESTS_DETECTION_ENABLED')))
1153
- }
1154
-
1155
- // Disable log injection when OTEL logs are enabled
1156
- // OTEL logs and DD log injection are mutually exclusive
1157
- if (this.#env.otelLogsEnabled) {
1158
- setBoolean(calc, 'logInjection', false)
584
+ if (!trackedConfigOrigins.has('traceId128BitGenerationEnabled')) {
585
+ setAndTrack(this, 'traceId128BitGenerationEnabled', false)
586
+ }
1159
587
  }
1160
588
 
1161
- calc['dogstatsd.hostname'] = this.#getHostname()
1162
-
1163
- // Compute OTLP logs and metrics URLs to send payloads to the active Datadog Agent
1164
- const agentHostname = this.#getHostname()
1165
- calc.otelLogsUrl = `http://${agentHostname}:${DEFAULT_OTLP_PORT}`
1166
- calc.otelMetricsUrl = `http://${agentHostname}:${DEFAULT_OTLP_PORT}/v1/metrics`
1167
- calc.otelUrl = `http://${agentHostname}:${DEFAULT_OTLP_PORT}`
1168
- calc['telemetry.heartbeatInterval'] = maybeInt(Math.floor(this.#defaults['telemetry.heartbeatInterval'] * 1000))
1169
-
1170
- setBoolean(calc, 'isGitUploadEnabled',
1171
- calc.isIntelligentTestRunnerEnabled && !isFalse(getEnv('DD_CIVISIBILITY_GIT_UPLOAD_ENABLED')))
1172
-
1173
- // Enable resourceRenamingEnabled when appsec is enabled and only
1174
- // if DD_TRACE_RESOURCE_RENAMING_ENABLED is not explicitly set
1175
- if (this.#env.resourceRenamingEnabled === undefined) {
1176
- const appsecEnabled = this.#options['appsec.enabled'] ?? this.#env['appsec.enabled']
1177
- if (appsecEnabled) {
1178
- setBoolean(calc, 'resourceRenamingEnabled', true)
589
+ // Apply all fallbacks to the calculated config.
590
+ for (const [configName, alias] of fallbackConfigurations) {
591
+ if (!trackedConfigOrigins.has(configName) && trackedConfigOrigins.has(alias)) {
592
+ setAndTrack(this, configName, this[alias])
1179
593
  }
1180
594
  }
1181
595
 
1182
- setBoolean(calc, 'spanComputePeerService', this.#getSpanComputePeerService())
1183
- setBoolean(calc, 'stats.enabled', this.#isTraceStatsComputationEnabled())
1184
- const defaultPropagationStyle = getDefaultPropagationStyle(this.#optionsArg)
1185
- if (defaultPropagationStyle.length > 2) {
1186
- // b3 was added, so update defaults to include it
1187
- // This will only be used if no other source (options, env, stable config) set the value
1188
- calc['tracePropagationStyle.inject'] = defaultPropagationStyle
1189
- calc['tracePropagationStyle.extract'] = defaultPropagationStyle
596
+ const DEFAULT_OTLP_PORT = '4318'
597
+ if (!this.otelLogsUrl) {
598
+ setAndTrack(this, 'otelLogsUrl', `http://${agentHostname}:${DEFAULT_OTLP_PORT}`)
1190
599
  }
1191
- }
1192
-
1193
- /**
1194
- * Applies remote configuration options from APM_TRACING configs.
1195
- *
1196
- * @param {import('./remote_config').RemoteConfigOptions} options - Configurations received via Remote Config
1197
- */
1198
- #applyRemoteConfig (options) {
1199
- const opts = this.#remote
1200
-
1201
- setBoolean(opts, 'dynamicInstrumentation.enabled', options.dynamic_instrumentation_enabled)
1202
- setBoolean(opts, 'codeOriginForSpans.enabled', options.code_origin_enabled)
1203
- setUnit(opts, 'sampleRate', options.tracing_sampling_rate)
1204
- setBoolean(opts, 'logInjection', options.log_injection_enabled)
1205
- setBoolean(opts, 'tracing', options.tracing_enabled)
1206
- this.#remoteUnprocessed['sampler.rules'] = options.tracing_sampling_rules
1207
- setSamplingRule(opts, 'sampler.rules', reformatTagsFromRC(options.tracing_sampling_rules))
1208
-
1209
- opts.headerTags = options.tracing_header_tags?.map(tag => {
1210
- return tag.tag_name ? `${tag.header}:${tag.tag_name}` : tag.header
1211
- })
1212
-
1213
- const tags = {}
1214
- tagger.add(tags, options.tracing_tags)
1215
- if (Object.keys(tags).length) {
1216
- tags['runtime-id'] = RUNTIME_ID
600
+ if (!this.otelMetricsUrl) {
601
+ setAndTrack(this, 'otelMetricsUrl', `http://${agentHostname}:${DEFAULT_OTLP_PORT}/v1/metrics`)
1217
602
  }
1218
- setTags(opts, 'tags', tags)
1219
- }
1220
603
 
1221
- #setAndTrackChange ({ name, value, origin, unprocessedValue, changes }) {
1222
- set(this, name, value)
1223
-
1224
- if (!changeTracker[name]) {
1225
- changeTracker[name] = {}
604
+ if (process.platform === 'win32') {
605
+ // OOM monitoring does not work properly on Windows, so it will be disabled.
606
+ deactivateIfEnabledAndWarnOnWindows(this, 'DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED')
607
+ // Profiler sampling contexts are not available on Windows, so features
608
+ // depending on those (code hotspots and endpoint collection) need to be disabled on Windows.
609
+ deactivateIfEnabledAndWarnOnWindows(this, 'DD_PROFILING_CODEHOTSPOTS_ENABLED')
610
+ deactivateIfEnabledAndWarnOnWindows(this, 'DD_PROFILING_ENDPOINT_COLLECTION_ENABLED')
611
+ deactivateIfEnabledAndWarnOnWindows(this, 'DD_PROFILING_CPU_ENABLED')
612
+ deactivateIfEnabledAndWarnOnWindows(this, 'DD_PROFILING_TIMELINE_ENABLED')
613
+ deactivateIfEnabledAndWarnOnWindows(this, 'DD_PROFILING_ASYNC_CONTEXT_FRAME_ENABLED')
1226
614
  }
1227
615
 
1228
- const originExists = origin in changeTracker[name]
1229
- const oldValue = changeTracker[name][origin]
616
+ // Single tags update is tracked as a calculated value.
617
+ setAndTrack(this, 'tags', this.tags)
1230
618
 
1231
- if (!originExists || oldValue !== value) {
1232
- changeTracker[name][origin] = value
1233
- changes.push({
1234
- name,
1235
- value: unprocessedValue || value,
1236
- origin,
1237
- })
1238
- }
1239
- }
1240
-
1241
- // TODO: Report origin changes and errors to telemetry.
1242
- // TODO: Deeply merge configurations.
1243
- // TODO: Move change tracking to telemetry.
1244
- // for telemetry reporting, `name`s in `containers` need to be keys from:
1245
- // https://github.com/DataDog/dd-go/blob/prod/trace/apps/tracer-telemetry-intake/telemetry-payload/static/config_norm_rules.json
1246
- #merge () {
1247
- const changes = []
1248
- const sources = this.#getSourcesInOrder()
1249
-
1250
- for (const name of Object.keys(this.#defaults)) {
1251
- // Use reverse order for merge (lowest priority first)
1252
- for (let i = sources.length - 1; i >= 0; i--) {
1253
- const { container, origin, unprocessed } = sources[i]
1254
- const value = container[name]
1255
- if (value != null || container === this.#defaults) {
1256
- this.#setAndTrackChange({
1257
- name,
1258
- value,
1259
- origin,
1260
- unprocessedValue: unprocessed?.[name],
1261
- changes,
1262
- })
1263
- }
1264
- }
1265
- }
1266
- this.sampler.sampleRate = this.sampleRate
1267
- updateConfig(changes, this)
619
+ telemetry.updateConfig([...configWithOrigin.values()], this)
1268
620
  }
1269
621
 
622
+ // TODO: Move outside of config. This is unrelated to the config system.
1270
623
  #loadGitMetadata () {
1271
- // try to read Git metadata from the environment variables
1272
- this.repositoryUrl = removeUserSensitiveInfo(
1273
- getEnv('DD_GIT_REPOSITORY_URL') ?? this.tags[GIT_REPOSITORY_URL]
1274
- )
1275
- this.commitSHA = getEnv('DD_GIT_COMMIT_SHA') ?? this.tags[GIT_COMMIT_SHA]
624
+ // Try to read Git metadata from the environment variables
625
+ this.repositoryUrl = removeUserSensitiveInfo(this.DD_GIT_REPOSITORY_URL ?? this.tags[GIT_REPOSITORY_URL])
626
+ this.commitSHA = this.DD_GIT_COMMIT_SHA ?? this.tags[GIT_COMMIT_SHA]
1276
627
 
1277
- // otherwise, try to read Git metadata from the git.properties file
628
+ // Otherwise, try to read Git metadata from the git.properties file
1278
629
  if (!this.repositoryUrl || !this.commitSHA) {
1279
- const DD_GIT_PROPERTIES_FILE = getEnv('DD_GIT_PROPERTIES_FILE')
630
+ const DD_GIT_PROPERTIES_FILE = this.DD_GIT_PROPERTIES_FILE
1280
631
  const gitPropertiesFile = DD_GIT_PROPERTIES_FILE ?? `${process.cwd()}/git.properties`
1281
- let gitPropertiesString
1282
632
  try {
1283
- gitPropertiesString = fs.readFileSync(gitPropertiesFile, 'utf8')
1284
- } catch (e) {
633
+ const gitPropertiesString = fs.readFileSync(gitPropertiesFile, 'utf8')
634
+ const { commitSHA, repositoryUrl } = getGitMetadataFromGitProperties(gitPropertiesString)
635
+ this.commitSHA ??= commitSHA
636
+ this.repositoryUrl ??= repositoryUrl
637
+ } catch (error) {
1285
638
  // Only log error if the user has set a git.properties path
1286
639
  if (DD_GIT_PROPERTIES_FILE) {
1287
- log.error('Error reading DD_GIT_PROPERTIES_FILE: %s', gitPropertiesFile, e)
640
+ log.error('Error reading DD_GIT_PROPERTIES_FILE: %s', gitPropertiesFile, error)
1288
641
  }
1289
642
  }
1290
- if (gitPropertiesString) {
1291
- const { commitSHA, repositoryUrl } = getGitMetadataFromGitProperties(gitPropertiesString)
1292
- this.commitSHA = this.commitSHA || commitSHA
1293
- this.repositoryUrl = this.repositoryUrl || repositoryUrl
1294
- }
1295
643
  }
1296
- // otherwise, try to read Git metadata from the .git/ folder
1297
- if (!this.repositoryUrl || !this.commitSHA) {
1298
- const DD_GIT_FOLDER_PATH = getEnv('DD_GIT_FOLDER_PATH')
1299
- const gitFolderPath = DD_GIT_FOLDER_PATH ?? path.join(process.cwd(), '.git')
1300
- if (!this.repositoryUrl) {
1301
- // try to read git config (repository URL)
1302
- const gitConfigPath = path.join(gitFolderPath, 'config')
1303
- try {
1304
- const gitConfigContent = fs.readFileSync(gitConfigPath, 'utf8')
1305
- if (gitConfigContent) {
1306
- this.repositoryUrl = getRemoteOriginURL(gitConfigContent)
1307
- }
1308
- } catch (e) {
1309
- // Only log error if the user has set a .git/ path
1310
- if (DD_GIT_FOLDER_PATH) {
1311
- log.error('Error reading git config: %s', gitConfigPath, e)
1312
- }
644
+
645
+ // Otherwise, try to read Git metadata from the .git/ folder
646
+ const DD_GIT_FOLDER_PATH = this.DD_GIT_FOLDER_PATH
647
+ const gitFolderPath = DD_GIT_FOLDER_PATH ?? path.join(process.cwd(), '.git')
648
+
649
+ if (!this.repositoryUrl) {
650
+ // Try to read git config (repository URL)
651
+ const gitConfigPath = path.join(gitFolderPath, 'config')
652
+ try {
653
+ const gitConfigContent = fs.readFileSync(gitConfigPath, 'utf8')
654
+ if (gitConfigContent) {
655
+ this.repositoryUrl = getRemoteOriginURL(gitConfigContent)
1313
656
  }
1314
- }
1315
- if (!this.commitSHA) {
1316
- // try to read git HEAD (commit SHA)
1317
- const gitHeadSha = resolveGitHeadSHA(gitFolderPath)
1318
- if (gitHeadSha) {
1319
- this.commitSHA = gitHeadSha
657
+ } catch (error) {
658
+ // Only log error if the user has set a .git/ path
659
+ if (DD_GIT_FOLDER_PATH) {
660
+ log.error('Error reading git config: %s', gitConfigPath, error)
1320
661
  }
1321
662
  }
1322
663
  }
664
+ // Try to read git HEAD (commit SHA)
665
+ this.commitSHA ??= resolveGitHeadSHA(gitFolderPath)
1323
666
  }
1324
667
  }
1325
668
 
1326
- function getCounter (event, ddVar, otelVar) {
1327
- const counters = TELEMETRY_COUNTERS.get(event)
1328
- const tags = []
1329
- const ddVarPrefix = 'config_datadog:'
1330
- const otelVarPrefix = 'config_opentelemetry:'
1331
- if (ddVar) {
1332
- ddVar = ddVarPrefix + ddVar.toLowerCase()
1333
- tags.push(ddVar)
1334
- }
1335
- if (otelVar) {
1336
- otelVar = otelVarPrefix + otelVar.toLowerCase()
1337
- tags.push(otelVar)
1338
- }
1339
-
1340
- if (!(otelVar in counters)) counters[otelVar] = {}
1341
-
1342
- const counter = tracerMetrics.count(event, tags)
1343
- counters[otelVar][ddVar] = counter
1344
- return counter
1345
- }
1346
-
1347
- function getFromOtelSamplerMap (otelTracesSampler, otelTracesSamplerArg) {
1348
- const OTEL_TRACES_SAMPLER_MAPPING = {
1349
- always_on: '1.0',
1350
- always_off: '0.0',
1351
- traceidratio: otelTracesSamplerArg,
1352
- parentbased_always_on: '1.0',
1353
- parentbased_always_off: '0.0',
1354
- parentbased_traceidratio: otelTracesSamplerArg,
1355
- }
1356
- return OTEL_TRACES_SAMPLER_MAPPING[otelTracesSampler]
1357
- }
1358
-
1359
669
  /**
1360
- * Validate the type of an environment variable
1361
- * @param {string} envVar - The name of the environment variable
1362
- * @param {string} [value] - The value of the environment variable
1363
- * @returns {boolean} - True if the value is valid, false otherwise
670
+ * @param {Config} config
671
+ * @param {ConfigKey} envVar
1364
672
  */
1365
- function isInvalidOtelEnvironmentVariable (envVar, value) {
1366
- // Skip validation if the value is undefined (it was not set as environment variable)
1367
- if (value === undefined) return false
1368
-
1369
- switch (envVar) {
1370
- case 'OTEL_LOG_LEVEL':
1371
- return !VALID_LOG_LEVELS.has(value)
1372
- case 'OTEL_PROPAGATORS':
1373
- case 'OTEL_RESOURCE_ATTRIBUTES':
1374
- case 'OTEL_SERVICE_NAME':
1375
- return typeof value !== 'string'
1376
- case 'OTEL_TRACES_SAMPLER':
1377
- return getFromOtelSamplerMap(value, getEnv('OTEL_TRACES_SAMPLER_ARG')) === undefined
1378
- case 'OTEL_TRACES_SAMPLER_ARG':
1379
- return Number.isNaN(Number.parseFloat(value))
1380
- case 'OTEL_SDK_DISABLED':
1381
- return value.toLowerCase() !== 'true' && value.toLowerCase() !== 'false'
1382
- case 'OTEL_TRACES_EXPORTER':
1383
- case 'OTEL_METRICS_EXPORTER':
1384
- case 'OTEL_LOGS_EXPORTER':
1385
- return value.toLowerCase() !== 'none'
1386
- default:
1387
- return true
1388
- }
1389
- }
1390
-
1391
- function checkIfBothOtelAndDdEnvVarSet () {
1392
- for (const [otelEnvVar, ddEnvVar] of OTEL_DD_ENV_MAPPING) {
1393
- const otelValue = getEnv(otelEnvVar)
1394
-
1395
- if (ddEnvVar && getEnv(ddEnvVar) && otelValue) {
1396
- log.warn('both %s and %s environment variables are set', ddEnvVar, otelEnvVar)
1397
- getCounter('otel.env.hiding', ddEnvVar, otelEnvVar).inc()
673
+ function deactivateIfEnabledAndWarnOnWindows (config, envVar) {
674
+ if (config[envVar]) {
675
+ const source = trackedConfigOrigins.get(envVar)
676
+ setAndTrack(config, envVar, false)
677
+ // TODO: Should we log even for default values?
678
+ if (source) {
679
+ log.warn('%s is not supported on Windows. Deactivating. (source: %s)', envVar, source)
1398
680
  }
1399
-
1400
- if (isInvalidOtelEnvironmentVariable(otelEnvVar, otelValue)) {
1401
- log.warn('unexpected value %s for %s environment variable', otelValue, otelEnvVar)
1402
- getCounter('otel.env.invalid', ddEnvVar, otelEnvVar).inc()
1403
- }
1404
- }
1405
- }
1406
-
1407
- function maybeFile (filepath) {
1408
- if (!filepath) return
1409
- try {
1410
- return fs.readFileSync(filepath, 'utf8')
1411
- } catch (e) {
1412
- log.error('Error reading file %s', filepath, e)
1413
- }
1414
- }
1415
-
1416
- function maybeJsonFile (filepath) {
1417
- const file = maybeFile(filepath)
1418
- if (!file) return
1419
- try {
1420
- return JSON.parse(file)
1421
- } catch (e) {
1422
- log.error('Error parsing JSON file %s', filepath, e)
1423
- }
1424
- }
1425
-
1426
- function safeJsonParse (input) {
1427
- try {
1428
- return JSON.parse(input)
1429
- } catch {}
1430
- }
1431
-
1432
- function validateNamingVersion (versionString) {
1433
- if (!versionString) {
1434
- return DEFAULT_NAMING_VERSION
1435
- }
1436
- if (!NAMING_VERSIONS.has(versionString)) {
1437
- log.warn('Unexpected input for config.spanAttributeSchema, picked default', DEFAULT_NAMING_VERSION)
1438
- return DEFAULT_NAMING_VERSION
1439
681
  }
1440
- return versionString
1441
682
  }
1442
683
 
1443
- /**
1444
- * Given a string of comma-separated paths, return the array of paths.
1445
- * If a blank path is provided a null is returned to signal that the feature is disabled.
1446
- * An empty array means the feature is enabled but that no rules need to be applied.
1447
- *
1448
- * @param {string | string[]} input
1449
- */
1450
- function splitJSONPathRules (input) {
1451
- if (!input || input === '$') return
1452
- if (Array.isArray(input)) return input
1453
- if (input === 'all') return []
1454
- return input.split(',')
1455
- }
1456
-
1457
- // Shallow clone with property name remapping
1458
- function remapify (input, mappings) {
1459
- if (!input) return
1460
- const output = {}
1461
- for (const [key, value] of Object.entries(input)) {
1462
- output[key in mappings ? mappings[key] : key] = value
1463
- }
1464
- return output
1465
- }
1466
-
1467
- /**
1468
- * Normalizes propagation style values to a lowercase array.
1469
- * Handles both string (comma-separated) and array inputs.
1470
- */
1471
- function normalizePropagationStyle (value) {
1472
- if (Array.isArray(value)) {
1473
- return value.map(v => v.toLowerCase())
1474
- }
1475
- if (typeof value === 'string') {
1476
- return value.split(',')
1477
- .filter(v => v !== '')
1478
- .map(v => v.trim().toLowerCase())
1479
- }
1480
- if (value !== undefined) {
1481
- log.warn('Unexpected input for config.tracePropagationStyle')
684
+ function increaseCounter (event, ddVar, otelVar) {
685
+ const tags = []
686
+ if (ddVar) {
687
+ tags.push(`config_datadog:${ddVar.toLowerCase()}`)
1482
688
  }
689
+ tags.push(`config_opentelemetry:${otelVar.toLowerCase()}`)
690
+ tracerMetrics.count(event, tags).inc()
1483
691
  }
1484
692
 
1485
- /**
1486
- * Warns if both DD_TRACE_PROPAGATION_STYLE and specific inject/extract vars are set.
1487
- */
1488
- function warnIfPropagationStyleConflict (general, inject, extract) {
1489
- if (general && (inject || extract)) {
1490
- log.warn(
1491
- // eslint-disable-next-line @stylistic/max-len
1492
- 'Use either the DD_TRACE_PROPAGATION_STYLE environment variable or separate DD_TRACE_PROPAGATION_STYLE_INJECT and DD_TRACE_PROPAGATION_STYLE_EXTRACT environment variables'
1493
- )
693
+ function getFromOtelSamplerMap (otelTracesSampler, otelTracesSamplerArg) {
694
+ const OTEL_TRACES_SAMPLER_MAPPING = {
695
+ always_on: 1,
696
+ always_off: 0,
697
+ parentbased_always_on: 1,
698
+ parentbased_always_off: 0,
1494
699
  }
1495
- }
1496
-
1497
- function reformatSpanSamplingRules (rules) {
1498
- if (!rules) return rules
1499
- return rules.map(rule => {
1500
- return remapify(rule, {
1501
- sample_rate: 'sampleRate',
1502
- max_per_second: 'maxPerSecond',
1503
- })
1504
- })
1505
- }
1506
700
 
1507
- function getDefaultPropagationStyle (options) {
1508
- // TODO: Remove the experimental env vars as a major?
1509
- const DD_TRACE_B3_ENABLED = options.experimental?.b3 ??
1510
- getEnv('DD_TRACE_EXPERIMENTAL_B3_ENABLED')
1511
- const defaultPropagationStyle = ['datadog', 'tracecontext']
1512
- if (isTrue(DD_TRACE_B3_ENABLED)) {
1513
- defaultPropagationStyle.push('b3', 'b3 single header')
701
+ const result = OTEL_TRACES_SAMPLER_MAPPING[otelTracesSampler] ?? otelTracesSamplerArg
702
+ if (result === undefined) {
703
+ increaseCounter('otel.env.invalid', 'DD_TRACE_SAMPLE_RATE', 'OTEL_TRACES_SAMPLER')
1514
704
  }
1515
- return defaultPropagationStyle
705
+ return result
1516
706
  }
1517
707
 
1518
- function isCiVisibilityItrEnabled () {
1519
- return getEnv('DD_CIVISIBILITY_ITR_ENABLED') ?? true
1520
- }
1521
-
1522
- function reformatTagsFromRC (samplingRules) {
1523
- for (const rule of (samplingRules || [])) {
1524
- if (rule.tags) {
1525
- const reformattedTags = {}
1526
- for (const tag of rule.tags) {
1527
- reformattedTags[tag.key] = tag.value_glob
708
+ function warnWrongOtelSettings () {
709
+ // This mostly works for non-aliased environment variables only.
710
+ // TODO: Adjust this to work across all sources.
711
+ for (const [otelEnvVar, ddEnvVar, key] of [
712
+ // eslint-disable-next-line eslint-rules/eslint-env-aliases
713
+ ['OTEL_LOG_LEVEL', 'DD_TRACE_LOG_LEVEL', 'logLevel'],
714
+ // eslint-disable-next-line eslint-rules/eslint-env-aliases
715
+ ['OTEL_PROPAGATORS', 'DD_TRACE_PROPAGATION_STYLE'],
716
+ // eslint-disable-next-line eslint-rules/eslint-env-aliases
717
+ ['OTEL_SERVICE_NAME', 'DD_SERVICE', 'service'],
718
+ ['OTEL_TRACES_SAMPLER', 'DD_TRACE_SAMPLE_RATE'],
719
+ ['OTEL_TRACES_SAMPLER_ARG', 'DD_TRACE_SAMPLE_RATE'],
720
+ ['OTEL_TRACES_EXPORTER', 'DD_TRACE_ENABLED'],
721
+ ['OTEL_METRICS_EXPORTER', 'DD_RUNTIME_METRICS_ENABLED'],
722
+ ['OTEL_RESOURCE_ATTRIBUTES', 'DD_TAGS'],
723
+ ['OTEL_SDK_DISABLED', 'DD_TRACE_OTEL_ENABLED'],
724
+ ['OTEL_LOGS_EXPORTER'],
725
+ ]) {
726
+ // eslint-disable-next-line eslint-rules/eslint-process-env
727
+ const envs = process.env
728
+ const otelSource = trackedConfigOrigins.get(/** @type {ConfigPath} */ (key ?? otelEnvVar))
729
+ const otelEnvValue = envs[otelEnvVar]
730
+ if (otelEnvValue) {
731
+ if (envs[ddEnvVar]) {
732
+ log.warn('Conflicting %s and %s environment variables are set for %s', ddEnvVar, otelEnvVar, otelSource)
733
+ increaseCounter('otel.env.hiding', ddEnvVar, otelEnvVar)
1528
734
  }
1529
- rule.tags = reformattedTags
1530
- }
1531
- }
1532
- return samplingRules
1533
- }
1534
-
1535
- function setBoolean (obj, name, value) {
1536
- if (value === undefined || value === null) {
1537
- obj[name] = value
1538
- } else if (isTrue(value)) {
1539
- obj[name] = true
1540
- } else if (isFalse(value)) {
1541
- obj[name] = false
1542
- }
1543
- }
1544
-
1545
- function setUnit (obj, name, value) {
1546
- if (value === null || value === undefined) {
1547
- obj[name] = value
1548
- return
1549
- }
1550
-
1551
- value = Number.parseFloat(value)
1552
-
1553
- if (!Number.isNaN(value)) {
1554
- // TODO: Ignore out of range values instead of normalizing them.
1555
- obj[name] = Math.min(Math.max(value, 0), 1)
1556
- }
1557
- }
1558
-
1559
- function setArray (obj, name, value) {
1560
- if (value == null) {
1561
- obj[name] = null
1562
- return
1563
- }
1564
-
1565
- if (typeof value === 'string') {
1566
- value = value.split(',').map(item => {
1567
- // Trim each item and remove whitespace around the colon
1568
- const [key, val] = item.split(':').map(part => part.trim())
1569
- return val === undefined ? key : `${key}:${val}`
1570
- })
1571
- }
1572
735
 
1573
- if (Array.isArray(value)) {
1574
- obj[name] = value
1575
- }
1576
- }
1577
-
1578
- function setIntegerRangeSet (obj, name, value) {
1579
- if (value == null) {
1580
- obj[name] = null
1581
- return
1582
- }
1583
- value = value.split(',')
1584
- const result = []
1585
-
1586
- for (const val of value) {
1587
- if (val.includes('-')) {
1588
- const [start, end] = val.split('-').map(Number)
1589
- for (let i = start; i <= end; i++) {
1590
- result.push(i)
736
+ // eslint-disable-next-line eslint-rules/eslint-env-aliases
737
+ const invalidOtelValue = otelEnvVar === 'OTEL_PROPAGATORS'
738
+ ? trackedConfigOrigins.get(/** @type {ConfigPath} */ ('tracePropagationStyle.inject')) !== otelSource &&
739
+ !envs[ddEnvVar]
740
+ : !otelSource
741
+ if (invalidOtelValue) {
742
+ increaseCounter('otel.env.invalid', ddEnvVar, otelEnvVar)
1591
743
  }
1592
- } else {
1593
- result.push(Number(val))
1594
744
  }
1595
745
  }
1596
- obj[name] = result
1597
- }
1598
-
1599
- function setSamplingRule (obj, name, value) {
1600
- if (value == null) {
1601
- obj[name] = null
1602
- return
1603
- }
1604
-
1605
- if (typeof value === 'string') {
1606
- value = value.split(',')
1607
- }
1608
-
1609
- if (Array.isArray(value)) {
1610
- value = value.map(rule => {
1611
- return remapify(rule, {
1612
- sample_rate: 'sampleRate',
1613
- })
1614
- })
1615
- obj[name] = value
1616
- }
1617
- }
1618
-
1619
- function setString (obj, name, value) {
1620
- obj[name] = value ? String(value) : undefined // unset for empty strings
1621
- }
1622
-
1623
- function setTags (obj, name, value) {
1624
- if (!value || Object.keys(value).length === 0) {
1625
- obj[name] = null
1626
- return
1627
- }
1628
-
1629
- obj[name] = value
1630
- }
1631
-
1632
- function handleOtel (tagString) {
1633
- return tagString
1634
- ?.replace(/(^|,)deployment\.environment=/, '$1env:')
1635
- .replace(/(^|,)service\.name=/, '$1service:')
1636
- .replace(/(^|,)service\.version=/, '$1version:')
1637
- .replaceAll('=', ':')
1638
- }
1639
-
1640
- function parseSpaceSeparatedTags (tagString) {
1641
- if (tagString && !tagString.includes(',')) {
1642
- tagString = tagString.replaceAll(/\s+/g, ',')
1643
- }
1644
- return tagString
1645
- }
1646
-
1647
- function maybeInt (number) {
1648
- const parsed = Number.parseInt(number)
1649
- return Number.isNaN(parsed) ? undefined : parsed
1650
- }
1651
-
1652
- function maybeFloat (number) {
1653
- const parsed = Number.parseFloat(number)
1654
- return Number.isNaN(parsed) ? undefined : parsed
1655
- }
1656
-
1657
- function nonNegInt (value, envVarName, allowZero = true) {
1658
- if (value === undefined) return
1659
- const parsed = Number.parseInt(value)
1660
- if (Number.isNaN(parsed) || parsed < 0 || (parsed === 0 && !allowZero)) {
1661
- log.warn('Invalid value %d for %s. Using default value.', parsed, envVarName)
1662
- return
1663
- }
1664
- return parsed
1665
- }
1666
-
1667
- function getAgentUrl (url, options) {
1668
- if (url) return new URL(url)
1669
-
1670
- if (os.type() === 'Windows_NT') return
1671
-
1672
- if (
1673
- !options.hostname &&
1674
- !options.port &&
1675
- !getEnv('DD_AGENT_HOST') &&
1676
- !getEnv('DD_TRACE_AGENT_PORT') &&
1677
- !isTrue(getEnv('DD_CIVISIBILITY_AGENTLESS_ENABLED')) &&
1678
- fs.existsSync('/var/run/datadog/apm.socket')
1679
- ) {
1680
- return new URL('unix:///var/run/datadog/apm.socket')
1681
- }
1682
746
  }
1683
747
 
748
+ /**
749
+ * @param {TracerOptions} [options]
750
+ */
1684
751
  function getConfig (options) {
1685
752
  if (!configInstance) {
1686
753
  configInstance = new Config(options)