dd-trace 5.99.0 → 5.99.1

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 (29) hide show
  1. package/package.json +21 -2
  2. package/packages/datadog-instrumentations/src/jest.js +5 -5
  3. package/packages/datadog-plugin-dd-trace-api/src/index.js +1 -1
  4. package/packages/datadog-plugin-graphql/src/utils.js +2 -2
  5. package/packages/datadog-plugin-memcached/src/index.js +1 -1
  6. package/packages/dd-trace/src/appsec/blocking.js +18 -6
  7. package/packages/dd-trace/src/appsec/graphql.js +1 -1
  8. package/packages/dd-trace/src/baggage.js +16 -13
  9. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -1
  10. package/packages/dd-trace/src/config/generated-config-types.d.ts +28 -28
  11. package/packages/dd-trace/src/config/index.js +7 -7
  12. package/packages/dd-trace/src/config/supported-configurations.json +16 -44
  13. package/packages/dd-trace/src/dogstatsd.js +5 -8
  14. package/packages/dd-trace/src/exporter.js +1 -1
  15. package/packages/dd-trace/src/git_metadata_tagger.js +1 -1
  16. package/packages/dd-trace/src/llmobs/constants/tags.js +3 -0
  17. package/packages/dd-trace/src/llmobs/sdk.js +21 -1
  18. package/packages/dd-trace/src/llmobs/span_processor.js +14 -1
  19. package/packages/dd-trace/src/llmobs/writers/base.js +7 -1
  20. package/packages/dd-trace/src/llmobs/writers/spans.js +1 -1
  21. package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -5
  22. package/packages/dd-trace/src/opentelemetry/metrics/index.js +6 -6
  23. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +1 -1
  24. package/packages/dd-trace/src/opentracing/propagation/text_map.js +13 -13
  25. package/packages/dd-trace/src/opentracing/span.js +1 -1
  26. package/packages/dd-trace/src/plugin_manager.js +6 -6
  27. package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
  28. package/packages/dd-trace/src/plugins/util/test.js +2 -2
  29. package/packages/dd-trace/src/proxy.js +6 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.99.0",
3
+ "version": "5.99.1",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -66,23 +66,39 @@
66
66
  "test:profiler": "node scripts/mocha-parallel-files.js --expose-gc --timeout 30000 -- \"packages/dd-trace/test/profiling/**/*.spec.js\"",
67
67
  "test:profiler:ci": "nyc --silent node init && nyc -- npm run test:profiler",
68
68
  "test:integration": "mocha --timeout 60000 \"integration-tests/*.spec.js\"",
69
+ "test:integration:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/*.spec.js\"",
69
70
  "test:integration:aiguard": "mocha --timeout 60000 \"integration-tests/aiguard/*.spec.js\"",
71
+ "test:integration:aiguard:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/aiguard/*.spec.js\"",
70
72
  "test:integration:appsec": "mocha --timeout 60000 \"integration-tests/appsec/*.spec.js\"",
73
+ "test:integration:appsec:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/appsec/*.spec.js\"",
71
74
  "test:integration:bun": "mocha --timeout 60000 \"integration-tests/bun/*.spec.js\"",
72
75
  "test:integration:cucumber": "mocha --timeout 60000 \"integration-tests/cucumber/*.spec.js\"",
76
+ "test:integration:cucumber:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/cucumber/*.spec.js\"",
73
77
  "test:integration:cypress": "mocha --timeout 60000 \"integration-tests/cypress/${SPEC:-cypress-*}.spec.js\"",
78
+ "test:integration:cypress:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/cypress/${SPEC:-cypress-*}.spec.js\"",
74
79
  "test:integration:debugger": "mocha --timeout 60000 \"integration-tests/debugger/*.spec.js\"",
80
+ "test:integration:debugger:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/debugger/*.spec.js\"",
75
81
  "test:integration:esbuild": "mocha --timeout 60000 \"integration-tests/esbuild/*.spec.js\"",
82
+ "test:integration:esbuild:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/esbuild/*.spec.js\"",
76
83
  "test:integration:webpack": "mocha --timeout 60000 \"integration-tests/webpack/*.spec.js\"",
77
84
  "test:integration:openfeature": "mocha --timeout 60000 \"integration-tests/openfeature/*.spec.js\"",
85
+ "test:integration:openfeature:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/openfeature/*.spec.js\"",
78
86
  "test:integration:jest": "mocha --timeout 60000 \"integration-tests/jest/*.spec.js\"",
87
+ "test:integration:jest:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/jest/*.spec.js\"",
79
88
  "test:integration:mocha": "mocha --timeout 60000 \"integration-tests/mocha/*.spec.js\"",
89
+ "test:integration:mocha:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/mocha/*.spec.js\"",
80
90
  "test:integration:playwright": "mocha --timeout 60000 \"integration-tests/playwright/${SPEC:-playwright-*}.spec.js\"",
91
+ "test:integration:playwright:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/playwright/${SPEC:-playwright-*}.spec.js\"",
81
92
  "test:integration:selenium": "mocha --timeout 60000 \"integration-tests/selenium/*.spec.js\"",
93
+ "test:integration:selenium:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/selenium/*.spec.js\"",
82
94
  "test:integration:vitest": "mocha --timeout 60000 \"integration-tests/vitest/*.spec.js\"",
95
+ "test:integration:vitest:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 60000 \"integration-tests/vitest/*.spec.js\"",
83
96
  "test:integration:testopt": "mocha --timeout 120000 \"integration-tests/ci-visibility/*.spec.js\"",
97
+ "test:integration:testopt:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 120000 \"integration-tests/ci-visibility/*.spec.js\"",
84
98
  "test:integration:profiler": "mocha --timeout 180000 \"integration-tests/profiler/*.spec.js\"",
85
- "test:integration:plugins": "mocha \"packages/datadog-plugin-@(${PLUGINS})/test/integration-test/**/*.spec.js\"",
99
+ "test:integration:profiler:coverage": "node ./integration-tests/coverage/run-suite.js --timeout 180000 \"integration-tests/profiler/*.spec.js\"",
100
+ "test:integration:plugins": "yarn services && mocha \"packages/datadog-plugin-@(${PLUGINS})/test/integration-test/**/${SPEC:-*}*.spec.js\"",
101
+ "test:integration:plugins:coverage": "yarn services && node ./integration-tests/coverage/run-suite.js \"packages/datadog-plugin-@(${PLUGINS})/test/integration-test/**/${SPEC:-*}*.spec.js\"",
86
102
  "test:unit:plugins": "mocha \"packages/datadog-instrumentations/test/@(${PLUGINS}).spec.js\" \"packages/datadog-plugin-@(${PLUGINS})/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@(${PLUGINS})/test/integration-test/**/*.spec.js\"",
87
103
  "test:shimmer": "mocha \"packages/datadog-shimmer/test/**/*.spec.js\"",
88
104
  "test:shimmer:ci": "nyc --silent node init && nyc -- npm run test:shimmer",
@@ -188,12 +204,15 @@
188
204
  "globals": "^17.2.0",
189
205
  "graphql": "*",
190
206
  "husky": "^9.1.7",
207
+ "istanbul-lib-report": "^3.0.0",
208
+ "istanbul-reports": "^3.0.2",
191
209
  "jszip": "^3.10.1",
192
210
  "mocha": "^11.6.0",
193
211
  "mocha-junit-reporter": "^2.2.1",
194
212
  "mocha-multi-reporters": "^1.5.1",
195
213
  "multer": "^2.1.1",
196
214
  "nock": "^13.5.6",
215
+ "node-preload": "^0.2.1",
197
216
  "nyc": "^18.0.0",
198
217
  "octokit": "^5.0.3",
199
218
  "opentracing": ">=0.14.7",
@@ -78,7 +78,7 @@ let knownTests = {}
78
78
  let isCodeCoverageEnabled = false
79
79
  let isCodeCoverageEnabledBecauseOfUs = false
80
80
  let isSuitesSkippingEnabled = false
81
- let isKeepingCoverageConfiguration = false
81
+ let DD_TEST_TIA_KEEP_COV_CONFIG = false
82
82
  let isUserCodeCoverageEnabled = false
83
83
  let isSuitesSkipped = false
84
84
  let numSkippedSuites = 0
