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