@@ -1141,8 +1141,8 @@ function getCliWrapper (isNewJestVersion) {
1141
1141
  if (!err) {
1142
1142
  isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
1143
1143
  isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
1144
- isKeepingCoverageConfiguration =
1145
- libraryConfig.isKeepingCoverageConfiguration ?? isKeepingCoverageConfiguration
1144
+ DD_TEST_TIA_KEEP_COV_CONFIG =
1145
+ libraryConfig.DD_TEST_TIA_KEEP_COV_CONFIG ?? DD_TEST_TIA_KEEP_COV_CONFIG
1146
1146
  isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
1147
1147
  earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
1148
1148
  earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
@@ -1495,7 +1495,7 @@ function coverageReporterWrapper (coverageReporter) {
1495
1495
  */
1496
1496
  // `_addUntestedFiles` is an async function
1497
1497
  shimmer.wrap(CoverageReporter.prototype, '_addUntestedFiles', addUntestedFiles => function () {
1498
- if (isKeepingCoverageConfiguration) {
1498
+ if (DD_TEST_TIA_KEEP_COV_CONFIG) {
1499
1499
  return addUntestedFiles.apply(this, arguments)
1500
1500
  }
1501
1501
  if (isCodeCoverageEnabledBecauseOfUs) {
@@ -1697,7 +1697,7 @@ function configureTestEnvironment (readConfigsResult) {
1697
1697
  ...readConfigsResult.globalConfig,
1698
1698
  passWithNoTests: true,
1699
1699
  }
1700
- if (isCodeCoverageEnabledBecauseOfUs && !isKeepingCoverageConfiguration) {
1700
+ if (isCodeCoverageEnabledBecauseOfUs && !DD_TEST_TIA_KEEP_COV_CONFIG) {
1701
1701
  globalConfig.coverageReporters = ['none']
1702
1702
  readConfigsResult.configs = configs.map(config => ({
1703
1703
  ...config,
@@ -14,7 +14,7 @@ module.exports = class DdTraceApiPlugin extends Plugin {
14
14
  super(...args)
15
15
 
16
16
  const tracer = this._tracer
17
- const injectionEnabledTag = `injection_enabled:${this._tracerConfig.injectionEnabled ? 'yes' : 'no'}`
17
+ const injectionEnabledTag = `injection_enabled:${this._tracerConfig.DD_INJECTION_ENABLED ? 'yes' : 'no'}`
18
18
 
19
19
  this.addSub('datadog-api:v1:tracerinit', ({ proxy }) => {
20
20
  const proxyVal = proxy()
@@ -26,8 +26,8 @@ function extractErrorIntoSpanEvent (config, span, exc) {
26
26
  attributes.message = exc.message
27
27
  }
28
28
 
29
- if (config.graphqlErrorExtensions) {
30
- for (const ext of config.graphqlErrorExtensions) {
29
+ if (config.DD_TRACE_GRAPHQL_ERROR_EXTENSIONS) {
30
+ for (const ext of config.DD_TRACE_GRAPHQL_ERROR_EXTENSIONS) {
31
31
  if (exc.extensions?.[ext]) {
32
32
  const value = exc.extensions[ext]
33
33
 
@@ -16,7 +16,7 @@ class MemcachedPlugin extends CachePlugin {
16
16
  [CLIENT_PORT_KEY]: address[1],
17
17
  }
18
18
 
19
- if (this.config.memcachedCommandEnabled) {
19
+ if (this.config.DD_TRACE_MEMCACHED_COMMAND_ENABLED) {
20
20
  meta['memcached.command'] = query.command
21
21
  }
22
22
 
@@ -1,10 +1,16 @@
1
1
  'use strict'
2
2
 
3
+ const { LRUCache } = require('../../../../vendor/dist/lru-cache')
3
4
  const log = require('../log')
5
+ const web = require('../plugins/util/web')
4
6
  const blockedTemplates = require('./blocked_templates')
5
7
  const { updateBlockFailureMetric } = require('./telemetry')
6
8
 
7
- const detectedSpecificEndpoints = {}
9
+ // Bounded by the LRU as defense-in-depth: getSpecificKey already keys on the
10
+ // resolved route (or the path with the query string stripped) so cardinality
11
+ // follows the routing table, not the URL space.
12
+ const SPECIFIC_ENDPOINT_CACHE_MAX = 16_384
13
+ const detectedSpecificEndpoints = new LRUCache({ max: SPECIFIC_ENDPOINT_CACHE_MAX })
8
14
 
9
15
  const templateKeyword = '[security_response_id]'
10
16
 
@@ -38,12 +44,18 @@ const specificBlockingTypes = {
38
44
  GRAPHQL: 'graphqlJson',
39
45
  }
40
46
 
41
- function getSpecificKey (method, url) {
42
- return `${method}+${url}`
47
+ function getSpecificKey (req) {
48
+ const route = web.getContext(req)?.paths?.join('')
49
+ if (route) return `${req.method}+${route}`
50
+
51
+ // Strip the query string so unique parameters do not balloon the cache.
52
+ const url = req.originalUrl || req.url || ''
53
+ const queryStart = url.indexOf('?')
54
+ return `${req.method}+${queryStart === -1 ? url : url.slice(0, queryStart)}`
43
55
  }
44
56
 
45
- function addSpecificEndpoint (method, url, type) {
46
- detectedSpecificEndpoints[getSpecificKey(method, url)] = type
57
+ function addSpecificEndpoint (req, type) {
58
+ detectedSpecificEndpoints.set(getSpecificKey(req), type)
47
59
  }
48
60
 
49
61
  function getBlockWithRedirectData (actionParameters) {
@@ -65,7 +77,7 @@ function getBlockWithContentData (req, specificType, actionParameters) {
65
77
  let type
66
78
  let body
67
79
 
68
- const specificBlockingType = specificType || detectedSpecificEndpoints[getSpecificKey(req.method, req.url)]
80
+ const specificBlockingType = specificType || detectedSpecificEndpoints.get(getSpecificKey(req))
69
81
  if (specificBlockingType) {
70
82
  const specificBlockingContent = getTemplate(specificBlockingType, actionParameters)
71
83
  type = specificBlockingContent?.type
@@ -78,7 +78,7 @@ function enterInApolloRequest () {
78
78
  // Set isInGraphqlRequest=true since this function only runs for GraphQL requests
79
79
  // This works for both Apollo v4 (middleware) and v5 (HTTP server) contexts
80
80
  requestData.isInGraphqlRequest = true
81
- addSpecificEndpoint(req.method, req.originalUrl || req.url, specificBlockingTypes.GRAPHQL)
81
+ addSpecificEndpoint(req, specificBlockingTypes.GRAPHQL)
82
82
  }
83
83
  }
84
84
 
@@ -3,10 +3,9 @@
3
3
  const { storage } = require('../../datadog-core')
4
4
 
5
5
  /**
6
- * Spec (API semantics):
7
- * - OpenTelemetry Baggage API: https://opentelemetry.io/docs/specs/otel/baggage/api/
6
+ * In-process baggage map stored in async local storage. Frozen on every write.
8
7
  *
9
- * In-process baggage is a string->string map stored in async local storage.
8
+ * @see https://opentelemetry.io/docs/specs/otel/baggage/api/
10
9
  * @typedef {import('../../datadog-core/src/storage').Store<string>} BaggageStore
11
10
  */
12
11
 
@@ -18,6 +17,8 @@ const baggageStorage =
18
17
  /** @type {unknown} */ (storage('baggage'))
19
18
  )
20
19
 
20
+ const EMPTY_STORE = Object.freeze(/** @type {BaggageStore} */ ({}))
21
+
21
22
  // TODO: Implement metadata https://opentelemetry.io/docs/specs/otel/baggage/api/#set-value
22
23
  /**
23
24
  * @param {string} key
@@ -25,12 +26,11 @@ const baggageStorage =
25
26
  * @param {object} [metadata] Not used yet
26
27
  */
27
28
  function setBaggageItem (key, value, metadata) {
29
+ const store = baggageStorage.getStore()
28
30
  if (typeof key !== 'string' || typeof value !== 'string' || key === '') {
29
- return baggageStorage.getStore() ?? {}
31
+ return store ?? EMPTY_STORE
30
32
  }
31
-
32
- const store = baggageStorage.getStore()
33
- const newStore = { ...store, [key]: value }
33
+ const newStore = Object.freeze({ ...store, [key]: value })
34
34
  baggageStorage.enterWith(newStore)
35
35
  return newStore
36
36
  }
@@ -44,23 +44,26 @@ function getBaggageItem (key) {
44
44
  }
45
45
 
46
46
  function getAllBaggageItems () {
47
- return baggageStorage.getStore() ?? {}
47
+ return baggageStorage.getStore() ?? EMPTY_STORE
48
48
  }
49
49
 
50
50
  /**
51
- * @param {string} keyToRemove
51
+ * @param {string} keyToRemove No-op for non-string or empty keys.
52
52
  */
53
53
  function removeBaggageItem (keyToRemove) {
54
- const store = baggageStorage.getStore() ?? {}
54
+ const store = baggageStorage.getStore() ?? EMPTY_STORE
55
+ if (typeof keyToRemove !== 'string' || keyToRemove === '') {
56
+ return store
57
+ }
55
58
  const { [keyToRemove]: _, ...newBaggage } = store
59
+ Object.freeze(newBaggage)
56
60
  baggageStorage.enterWith(newBaggage)
57
61
  return newBaggage
58
62
  }
59
63
 
60
64
  function removeAllBaggageItems () {
61
- const newContext = /** @type {BaggageStore} */ ({})
62
- baggageStorage.enterWith(newContext)
63
- return newContext
65
+ baggageStorage.enterWith(EMPTY_STORE)
66
+ return EMPTY_STORE
64
67
  }
65
68
 
66
69
  module.exports = {
@@ -234,7 +234,7 @@ class CiVisibilityExporter extends BufferingExporter {
234
234
  testManagementAttemptToFixRetries ?? this._config.testManagementAttemptToFixRetries,
235
235
  isImpactedTestsEnabled: isImpactedTestsEnabled && this._config.isImpactedTestsEnabled,
236
236
  isCoverageReportUploadEnabled,
237
- isKeepingCoverageConfiguration: this._config.isKeepingCoverageConfiguration,
237
+ DD_TEST_TIA_KEEP_COV_CONFIG: this._config.DD_TEST_TIA_KEEP_COV_CONFIG,
238
238
  }
239
239
  }
240
240
 
@@ -48,8 +48,6 @@ export interface GeneratedConfig {
48
48
  baggageMaxBytes: number;
49
49
  baggageMaxItems: number;
50
50
  baggageTagKeys: string[];
51
- ciVisAgentlessLogSubmissionEnabled: boolean;
52
- ciVisibilityTestSessionName: string | undefined;
53
51
  clientIpEnabled: boolean;
54
52
  clientIpHeader: string | undefined;
55
53
  cloudPayloadTagging: {
@@ -73,6 +71,7 @@ export interface GeneratedConfig {
73
71
  };
74
72
  dbmPropagationMode: string;
75
73
  DD_ACTION_EXECUTION_ID: string | undefined;
74
+ DD_AGENTLESS_LOG_SUBMISSION_ENABLED: boolean;
76
75
  DD_AGENTLESS_LOG_SUBMISSION_URL: string | undefined;
77
76
  DD_APM_FLUSH_DEADLINE_MILLISECONDS: number;
78
77
  DD_AZURE_RESOURCE_GROUP: string | undefined;
@@ -83,6 +82,7 @@ export interface GeneratedConfig {
83
82
  DD_CIVISIBILITY_DANGEROUSLY_FORCE_TEST_SKIPPING: boolean;
84
83
  DD_CIVISIBILITY_ENABLED: boolean;
85
84
  DD_CIVISIBILITY_GIT_UNSHALLOW_ENABLED: boolean;
85
+ DD_CIVISIBILITY_MANUAL_API_ENABLED: boolean;
86
86
  DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS: number;
87
87
  DD_CIVISIBILITY_TEST_COMMAND: string | undefined;
88
88
  DD_CIVISIBILITY_TEST_MODULE_ID: string | undefined;
@@ -111,8 +111,12 @@ export interface GeneratedConfig {
111
111
  DD_GIT_PULL_REQUEST_BASE_BRANCH_SHA: string | undefined;
112
112
  DD_GIT_REPOSITORY_URL: string | undefined;
113
113
  DD_GIT_TAG: string | undefined;
114
+ DD_INJECT_FORCE: boolean;
115
+ DD_INJECTION_ENABLED: string | undefined;
116
+ DD_INSTRUMENTATION_CONFIG_ID: string | undefined;
114
117
  DD_INTERNAL_PROFILING_TIMELINE_SAMPLING_ENABLED: boolean;
115
118
  DD_LAMBDA_HANDLER: string | undefined;
119
+ DD_LOGS_OTEL_ENABLED: boolean;
116
120
  DD_MINI_AGENT_PATH: string | undefined;
117
121
  DD_PIPELINE_EXECUTION_ID: string | undefined;
118
122
  DD_PLAYWRIGHT_WORKER: string | undefined;
@@ -147,6 +151,8 @@ export interface GeneratedConfig {
147
151
  DD_TELEMETRY_FORWARDER_PATH: string | undefined;
148
152
  DD_TEST_FLEET_CONFIG_PATH: string | undefined;
149
153
  DD_TEST_LOCAL_CONFIG_PATH: string | undefined;
154
+ DD_TEST_SESSION_NAME: string | undefined;
155
+ DD_TEST_TIA_KEEP_COV_CONFIG: boolean;
150
156
  DD_TRACE_AEROSPIKE_ENABLED: boolean;
151
157
  DD_TRACE_AI_ENABLED: boolean;
152
158
  DD_TRACE_AMQP10_ENABLED: boolean;
@@ -239,11 +245,13 @@ export interface GeneratedConfig {
239
245
  DD_TRACE_FS_ENABLED: boolean;
240
246
  DD_TRACE_GCP_PUBSUB_PUSH_ENABLED: boolean;
241
247
  DD_TRACE_GENERIC_POOL_ENABLED: boolean;
248
+ DD_TRACE_GIT_METADATA_ENABLED: boolean;
242
249
  DD_TRACE_GOOGLE_CLOUD_PUBSUB_ENABLED: boolean;
243
250
  DD_TRACE_GOOGLE_CLOUD_VERTEXAI_ENABLED: boolean;
244
251
  DD_TRACE_GOOGLE_GAX_ENABLED: boolean;
245
252
  DD_TRACE_GOOGLE_GENAI_ENABLED: boolean;
246
253
  DD_TRACE_GRAPHQL_ENABLED: boolean;
254
+ DD_TRACE_GRAPHQL_ERROR_EXTENSIONS: string[];
247
255
  DD_TRACE_GRAPHQL_TAG_ENABLED: boolean;
248
256
  DD_TRACE_GRAPHQL_TOOLS_ENABLED: boolean;
249
257
  DD_TRACE_GRAPHQL_TOOLS_EXECUTOR_ENABLED: boolean;
@@ -292,6 +300,7 @@ export interface GeneratedConfig {
292
300
  DD_TRACE_LODASH_ENABLED: boolean;
293
301
  DD_TRACE_LOOPBACK_ENABLED: boolean;
294
302
  DD_TRACE_MARIADB_ENABLED: boolean;
303
+ DD_TRACE_MEMCACHED_COMMAND_ENABLED: boolean;
295
304
  DD_TRACE_MEMCACHED_ENABLED: boolean;
296
305
  DD_TRACE_MICROGATEWAY_CORE_ENABLED: boolean;
297
306
  DD_TRACE_MIDDIE_ENABLED: boolean;
@@ -335,6 +344,7 @@ export interface GeneratedConfig {
335
344
  DD_TRACE_PROCESS_ENABLED: boolean;
336
345
  DD_TRACE_PROMISE_ENABLED: boolean;
337
346
  DD_TRACE_PROMISE_JS_ENABLED: boolean;
347
+ DD_TRACE_PROPAGATION_EXTRACT_FIRST: boolean;
338
348
  DD_TRACE_PROPAGATION_STYLE: string[];
339
349
  DD_TRACE_PROTOBUFJS_ENABLED: boolean;
340
350
  DD_TRACE_PUG_ENABLED: boolean;
@@ -352,6 +362,7 @@ export interface GeneratedConfig {
352
362
  DD_TRACE_SEQUELIZE_ENABLED: boolean;
353
363
  DD_TRACE_SHAREDB_ENABLED: boolean;
354
364
  DD_TRACE_SMITHY_SMITHY_CLIENT_ENABLED: boolean;
365
+ DD_TRACE_SPAN_LEAK_DEBUG: number;
355
366
  DD_TRACE_SQLITE3_ENABLED: boolean;
356
367
  DD_TRACE_SUFFIXPLUGIN_ENABLED: boolean;
357
368
  DD_TRACE_TAGS: Record<string, string> | undefined;
@@ -365,6 +376,7 @@ export interface GeneratedConfig {
365
376
  DD_TRACE_WINSTON_ENABLED: boolean;
366
377
  DD_TRACE_WORKERPOOL_ENABLED: boolean;
367
378
  DD_TRACE_WS_ENABLED: boolean;
379
+ DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH: number;
368
380
  DD_VITEST_WORKER: string | undefined;
369
381
  dogstatsd: {
370
382
  hostname: string;
@@ -405,8 +417,6 @@ export interface GeneratedConfig {
405
417
  flakyTestRetriesCount: number;
406
418
  flushInterval: number;
407
419
  flushMinSpans: number;
408
- gitMetadataEnabled: boolean;
409
- graphqlErrorExtensions: string[];
410
420
  grpc: {
411
421
  client: {
412
422
  error: {
@@ -443,21 +453,16 @@ export interface GeneratedConfig {
443
453
  telemetryVerbosity: string;
444
454
  };
445
455
  inferredProxyServicesEnabled: boolean;
446
- injectForce: boolean;
447
- injectionEnabled: string | undefined;
448
456
  installSignature: {
449
457
  id: string | undefined;
450
458
  time: string | undefined;
451
459
  type: string | undefined;
452
460
  };
453
- instrumentation_config_id: string | undefined;
454
461
  isEarlyFlakeDetectionEnabled: boolean;
455
462
  isFlakyTestRetriesEnabled: boolean;
456
463
  isGitUploadEnabled: boolean;
457
464
  isImpactedTestsEnabled: boolean;
458
465
  isIntelligentTestRunnerEnabled: boolean;
459
- isKeepingCoverageConfiguration: boolean;
460
- isManualApiEnabled: boolean;
461
466
  isTestDynamicInstrumentationEnabled: boolean;
462
467
  isTestManagementEnabled: boolean;
463
468
  langchain: {
@@ -472,43 +477,41 @@ export interface GeneratedConfig {
472
477
  };
473
478
  logInjection: boolean;
474
479
  logLevel: "debug" | "info" | "warn" | "error";
475
- memcachedCommandEnabled: boolean;
476
480
  middlewareTracingEnabled: boolean;
477
481
  openai: {
478
482
  spanCharLimit: number;
479
483
  };
480
484
  openAiLogsEnabled: boolean;
485
+ OTEL_BSP_MAX_EXPORT_BATCH_SIZE: number;
486
+ OTEL_BSP_MAX_QUEUE_SIZE: number;
487
+ OTEL_BSP_SCHEDULE_DELAY: number;
481
488
  OTEL_EXPORTER_OTLP_ENDPOINT: string | undefined;
482
489
  OTEL_EXPORTER_OTLP_HEADERS: Record<string, string> | undefined;
490
+ OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: string;
483
491
  OTEL_EXPORTER_OTLP_LOGS_HEADERS: Record<string, string> | undefined;
492
+ OTEL_EXPORTER_OTLP_LOGS_PROTOCOL: string;
493
+ OTEL_EXPORTER_OTLP_LOGS_TIMEOUT: number;
494
+ OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: string;
484
495
  OTEL_EXPORTER_OTLP_METRICS_HEADERS: Record<string, string> | undefined;
496
+ OTEL_EXPORTER_OTLP_METRICS_PROTOCOL: string;
497
+ OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: "DELTA" | "CUMULATIVE" | "LOWMEMORY";
498
+ OTEL_EXPORTER_OTLP_METRICS_TIMEOUT: number;
499
+ OTEL_EXPORTER_OTLP_PROTOCOL: string;
500
+ OTEL_EXPORTER_OTLP_TIMEOUT: number;
485
501
  OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: string;
486
502
  OTEL_EXPORTER_OTLP_TRACES_HEADERS: Record<string, string> | undefined;
487
503
  OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: "http/json";
488
504
  OTEL_EXPORTER_OTLP_TRACES_TIMEOUT: number;
489
505
  OTEL_LOGS_EXPORTER: "none" | "otlp" | undefined;
506
+ OTEL_METRIC_EXPORT_INTERVAL: number;
507
+ OTEL_METRIC_EXPORT_TIMEOUT: number;
490
508
  OTEL_METRICS_EXPORTER: "none" | "otlp" | undefined;
491
509
  OTEL_RESOURCE_ATTRIBUTES: Record<string, string>;
492
510
  OTEL_SDK_DISABLED: boolean;
493
511
  OTEL_TRACES_EXPORTER: "none" | "otlp" | undefined;
494
512
  OTEL_TRACES_SAMPLER: "always_on" | "always_off" | "traceidratio" | "parentbased_always_on" | "parentbased_always_off" | "parentbased_traceidratio";
495
513
  OTEL_TRACES_SAMPLER_ARG: number | undefined;
496
- otelBatchTimeout: number;
497
- otelLogsEnabled: boolean;
498
- otelLogsProtocol: string;
499
- otelLogsTimeout: number;
500
- otelLogsUrl: string;
501
- otelMaxExportBatchSize: number;
502
- otelMaxQueueSize: number;
503
514
  otelMetricsEnabled: boolean;
504
- otelMetricsExportInterval: number;
505
- otelMetricsExportTimeout: number;
506
- otelMetricsProtocol: string;
507
- otelMetricsTemporalityPreference: "DELTA" | "CUMULATIVE" | "LOWMEMORY";
508
- otelMetricsTimeout: number;
509
- otelMetricsUrl: string;
510
- otelProtocol: string;
511
- otelTimeout: number;
512
515
  peerServiceMapping: Record<string, string>;
513
516
  port: string | number;
514
517
  profiling: {
@@ -542,7 +545,6 @@ export interface GeneratedConfig {
542
545
  site: string;
543
546
  spanAttributeSchema: "v0" | "v1";
544
547
  spanComputePeerService: boolean;
545
- spanLeakDebug: number;
546
548
  spanRemoveIntegrationFromService: boolean;
547
549
  spanSamplingRules: import('../../../../index').SpanSamplingRule[] | undefined;
548
550
  startupLogs: boolean;
@@ -550,7 +552,6 @@ export interface GeneratedConfig {
550
552
  enabled: boolean;
551
553
  };
552
554
  tags: Record<string, string>;
553
- tagsHeaderMaxLength: number;
554
555
  telemetry: {
555
556
  debug: boolean;
556
557
  dependencyCollection: boolean;
@@ -573,7 +574,6 @@ export interface GeneratedConfig {
573
574
  traceId128BitGenerationEnabled: boolean;
574
575
  traceId128BitLoggingEnabled: boolean;
575
576
  tracePropagationBehaviorExtract: "continue" | "restart" | "ignore";
576
- tracePropagationExtractFirst: boolean;
577
577
  tracePropagationStyle: {
578
578
  extract: string[];
579
579
  inject: string[];
@@ -214,7 +214,7 @@ class Config extends ConfigBase {
214
214
 
215
215
  warnWrongOtelSettings()
216
216
 
217
- if (this.gitMetadataEnabled) {
217
+ if (this.DD_TRACE_GIT_METADATA_ENABLED) {
218
218
  this.#loadGitMetadata()
219
219
  }
220
220
 
@@ -355,7 +355,7 @@ class Config extends ConfigBase {
355
355
  }
356
356
  // Disable log injection when OTEL logs are enabled
357
357
  // OTEL logs and DD log injection are mutually exclusive
358
- if (this.otelLogsEnabled) {
358
+ if (this.DD_LOGS_OTEL_ENABLED) {
359
359
  setAndTrack(this, 'logInjection', false)
360
360
  }
361
361
  if (this.otelMetricsEnabled &&
@@ -427,7 +427,7 @@ class Config extends ConfigBase {
427
427
  ))
428
428
  }
429
429
 
430
- if (this.injectionEnabled) {
430
+ if (this.DD_INJECTION_ENABLED) {
431
431
  setAndTrack(this, 'instrumentationSource', 'ssi')
432
432
  }
433
433
 
@@ -600,11 +600,11 @@ class Config extends ConfigBase {
600
600
  // Default OTLP endpoints follow the configured agent host so users who point DD at a custom
601
601
  // agent (DD_AGENT_HOST / DD_TRACE_AGENT_URL) also reach OTLP on that host.
602
602
  const defaultOtlpBase = this.OTEL_EXPORTER_OTLP_ENDPOINT?.replace(/\/$/, '') ?? `http://${agentHostname}:4318`
603
- if (!this.otelLogsUrl) {
604
- setAndTrack(this, 'otelLogsUrl', `${defaultOtlpBase}/v1/logs`)
603
+ if (!this.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) {
604
+ setAndTrack(this, 'OTEL_EXPORTER_OTLP_LOGS_ENDPOINT', `${defaultOtlpBase}/v1/logs`)
605
605
  }
606
- if (!this.otelMetricsUrl) {
607
- setAndTrack(this, 'otelMetricsUrl', `${defaultOtlpBase}/v1/metrics`)
606
+ if (!this.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) {
607
+ setAndTrack(this, 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', `${defaultOtlpBase}/v1/metrics`)
608
608
  }
609
609
  if (!this.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) {
610
610
  setAndTrack(this, 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', `${defaultOtlpBase}/v1/traces`)
@@ -12,8 +12,7 @@
12
12
  {
13
13
  "implementation": "A",
14
14
  "type": "boolean",
15
- "default": "false",
16
- "internalPropertyName": "ciVisAgentlessLogSubmissionEnabled"
15
+ "default": "false"
17
16
  }
18
17
  ],
19
18
  "DD_AGENTLESS_LOG_SUBMISSION_URL": [
@@ -541,8 +540,7 @@
541
540
  {
542
541
  "implementation": "A",
543
542
  "type": "boolean",
544
- "default": "true",
545
- "internalPropertyName": "isManualApiEnabled"
543
+ "default": "true"
546
544
  }
547
545
  ],
548
546
  "DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS": [
@@ -1105,24 +1103,21 @@
1105
1103
  {
1106
1104
  "implementation": "C",
1107
1105
  "type": "string",
1108
- "default": null,
1109
- "internalPropertyName": "injectionEnabled"
1106
+ "default": null
1110
1107
  }
1111
1108
  ],
1112
1109
  "DD_INJECT_FORCE": [
1113
1110
  {
1114
1111
  "implementation": "A",
1115
1112
  "type": "boolean",
1116
- "default": "false",
1117
- "internalPropertyName": "injectForce"
1113
+ "default": "false"
1118
1114
  }
1119
1115
  ],
1120
1116
  "DD_INSTRUMENTATION_CONFIG_ID": [
1121
1117
  {
1122
1118
  "implementation": "A",
1123
1119
  "type": "string",
1124
- "default": null,
1125
- "internalPropertyName": "instrumentation_config_id"
1120
+ "default": null
1126
1121
  }
1127
1122
  ],
1128
1123
  "DD_INSTRUMENTATION_INSTALL_ID": [
@@ -1240,8 +1235,7 @@
1240
1235
  {
1241
1236
  "implementation": "A",
1242
1237
  "type": "boolean",
1243
- "default": "false",
1244
- "internalPropertyName": "otelLogsEnabled"
1238
+ "default": "false"
1245
1239
  }
1246
1240
  ],
1247
1241
  "DD_TRACE_LOG_LEVEL": [
@@ -1790,16 +1784,14 @@
1790
1784
  {
1791
1785
  "implementation": "A",
1792
1786
  "type": "boolean",
1793
- "default": "false",
1794
- "internalPropertyName": "isKeepingCoverageConfiguration"
1787
+ "default": "false"
1795
1788
  }
1796
1789
  ],
1797
1790
  "DD_TEST_SESSION_NAME": [
1798
1791
  {
1799
1792
  "implementation": "A",
1800
1793
  "type": "string",
1801
- "default": null,
1802
- "internalPropertyName": "ciVisibilityTestSessionName"
1794
+ "default": null
1803
1795
  }
1804
1796
  ],
1805
1797
  "DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED": [
@@ -2644,8 +2636,7 @@
2644
2636
  {
2645
2637
  "implementation": "A",
2646
2638
  "type": "boolean",
2647
- "default": "true",
2648
- "internalPropertyName": "gitMetadataEnabled"
2639
+ "default": "true"
2649
2640
  }
2650
2641
  ],
2651
2642
  "DD_TRACE_GOOGLE_CLOUD_PUBSUB_ENABLED": [
@@ -2687,8 +2678,7 @@
2687
2678
  {
2688
2679
  "implementation": "A",
2689
2680
  "type": "array",
2690
- "default": "",
2691
- "internalPropertyName": "graphqlErrorExtensions"
2681
+ "default": ""
2692
2682
  }
2693
2683
  ],
2694
2684
  "DD_TRACE_GRAPHQL_TAG_ENABLED": [
@@ -3062,8 +3052,7 @@
3062
3052
  {
3063
3053
  "implementation": "A",
3064
3054
  "type": "boolean",
3065
- "default": "false",
3066
- "internalPropertyName": "memcachedCommandEnabled"
3055
+ "default": "false"
3067
3056
  }
3068
3057
  ],
3069
3058
  "DD_TRACE_MEMCACHED_ENABLED": [
@@ -3437,8 +3426,7 @@
3437
3426
  {
3438
3427
  "implementation": "A",
3439
3428
  "type": "boolean",
3440
- "default": "false",
3441
- "internalPropertyName": "tracePropagationExtractFirst"
3429
+ "default": "false"
3442
3430
  }
3443
3431
  ],
3444
3432
  "DD_TRACE_PROPAGATION_STYLE": [
@@ -3678,7 +3666,6 @@
3678
3666
  {
3679
3667
  "implementation": "A",
3680
3668
  "type": "int",
3681
- "internalPropertyName": "spanLeakDebug",
3682
3669
  "default": "0"
3683
3670
  }
3684
3671
  ],
@@ -3831,8 +3818,7 @@
3831
3818
  {
3832
3819
  "implementation": "A",
3833
3820
  "type": "int",
3834
- "default": "512",
3835
- "internalPropertyName": "tagsHeaderMaxLength"
3821
+ "default": "512"
3836
3822
  }
3837
3823
  ],
3838
3824
  "DD_VERSION": [
@@ -3872,7 +3858,6 @@
3872
3858
  {
3873
3859
  "implementation": "A",
3874
3860
  "type": "int",
3875
- "internalPropertyName": "otelMaxExportBatchSize",
3876
3861
  "default": "512",
3877
3862
  "allowed": "[1-9]\\d*"
3878
3863
  }
@@ -3881,7 +3866,6 @@
3881
3866
  {
3882
3867
  "implementation": "A",
3883
3868
  "type": "int",
3884
- "internalPropertyName": "otelMaxQueueSize",
3885
3869
  "default": "2048",
3886
3870
  "allowed": "[1-9]\\d*"
3887
3871
  }
@@ -3890,7 +3874,6 @@
3890
3874
  {
3891
3875
  "implementation": "A",
3892
3876
  "type": "int",
3893
- "internalPropertyName": "otelBatchTimeout",
3894
3877
  "default": "5000",
3895
3878
  "allowed": "[1-9]\\d*"
3896
3879
  }
@@ -3951,8 +3934,7 @@
3951
3934
  {
3952
3935
  "implementation": "A",
3953
3936
  "type": "string",
3954
- "default": null,
3955
- "internalPropertyName": "otelLogsUrl"
3937
+ "default": null
3956
3938
  }
3957
3939
  ],
3958
3940
  "OTEL_EXPORTER_OTLP_LOGS_HEADERS": [
@@ -3970,7 +3952,6 @@
3970
3952
  "implementation": "D",
3971
3953
  "type": "string",
3972
3954
  "default": "http/protobuf",
3973
- "internalPropertyName": "otelLogsProtocol",
3974
3955
  "aliases": [
3975
3956
  "OTEL_EXPORTER_OTLP_PROTOCOL"
3976
3957
  ]
@@ -3980,7 +3961,6 @@
3980
3961
  {
3981
3962
  "implementation": "A",
3982
3963
  "type": "int",
3983
- "internalPropertyName": "otelLogsTimeout",
3984
3964
  "default": "10000",
3985
3965
  "allowed": "[1-9]\\d*",
3986
3966
  "aliases": [
@@ -3992,8 +3972,7 @@
3992
3972
  {
3993
3973
  "implementation": "A",
3994
3974
  "type": "string",
3995
- "default": null,
3996
- "internalPropertyName": "otelMetricsUrl"
3975
+ "default": null
3997
3976
  }
3998
3977
  ],
3999
3978
  "OTEL_EXPORTER_OTLP_METRICS_HEADERS": [
@@ -4011,7 +3990,6 @@
4011
3990
  "implementation": "B",
4012
3991
  "type": "string",
4013
3992
  "default": "http/protobuf",
4014
- "internalPropertyName": "otelMetricsProtocol",
4015
3993
  "aliases": [
4016
3994
  "OTEL_EXPORTER_OTLP_PROTOCOL"
4017
3995
  ]
@@ -4023,7 +4001,6 @@
4023
4001
  "type": "string",
4024
4002
  "allowed": "Delta|Cumulative|LowMemory",
4025
4003
  "transform": "toUpperCase",
4026
- "internalPropertyName": "otelMetricsTemporalityPreference",
4027
4004
  "default": "delta"
4028
4005
  }
4029
4006
  ],
@@ -4032,7 +4009,6 @@
4032
4009
  "implementation": "B",
4033
4010
  "type": "int",
4034
4011
  "allowed": "[1-9]\\d*",
4035
- "internalPropertyName": "otelMetricsTimeout",
4036
4012
  "default": "10000",
4037
4013
  "aliases": [
4038
4014
  "OTEL_EXPORTER_OTLP_TIMEOUT"
@@ -4043,8 +4019,7 @@
4043
4019
  {
4044
4020
  "implementation": "A",
4045
4021
  "type": "string",
4046
- "default": "http/protobuf",
4047
- "internalPropertyName": "otelProtocol"
4022
+ "default": "http/protobuf"
4048
4023
  }
4049
4024
  ],
4050
4025
  "OTEL_EXPORTER_OTLP_TIMEOUT": [
@@ -4052,7 +4027,6 @@
4052
4027
  "implementation": "A",
4053
4028
  "type": "int",
4054
4029
  "allowed": "[1-9]\\d*",
4055
- "internalPropertyName": "otelTimeout",
4056
4030
  "default": "10000"
4057
4031
  }
4058
4032
  ],
@@ -4079,7 +4053,6 @@
4079
4053
  "implementation": "A",
4080
4054
  "type": "int",
4081
4055
  "allowed": "[1-9]\\d*",
4082
- "internalPropertyName": "otelMetricsExportInterval",
4083
4056
  "default": "10000"
4084
4057
  }
4085
4058
  ],
@@ -4088,7 +4061,6 @@
4088
4061
  "implementation": "A",
4089
4062
  "type": "int",
4090
4063
  "allowed": "[1-9]\\d*",
4091
- "internalPropertyName": "otelMetricsExportTimeout",
4092
4064
  "default": "7500"
4093
4065
  }
4094
4066
  ],
@@ -266,6 +266,8 @@ class MetricsAggregationClient {
266
266
  this._captureTree(this._gauges, (node, name, tags) => {
267
267
  this._client.gauge(name, node.value, tags)
268
268
  })
269
+
270
+ this._gauges.clear()
269
271
  }
270
272
 
271
273
  _captureCounters () {
@@ -278,12 +280,7 @@ class MetricsAggregationClient {
278
280
 
279
281
  _captureHistograms () {
280
282
  this._captureTree(this._histograms, (node, name, tags) => {
281
- let stats = node.value
282
-
283
- // Stats can contain garbage data when a value was never recorded.
284
- if (stats.count === 0) {
285
- stats = { max: 0, min: 0, sum: 0, avg: 0, median: 0, p95: 0, count: 0 }
286
- }
283
+ const stats = node.value
287
284
 
288
285
  this._client.gauge(`${name}.min`, stats.min, tags)
289
286
  this._client.gauge(`${name}.max`, stats.max, tags)
@@ -293,9 +290,9 @@ class MetricsAggregationClient {
293
290
  this._client.increment(`${name}.count`, stats.count, tags)
294
291
  this._client.gauge(`${name}.median`, stats.median, tags)
295
292
  this._client.gauge(`${name}.95percentile`, stats.p95, tags)
296
-
297
- node.value.reset()
298
293
  })
294
+
295
+ this._histograms.clear()
299
296
  }
300
297
 
301
298
  _captureTree (tree, fn) {
@@ -29,7 +29,7 @@ module.exports = function getExporter (name) {
29
29
  fs.existsSync(constants.DATADOG_LAMBDA_EXTENSION_PATH) ||
30
30
  fs.existsSync(constants.DATADOG_MINI_AGENT_PATH)
31
31
  )
32
- return require(inAWSLambda && !usingAgent ? './exporters/log' : './exporters/agent')
32
+ return inAWSLambda && !usingAgent ? require('./exporters/log') : require('./exporters/agent')
33
33
  }
34
34
  }
35
35
  }
@@ -8,7 +8,7 @@ class GitMetadataTagger {
8
8
  }
9
9
 
10
10
  tagGitMetadata (spanContext) {
11
- if (this._config.gitMetadataEnabled) {
11
+ if (this._config.DD_TRACE_GIT_METADATA_ENABLED) {
12
12
  // These tags are added only to the local root span
13
13
  spanContext._trace.tags[SCI_COMMIT_SHA] = this._config.commitSHA
14
14
  spanContext._trace.tags[SCI_REPOSITORY_URL] = this._config.repositoryUrl
@@ -16,6 +16,9 @@ module.exports = {
16
16
  NAME: '_ml_obs.name',
17
17
  TRACE_ID: '_ml_obs.trace_id',
18
18
  PROPAGATED_TRACE_ID_KEY: '_dd.p.llmobs_trace_id',
19
+ LLMOBS_TRACE_ID_BRIDGE_KEY: 'llmobs_trace_id',
20
+ LLMOBS_PARENT_ID_BRIDGE_KEY: 'llmobs_parent_id',
21
+ LLMOBS_SUBMITTED_TAG_KEY: '_dd.llmobs.submitted',
19
22
  ROOT_PARENT_ID: 'undefined',
20
23
  DEFAULT_PROMPT_NAME: 'unnamed-prompt',
21
24
  INTERNAL_CONTEXT_VARIABLE_KEYS: '_dd_context_variable_keys',
@@ -7,7 +7,13 @@ const tracerVersion = require('../../../../package.json').version
7
7
  const logger = require('../log')
8
8
  const { getValueFromEnvSources } = require('../config/helper')
9
9
  const Span = require('../opentracing/span')
10
- const { SPAN_KIND, OUTPUT_VALUE, INPUT_VALUE } = require('./constants/tags')
10
+ const {
11
+ SPAN_KIND,
12
+ OUTPUT_VALUE,
13
+ INPUT_VALUE,
14
+ LLMOBS_TRACE_ID_BRIDGE_KEY,
15
+ LLMOBS_PARENT_ID_BRIDGE_KEY,
16
+ } = require('./constants/tags')
11
17
  const {
12
18
  getFunctionArguments,
13
19
  validateKind,
@@ -533,6 +539,20 @@ class LLMObs extends NoopLLMObs {
533
539
  ...options,
534
540
  parent: parentStore?.span,
535
541
  })
542
+
543
+ // Bridge tags read by the dd-go LLMObs trace-indexer to correlate OTel
544
+ // gen_ai.* spans with SDK LLMObs spans. Written once per local trace,
545
+ // on the first successful SDK LLMObs span registration. The shared
546
+ // _trace.tags bag is serialized to the first span in every flushed
547
+ // chunk's meta, so partial flush is covered automatically without a
548
+ // separate flush-time processor. Writing only after registerLLMObsSpan
549
+ // succeeds avoids poisoning _trace.tags with bridge tags pointing at a
550
+ // span that will never produce an LLMObs event.
551
+ const traceTags = span?.context?.()._trace?.tags
552
+ if (this.enabled && traceTags && !traceTags[LLMOBS_TRACE_ID_BRIDGE_KEY]) {
553
+ traceTags[LLMOBS_TRACE_ID_BRIDGE_KEY] = span.context().toTraceId(true)
554
+ traceTags[LLMOBS_PARENT_ID_BRIDGE_KEY] = span.context().toSpanId()
555
+ }
536
556
  }
537
557
 
538
558
  try {
@@ -30,6 +30,7 @@ const {
30
30
  INPUT_PROMPT,
31
31
  ROUTING_API_KEY,
32
32
  ROUTING_SITE,
33
+ LLMOBS_SUBMITTED_TAG_KEY,
33
34
  } = require('./constants/tags')
34
35
  const { UNSERIALIZABLE_VALUE_TEXT } = require('./constants/text')
35
36
  const telemetry = require('./telemetry')
@@ -87,7 +88,19 @@ class LLMObsSpanProcessor {
87
88
  site: mlObsTags[ROUTING_SITE],
88
89
  }
89
90
 
90
- this.#writer.append(formattedEvent, routing)
91
+ const enqueued = this.#writer.append(formattedEvent, routing)
92
+
93
+ // Marker read by the dd-go LLMObs trace-indexer: when reparenting OTel
94
+ // gen_ai.* spans, the parent-chain walk stops at any span carrying this
95
+ // tag, preserving this span as the immediate LLMObs parent. Set only
96
+ // when the writer actually buffered the event — format may have dropped
97
+ // it (user processor returned null), thrown, or the writer may have
98
+ // dropped it silently when its buffer is full. Leaving this tag off in
99
+ // those cases avoids dd-go reparenting OTel children under a span that
100
+ // has no corresponding LLMObs event.
101
+ if (enqueued) {
102
+ span.context()._tags[LLMOBS_SUBMITTED_TAG_KEY] = '1'
103
+ }
91
104
  } catch (e) {
92
105
  // this should be a rare case
93
106
  // we protect against unserializable properties in the format function, and in
@@ -87,19 +87,25 @@ class BaseLLMObsWriter {
87
87
  return buffer
88
88
  }
89
89
 
90
+ /**
91
+ * @returns {boolean} `true` if the event was buffered, `false` if it was dropped
92
+ * (e.g. the per-routing buffer was full). Callers that depend on the event
93
+ * actually being submitted should check this value.
94
+ */
90
95
  append (event, routing, byteLength) {
91
96
  const buffer = this._getBuffer(routing)
92
97
 
93
98
  if (buffer.events.length >= buffer.limit) {
94
99
  logger.warn(`${this.constructor.name} event buffer full (limit is ${buffer.limit}), dropping event`)
95
100
  telemetry.recordDroppedPayload(1, this._eventType, 'buffer_full')
96
- return
101
+ return false
97
102
  }
98
103
 
99
104
  const eventSize = byteLength || Buffer.byteLength(JSON.stringify(event))
100
105
 
101
106
  buffer.size += eventSize
102
107
  buffer.events.push(event)
108
+ return true
103
109
  }
104
110
 
105
111
  flush () {
@@ -45,7 +45,7 @@ class LLMObsSpanWriter extends BaseWriter {
45
45
  this.flush()
46
46
  }
47
47
 
48
- super.append(event, routing, processedEventSizeBytes)
48
+ return super.append(event, routing, processedEventSizeBytes)
49
49
  }
50
50
 
51
51
  makePayload (events) {
@@ -60,18 +60,18 @@ function initializeOpenTelemetryLogs (config) {
60
60
 
61
61
  // Create OTLP exporter using resolved config values
62
62
  const exporter = new OtlpHttpLogExporter(
63
- config.otelLogsUrl,
63
+ config.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
64
64
  config.OTEL_EXPORTER_OTLP_LOGS_HEADERS,
65
- config.otelLogsTimeout,
66
- config.otelLogsProtocol,
65
+ config.OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
66
+ config.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
67
67
  resourceAttributes
68
68
  )
69
69
 
70
70
  // Create batch processor for exporting logs to Datadog Agent
71
71
  const processor = new BatchLogRecordProcessor(
72
72
  exporter,
73
- config.otelBatchTimeout,
74
- config.otelMaxExportBatchSize
73
+ config.OTEL_BSP_SCHEDULE_DELAY,
74
+ config.OTEL_BSP_MAX_EXPORT_BATCH_SIZE
75
75
  )
76
76
 
77
77
  // Create logger provider with processor for Datadog Agent export
@@ -57,18 +57,18 @@ function initializeOpenTelemetryMetrics (config) {
57
57
  }
58
58
 
59
59
  const exporter = new OtlpHttpMetricExporter(
60
- config.otelMetricsUrl,
60
+ config.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
61
61
  config.OTEL_EXPORTER_OTLP_METRICS_HEADERS,
62
- config.otelMetricsTimeout,
63
- config.otelMetricsProtocol,
62
+ config.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
63
+ config.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
64
64
  resourceAttributes
65
65
  )
66
66
 
67
67
  const reader = new PeriodicMetricReader(
68
68
  exporter,
69
- config.otelMetricsExportInterval,
70
- config.otelMetricsTemporalityPreference,
71
- config.otelMaxQueueSize
69
+ config.OTEL_METRIC_EXPORT_INTERVAL,
70
+ config.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE,
71
+ config.OTEL_BSP_MAX_QUEUE_SIZE
72
72
  )
73
73
 
74
74
  const meterProvider = new MeterProvider({ reader })
@@ -19,7 +19,7 @@ const OtlpTraceTransformer = require('./otlp_transformer')
19
19
  * (https://opentelemetry.io/docs/specs/otel/trace/sdk/#batching-processor).
20
20
  * Currently each finished trace is sent as its own HTTP request, which is
21
21
  * unsuitable for high-traffic production environments. The config values
22
- * `otelBatchTimeout`, `otelMaxExportBatchSize`, and `otelMaxQueueSize`
22
+ * `OTEL_BSP_SCHEDULE_DELAY`, `OTEL_BSP_MAX_EXPORT_BATCH_SIZE`, and `OTEL_BSP_MAX_QUEUE_SIZE`
23
23
  * (OTEL_BSP_*) are already defined and should drive that implementation.
24
24
  *
25
25
  * @class OtlpHttpTraceExporter
@@ -64,6 +64,7 @@ const tracestateTagValueFilter = /[^\x20-\x2B\x2D-\x3A\x3C-\x7D]/g
64
64
  const invalidSegment = /^0+$/
65
65
  const zeroTraceId = '0000000000000000'
66
66
  const hex16 = /^[0-9A-Fa-f]{16}$/
67
+ const percentByte = /%([0-9A-Fa-f]{2})/g
67
68
 
68
69
  class TextMapPropagator {
69
70
  #extractB3Context
@@ -173,15 +174,15 @@ class TextMapPropagator {
173
174
  const baggageItems = getAllBaggageItems()
174
175
  if (!baggageItems) return
175
176
  for (const [key, value] of Object.entries(baggageItems)) {
176
- const baggageKey = String(key).trim()
177
- if (!baggageKey || !baggageTokenExpr.test(baggageKey)) continue
177
+ const baggageKey = key.trim()
178
+ if (!baggageTokenExpr.test(baggageKey)) continue
178
179
 
179
180
  // Do not trim values. If callers include leading/trailing whitespace, it must be percent-encoded.
180
181
  // W3C list-member allows optional properties after ';'.
181
182
  // https://www.w3.org/TR/baggage/#header-content
182
- const item = `${baggageKey}=${encodeURIComponent(String(value))},`
183
+ const item = `${baggageKey}=${encodeURIComponent(value)},`
183
184
  itemCounter += 1
184
- byteCounter += Buffer.byteLength(item)
185
+ byteCounter += item.length
185
186
 
186
187
  // Check for item count limit exceeded
187
188
  if (itemCounter > this._config.baggageMaxItems) {
@@ -209,7 +210,7 @@ class TextMapPropagator {
209
210
  _injectTags (spanContext, carrier) {
210
211
  const trace = spanContext._trace
211
212
 
212
- if (this._config.tagsHeaderMaxLength === 0) {
213
+ if (this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) {
213
214
  log.debug('Trace tag propagation is disabled, skipping injection.')
214
215
  return
215
216
  }
@@ -228,7 +229,7 @@ class TextMapPropagator {
228
229
 
229
230
  const header = tags.join(',')
230
231
 
231
- if (header.length > this._config.tagsHeaderMaxLength) {
232
+ if (header.length > this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
232
233
  log.error('Trace tags from span are too large, skipping injection.')
233
234
  } else if (header) {
234
235
  carrier[tagsKey] = header
@@ -387,7 +388,7 @@ class TextMapPropagator {
387
388
  if (context === null) {
388
389
  context = extractedContext
389
390
  style = extractor
390
- if (this._config.tracePropagationExtractFirst) {
391
+ if (this._config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) {
391
392
  break
392
393
  }
393
394
  } else {
@@ -436,7 +437,7 @@ class TextMapPropagator {
436
437
  this._extractSamplingPriority(carrier, spanContext)
437
438
  this._extractTags(carrier, spanContext)
438
439
 
439
- if (this._config.tracePropagationExtractFirst) return spanContext
440
+ if (this._config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) return spanContext
440
441
 
441
442
  const tc = this._extractTraceparentContext(carrier)
442
443
 
@@ -706,9 +707,8 @@ class TextMapPropagator {
706
707
  try {
707
708
  value = decodeURIComponent(value)
708
709
  } catch {
709
- tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
710
- removeAllBaggageItems()
711
- return
710
+ const bytes = value.replaceAll(percentByte, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)))
711
+ value = Buffer.from(bytes, 'binary').toString('utf8')
712
712
  }
713
713
 
714
714
  if (spanContext && (tagAllKeys || baggageTagKeys.has(key))) {
@@ -734,9 +734,9 @@ class TextMapPropagator {
734
734
 
735
735
  const trace = spanContext._trace
736
736
 
737
- if (this._config.tagsHeaderMaxLength === 0) {
737
+ if (this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) {
738
738
  log.debug('Trace tag propagation is disabled, skipping extraction.')
739
- } else if (carrier[tagsKey].length > this._config.tagsHeaderMaxLength) {
739
+ } else if (carrier[tagsKey].length > this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
740
740
  log.error('Trace tags from carrier are too large, skipping extraction.')
741
741
  } else {
742
742
  const pairs = carrier[tagsKey].split(',')
@@ -112,7 +112,7 @@ class DatadogSpan {
112
112
  // even `Span` itself in this case.
113
113
  //
114
114
  // TODO: Refactor Tracer/Span + tests to avoid having to do nullish checks.
115
- if (tracer?._config?.spanLeakDebug > 0) {
115
+ if (tracer?._config?.DD_TRACE_SPAN_LEAK_DEBUG > 0) {
116
116
  require('../spanleak').addSpan(this, operationName)
117
117
  }
118
118
 
@@ -163,9 +163,9 @@ module.exports = class PluginManager {
163
163
  dsmEnabled,
164
164
  clientIpEnabled,
165
165
  clientIpHeader,
166
- memcachedCommandEnabled,
167
- ciVisibilityTestSessionName,
168
- ciVisAgentlessLogSubmissionEnabled,
166
+ DD_TRACE_MEMCACHED_COMMAND_ENABLED,
167
+ DD_TEST_SESSION_NAME,
168
+ DD_AGENTLESS_LOG_SUBMISSION_ENABLED,
169
169
  isTestDynamicInstrumentationEnabled,
170
170
  isServiceUserProvided,
171
171
  middlewareTracingEnabled,
@@ -180,13 +180,13 @@ module.exports = class PluginManager {
180
180
  codeOriginForSpans,
181
181
  dbmPropagationMode,
182
182
  dsmEnabled,
183
- memcachedCommandEnabled,
183
+ DD_TRACE_MEMCACHED_COMMAND_ENABLED,
184
184
  site,
185
185
  url,
186
186
  headers: headerTags || [],
187
187
  clientIpHeader,
188
- ciVisibilityTestSessionName,
189
- ciVisAgentlessLogSubmissionEnabled,
188
+ DD_TEST_SESSION_NAME,
189
+ DD_AGENTLESS_LOG_SUBMISSION_ENABLED,
190
190
  isTestDynamicInstrumentationEnabled,
191
191
  isServiceUserProvided,
192
192
  traceWebsocketMessagesEnabled,
@@ -51,7 +51,7 @@ module.exports = class LogPlugin extends Plugin {
51
51
  configure (config) {
52
52
  return super.configure({
53
53
  ...config,
54
- enabled: config.enabled && (config.logInjection || config.ciVisAgentlessLogSubmissionEnabled),
54
+ enabled: config.enabled && (config.logInjection || config.DD_AGENTLESS_LOG_SUBMISSION_ENABLED),
55
55
  })
56
56
  }
57
57
  }
@@ -1055,8 +1055,8 @@ function getIsFaultyEarlyFlakeDetection (projectSuites, testsBySuiteName, faulty
1055
1055
  }
1056
1056
 
1057
1057
  function getTestSessionName (config, trimmedCommand, envTags) {
1058
- if (config.ciVisibilityTestSessionName) {
1059
- return config.ciVisibilityTestSessionName
1058
+ if (config.DD_TEST_SESSION_NAME) {
1059
+ return config.DD_TEST_SESSION_NAME
1060
1060
  }
1061
1061
  const lageTestSessionName = getLageTestSessionName()
1062
1062
  if (lageTestSessionName) {
@@ -128,11 +128,11 @@ class Tracer extends NoopProxy {
128
128
  lazyProxy(this, 'dogstatsd', () => require('./dogstatsd').CustomMetrics, config)
129
129
  }
130
130
 
131
- if (config.spanLeakDebug > 0) {
131
+ if (config.DD_TRACE_SPAN_LEAK_DEBUG > 0) {
132
132
  const spanleak = require('./spanleak')
133
- if (config.spanLeakDebug === spanleak.MODES.LOG) {
133
+ if (config.DD_TRACE_SPAN_LEAK_DEBUG === spanleak.MODES.LOG) {
134
134
  spanleak.enableLogging()
135
- } else if (config.spanLeakDebug === spanleak.MODES.GC_AND_LOG) {
135
+ } else if (config.DD_TRACE_SPAN_LEAK_DEBUG === spanleak.MODES.GC_AND_LOG) {
136
136
  spanleak.enableGarbageCollection()
137
137
  }
138
138
  spanleak.startScrubber()
@@ -203,7 +203,7 @@ class Tracer extends NoopProxy {
203
203
 
204
204
  this._modules.rewriter.enable(config)
205
205
 
206
- if (config.tracing && config.isManualApiEnabled) {
206
+ if (config.tracing && config.DD_CIVISIBILITY_MANUAL_API_ENABLED) {
207
207
  const TestApiManualPlugin = require('./ci-visibility/test-api-manual/test-api-manual-plugin')
208
208
  this._testApiManualPlugin = new TestApiManualPlugin(this)
209
209
  // `shouldGetEnvironmentData` is passed as false so that we only lazily calculate it
@@ -211,7 +211,7 @@ class Tracer extends NoopProxy {
211
211
  // are lazily configured when the library is imported.
212
212
  this._testApiManualPlugin.configure({ ...config, enabled: true }, false)
213
213
  }
214
- if (config.ciVisAgentlessLogSubmissionEnabled) {
214
+ if (config.DD_AGENTLESS_LOG_SUBMISSION_ENABLED) {
215
215
  if (config.apiKey) {
216
216
  const LogSubmissionPlugin = require('./ci-visibility/log-submission/log-submission-plugin')
217
217
  const automaticLogPlugin = new LogSubmissionPlugin(this)
@@ -224,7 +224,7 @@ class Tracer extends NoopProxy {
224
224
  }
225
225
  }
226
226
 
227
- if (config.otelLogsEnabled) {
227
+ if (config.DD_LOGS_OTEL_ENABLED) {
228
228
  const { initializeOpenTelemetryLogs } = require('./opentelemetry/logs')
229
229
  initializeOpenTelemetryLogs(config)
230
230
  }