dd-trace 6.10.0 → 6.12.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 (173) hide show
  1. package/LICENSE-3rdparty.csv +5 -0
  2. package/README.md +0 -17
  3. package/index.d.ts +128 -6
  4. package/initialize.mjs +28 -17
  5. package/openfeature.js +2 -2
  6. package/package.json +27 -23
  7. package/packages/datadog-esbuild/index.js +0 -34
  8. package/packages/datadog-instrumentations/src/anthropic.js +210 -15
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
  10. package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
  11. package/packages/datadog-instrumentations/src/bunyan.js +3 -16
  12. package/packages/datadog-instrumentations/src/cucumber.js +30 -15
  13. package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
  14. package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
  15. package/packages/datadog-instrumentations/src/cypress.js +2 -0
  16. package/packages/datadog-instrumentations/src/fastify.js +2 -0
  17. package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
  18. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
  19. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +9 -4
  20. package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
  21. package/packages/datadog-instrumentations/src/jest.js +25 -17
  22. package/packages/datadog-instrumentations/src/mariadb.js +278 -21
  23. package/packages/datadog-instrumentations/src/mocha/main.js +539 -55
  24. package/packages/datadog-instrumentations/src/mocha/utils.js +135 -19
  25. package/packages/datadog-instrumentations/src/playwright-reporter.js +247 -0
  26. package/packages/datadog-instrumentations/src/playwright.js +182 -35
  27. package/packages/datadog-instrumentations/src/router.js +2 -0
  28. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +6 -8
  29. package/packages/datadog-instrumentations/src/vitest-main.js +11 -3
  30. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
  31. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
  32. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -8
  33. package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
  34. package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
  35. package/packages/datadog-plugin-bunyan/src/index.js +5 -8
  36. package/packages/datadog-plugin-cucumber/src/index.js +11 -0
  37. package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
  38. package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
  39. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +81 -29
  40. package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
  41. package/packages/datadog-plugin-cypress/src/index.js +12 -6
  42. package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
  43. package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
  44. package/packages/datadog-plugin-electron/src/index.js +2 -0
  45. package/packages/datadog-plugin-fs/src/index.js +1 -1
  46. package/packages/datadog-plugin-jest/src/index.js +6 -0
  47. package/packages/datadog-plugin-mocha/src/index.js +56 -1
  48. package/packages/datadog-plugin-nats/src/index.js +1 -1
  49. package/packages/datadog-plugin-openai/src/tracing.js +41 -30
  50. package/packages/datadog-plugin-openai-agents/src/integration.js +1 -0
  51. package/packages/datadog-plugin-playwright/src/index.js +46 -13
  52. package/packages/datadog-plugin-vitest/src/index.js +6 -0
  53. package/packages/datadog-webpack/index.js +0 -11
  54. package/packages/dd-trace/index.js +0 -1
  55. package/packages/dd-trace/src/agent/info.js +6 -5
  56. package/packages/dd-trace/src/aiguard/client.js +100 -1
  57. package/packages/dd-trace/src/aiguard/errors.js +41 -0
  58. package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
  59. package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
  60. package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
  61. package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
  62. package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
  63. package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
  64. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
  65. package/packages/dd-trace/src/aiguard/sdk.js +22 -278
  66. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
  67. package/packages/dd-trace/src/appsec/reporter.js +2 -0
  68. package/packages/dd-trace/src/appsec/telemetry/index.js +13 -0
  69. package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -0
  70. package/packages/dd-trace/src/appsec/telemetry/waf.js +24 -0
  71. package/packages/dd-trace/src/carrier.js +107 -48
  72. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
  73. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
  74. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +29 -3
  75. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +27 -9
  76. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
  77. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
  78. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +26 -8
  79. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
  80. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -4
  81. package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
  82. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
  83. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
  84. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -4
  85. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
  86. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
  87. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
  88. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +4 -2
  89. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +10 -4
  90. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
  91. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
  92. package/packages/dd-trace/src/config/index.js +13 -2
  93. package/packages/dd-trace/src/config/major-overrides.js +7 -0
  94. package/packages/dd-trace/src/config/supported-configurations.json +14 -0
  95. package/packages/dd-trace/src/datastreams/index.js +2 -0
  96. package/packages/dd-trace/src/datastreams/manager.js +9 -2
  97. package/packages/dd-trace/src/datastreams/processor.js +15 -13
  98. package/packages/dd-trace/src/datastreams/size.js +5 -0
  99. package/packages/dd-trace/src/debugger/constants.js +1 -0
  100. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  101. package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -11
  102. package/packages/dd-trace/src/debugger/index.js +4 -2
  103. package/packages/dd-trace/src/debugger/inspect-segment.js +154 -0
  104. package/packages/dd-trace/src/evp_proxy/constants.js +8 -0
  105. package/packages/dd-trace/src/evp_proxy/direct.js +64 -0
  106. package/packages/dd-trace/src/evp_proxy/discovery.js +133 -0
  107. package/packages/dd-trace/src/evp_proxy/path.js +22 -0
  108. package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
  109. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
  110. package/packages/dd-trace/src/exporters/common/request.js +5 -2
  111. package/packages/dd-trace/src/exporters/common/writer.js +11 -6
  112. package/packages/dd-trace/src/llmobs/experiments/client.js +167 -5
  113. package/packages/dd-trace/src/llmobs/experiments/dataset.js +484 -92
  114. package/packages/dd-trace/src/llmobs/experiments/experiment.js +302 -29
  115. package/packages/dd-trace/src/llmobs/experiments/index.js +75 -64
  116. package/packages/dd-trace/src/llmobs/experiments/noop.js +135 -7
  117. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  118. package/packages/dd-trace/src/llmobs/experiments/util.js +101 -9
  119. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
  120. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
  121. package/packages/dd-trace/src/noop/proxy.js +3 -4
  122. package/packages/dd-trace/src/openfeature/constants/constants.js +0 -18
  123. package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
  124. package/packages/dd-trace/src/openfeature/index.js +7 -4
  125. package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
  126. package/packages/dd-trace/src/openfeature/writers/base.js +153 -11
  127. package/packages/dd-trace/src/openfeature/writers/exposures.js +62 -14
  128. package/packages/dd-trace/src/openfeature/writers/util.js +97 -15
  129. package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
  130. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
  131. package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
  132. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
  133. package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
  134. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
  135. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
  136. package/packages/dd-trace/src/opentracing/propagation/text_map.js +22 -13
  137. package/packages/dd-trace/src/opentracing/tracer.js +5 -2
  138. package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
  139. package/packages/dd-trace/src/plugin_manager.js +3 -3
  140. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
  141. package/packages/dd-trace/src/plugins/index.js +1 -0
  142. package/packages/dd-trace/src/plugins/util/jest.js +4 -3
  143. package/packages/dd-trace/src/plugins/util/test.js +9 -9
  144. package/packages/dd-trace/src/plugins/util/web.js +3 -0
  145. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  146. package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
  147. package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
  148. package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
  149. package/packages/dd-trace/src/proxy.js +38 -29
  150. package/packages/dd-trace/src/ritm.js +9 -2
  151. package/packages/dd-trace/src/serverless.js +37 -0
  152. package/packages/dd-trace/src/span_format.js +1 -0
  153. package/packages/dd-trace/src/span_stats.js +30 -3
  154. package/packages/dd-trace/src/storage-channels.js +86 -0
  155. package/packages/dd-trace/src/telemetry/metrics.js +62 -8
  156. package/packages/dd-trace/src/telemetry/send-data.js +1 -0
  157. package/packages/dd-trace/src/tracer.js +9 -2
  158. package/packages/dd-trace/src/tracer_metadata.js +14 -1
  159. package/packages/dd-trace/src/web-tags-cache.js +132 -0
  160. package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
  161. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
  162. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  163. package/vendor/dist/https-proxy-agent/LICENSE +22 -0
  164. package/vendor/dist/https-proxy-agent/index.js +4 -0
  165. package/vendor/dist/protobufjs/index.js +1 -1
  166. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  167. package/vendor/dist/proxy-from-env/LICENSE +20 -0
  168. package/vendor/dist/proxy-from-env/index.js +1 -0
  169. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
  170. package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
  171. package/packages/dd-trace/src/feature-registry.js +0 -29
  172. package/packages/dd-trace/src/openfeature/register.js +0 -35
  173. package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
@@ -10,6 +10,8 @@ const MeterProvider = require('./meter_provider')
10
10
  const PeriodicMetricReader = require('./periodic_metric_reader')
11
11
  const OtlpHttpMetricExporter = require('./otlp_http_metric_exporter')
12
12
 
13
+ const RESERVED_TRACER_TAGS = new Set(['service', 'env', 'version', 'runtime_id', 'runtime-id'])
14
+
13
15
  /**
14
16
  * @typedef {import('../../config')} Config
15
17
  */
@@ -78,7 +80,16 @@ function initializeOpenTelemetryMetrics (config) {
78
80
  metrics.setGlobalMeterProvider(meterProvider)
79
81
  }
80
82
 
81
- function buildResourceAttributes (tags, { reportHostname, otelSemanticsEnabled, service, env, serviceVersion } = {}) {
83
+ /**
84
+ * @param {Record<string, unknown>} tags
85
+ * @param {object} [options]
86
+ * @param {boolean} [options.reportHostname]
87
+ * @param {string} [options.service]
88
+ * @param {string} [options.env]
89
+ * @param {string} [options.serviceVersion]
90
+ * @returns {import('@opentelemetry/api').Attributes}
91
+ */
92
+ function buildResourceAttributes (tags, { reportHostname, service, env, serviceVersion } = {}) {
82
93
  const attrs = {
83
94
  'telemetry.sdk.name': 'datadog',
84
95
  'telemetry.sdk.language': 'nodejs',
@@ -89,14 +100,19 @@ function buildResourceAttributes (tags, { reportHostname, otelSemanticsEnabled,
89
100
  if (env) attrs['deployment.environment.name'] = env
90
101
  if (reportHostname) attrs['host.name'] = os.hostname()
91
102
 
92
- if (!otelSemanticsEnabled) {
93
- if (tags?.['runtime-id']) attrs['datadog.runtime_id'] = tags['runtime-id']
94
- const processTagsObject = processTags.tagsObject
95
- if (processTagsObject) {
96
- for (const key of Object.keys(processTagsObject)) {
97
- attrs[`datadog.${key}`] = processTagsObject[key]
98
- }
99
- }
103
+ if (tags['runtime-id']) attrs['datadog.runtime_id'] = tags['runtime-id']
104
+ const tracerTags = []
105
+ for (const [key, value] of Object.entries(tags)) {
106
+ const valueType = typeof value
107
+ const supported = valueType === 'string' || valueType === 'boolean' ||
108
+ (valueType === 'number' && Number.isFinite(value))
109
+ if (!RESERVED_TRACER_TAGS.has(key) && supported) tracerTags.push(`${key}:${value}`)
110
+ }
111
+ if (tracerTags.length) attrs['datadog.tracer_tags'] = tracerTags
112
+ // Mirrors the legacy v0.6/stats ProcessTags shape (buildProcessTags().tagsArray); keep both in sync.
113
+ const processTagsArray = processTags.tagsArray
114
+ if (processTagsArray.length) {
115
+ attrs['datadog.process_tags'] = processTagsArray
100
116
  }
101
117
  return attrs
102
118
  }
@@ -106,7 +122,6 @@ function createOtlpSpanStatsExporter (config) {
106
122
  const protocol = config.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL || 'http/json'
107
123
  const resourceAttributes = buildResourceAttributes(config.tags, {
108
124
  reportHostname: config.reportHostname,
109
- otelSemanticsEnabled: config.DD_TRACE_OTEL_SEMANTICS_ENABLED,
110
125
  service: config.service,
111
126
  env: config.env,
112
127
  serviceVersion: config.version,
@@ -115,8 +130,6 @@ function createOtlpSpanStatsExporter (config) {
115
130
  config.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
116
131
  protocol,
117
132
  resourceAttributes,
118
- config.DD_TRACE_OTEL_SEMANTICS_ENABLED,
119
- config.service,
120
133
  config.OTEL_EXPORTER_OTLP_METRICS_HEADERS,
121
134
  config.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT
122
135
  )
@@ -11,15 +11,12 @@ class OtlpStatsExporter extends OtlpHttpExporterBase {
11
11
  * @param {string} url
12
12
  * @param {string} protocol
13
13
  * @param {import('@opentelemetry/api').Attributes} resourceAttributes
14
- * @param {boolean} [otelSemanticsEnabled]
15
- * @param {string} [defaultService]
16
14
  * @param {Record<string, string>} [headers]
17
15
  * @param {number} [timeout]
18
16
  */
19
- constructor (url, protocol, resourceAttributes, otelSemanticsEnabled = false, defaultService = '',
20
- headers, timeout = 10_000) {
17
+ constructor (url, protocol, resourceAttributes, headers, timeout = 10_000) {
21
18
  super(url, headers, timeout, protocol, 'span-stats')
22
- this.#transformer = new OtlpStatsTransformer(resourceAttributes, protocol, otelSemanticsEnabled, defaultService)
19
+ this.#transformer = new OtlpStatsTransformer(resourceAttributes, protocol)
23
20
  }
24
21
 
25
22
  /**
@@ -1,10 +1,11 @@
1
1
  'use strict'
2
2
 
3
- const { LogCollapsingLowestDenseDDSketch } = require('../../../../../vendor/dist/@datadog/sketches-js')
4
3
  const OtlpTransformerBase = require('../otlp/otlp_transformer_base')
5
4
  const { getProtobufTypes } = require('../otlp/protobuf_loader')
6
5
  const { GRPC_STATUS_NAMES } = require('../../constants')
7
6
 
7
+ const { stableStringify } = OtlpTransformerBase
8
+
8
9
  const NS_PER_S = 1e9
9
10
 
10
11
  // Must match libdatadog's EXPLICIT_BOUNDS_SECONDS and OTel spanmetrics connector defaults.
@@ -12,6 +13,19 @@ const EXPLICIT_BOUNDS_SECONDS = [
12
13
  0.002, 0.004, 0.006, 0.008, 0.01, 0.05, 0.1, 0.2, 0.4, 0.8, 1, 1.4, 2, 5, 10, 15,
13
14
  ]
14
15
 
16
+ const SPAN_KIND_METRIC_MAP = {
17
+ internal: 'SPAN_KIND_INTERNAL',
18
+ SPAN_KIND_INTERNAL: 'SPAN_KIND_INTERNAL',
19
+ server: 'SPAN_KIND_SERVER',
20
+ SPAN_KIND_SERVER: 'SPAN_KIND_SERVER',
21
+ client: 'SPAN_KIND_CLIENT',
22
+ SPAN_KIND_CLIENT: 'SPAN_KIND_CLIENT',
23
+ producer: 'SPAN_KIND_PRODUCER',
24
+ SPAN_KIND_PRODUCER: 'SPAN_KIND_PRODUCER',
25
+ consumer: 'SPAN_KIND_CONSUMER',
26
+ SPAN_KIND_CONSUMER: 'SPAN_KIND_CONSUMER',
27
+ }
28
+
15
29
  /**
16
30
  * @param {object} sketch
17
31
  * @returns {number[]}
@@ -41,22 +55,16 @@ function getDeltaTemporality () {
41
55
  return _deltaTemporality
42
56
  }
43
57
 
44
- const ERROR_STATUS_ATTR = { key: 'status.code', value: { intValue: 2 } }
58
+ const STATUS_CODE_OK = 'STATUS_CODE_OK'
59
+ const STATUS_CODE_ERROR = 'STATUS_CODE_ERROR'
45
60
 
46
61
  class OtlpStatsTransformer extends OtlpTransformerBase {
47
- #otelSemanticsEnabled
48
- #defaultService
49
-
50
62
  /**
51
63
  * @param {import('@opentelemetry/api').Attributes} resourceAttributes
52
64
  * @param {string} protocol
53
- * @param {boolean} [otelSemanticsEnabled]
54
- * @param {string} [defaultService]
55
65
  */
56
- constructor (resourceAttributes, protocol, otelSemanticsEnabled = false, defaultService = '') {
66
+ constructor (resourceAttributes, protocol) {
57
67
  super(resourceAttributes, protocol, 'span-stats')
58
- this.#otelSemanticsEnabled = otelSemanticsEnabled
59
- this.#defaultService = defaultService
60
68
  }
61
69
 
62
70
  /**
@@ -82,32 +90,35 @@ class OtlpStatsTransformer extends OtlpTransformerBase {
82
90
  const dataPoints = []
83
91
 
84
92
  for (const { timeNs, bucket } of drained) {
93
+ const distributions = new Map()
85
94
  const endTimeNs = timeNs + bucketSizeNs
86
95
  const startNano = isJson ? String(timeNs) : timeNs
87
96
  const endNano = isJson ? String(endTimeNs) : endTimeNs
88
97
 
89
98
  for (const aggStats of bucket.values()) {
90
- const baseAttrs = this.#buildAttributes(aggStats.aggKey)
91
-
92
- if (this.#otelSemanticsEnabled) {
93
- const okDist = new LogCollapsingLowestDenseDDSketch()
94
- okDist.merge(aggStats.topLevelOkDistribution)
95
- okDist.merge(aggStats.nonTopLevelOkDistribution)
96
- const errDist = new LogCollapsingLowestDenseDDSketch()
97
- errDist.merge(aggStats.topLevelErrorDistribution)
98
- errDist.merge(aggStats.nonTopLevelErrorDistribution)
99
- this.#pushPoint(dataPoints, okDist, startNano, endNano, baseAttrs)
100
- this.#pushPoint(dataPoints, errDist, startNano, endNano, [...baseAttrs, ERROR_STATUS_ATTR])
101
- } else {
102
- const tlAttrs = [...baseAttrs, { key: 'datadog.span.top_level', value: { boolValue: true } }]
103
- const ntlAttrs = [...baseAttrs, { key: 'datadog.span.top_level', value: { boolValue: false } }]
104
- this.#pushPoint(dataPoints, aggStats.topLevelOkDistribution, startNano, endNano, tlAttrs)
105
- this.#pushPoint(dataPoints, aggStats.topLevelErrorDistribution, startNano, endNano,
106
- [...tlAttrs, ERROR_STATUS_ATTR])
107
- this.#pushPoint(dataPoints, aggStats.nonTopLevelOkDistribution, startNano, endNano, ntlAttrs)
108
- this.#pushPoint(dataPoints, aggStats.nonTopLevelErrorDistribution, startNano, endNano,
109
- [...ntlAttrs, ERROR_STATUS_ATTR])
110
- }
99
+ const baseAttributes = this.#buildAttributes(aggStats.aggKey)
100
+ const baseKey = stableStringify(baseAttributes)
101
+
102
+ this.#addDistribution(
103
+ distributions, aggStats.topLevelOkDistribution, startNano, endNano,
104
+ baseAttributes, baseKey, true, STATUS_CODE_OK
105
+ )
106
+ this.#addDistribution(
107
+ distributions, aggStats.topLevelErrorDistribution, startNano, endNano,
108
+ baseAttributes, baseKey, true, STATUS_CODE_ERROR
109
+ )
110
+ this.#addDistribution(
111
+ distributions, aggStats.nonTopLevelOkDistribution, startNano, endNano,
112
+ baseAttributes, baseKey, false, STATUS_CODE_OK
113
+ )
114
+ this.#addDistribution(
115
+ distributions, aggStats.nonTopLevelErrorDistribution, startNano, endNano,
116
+ baseAttributes, baseKey, false, STATUS_CODE_ERROR
117
+ )
118
+ }
119
+
120
+ for (const { sketch, startNano, endNano, attributes } of distributions.values()) {
121
+ this.#pushPoint(dataPoints, sketch, startNano, endNano, attributes)
111
122
  }
112
123
  }
113
124
 
@@ -123,8 +134,45 @@ class OtlpStatsTransformer extends OtlpTransformerBase {
123
134
  }]
124
135
  }
125
136
 
126
- #pushPoint (points, sketch, startNano, endNano, attributes) {
137
+ /**
138
+ * @param {Map<string, {
139
+ * sketch: object,
140
+ * startNano: string | number,
141
+ * endNano: string | number,
142
+ * attributes: object[]
143
+ * }>} distributions
144
+ * @param {object} sketch
145
+ * @param {string | number} startNano
146
+ * @param {string | number} endNano
147
+ * @param {import('@opentelemetry/api').Attributes} baseAttributes
148
+ * @param {string} baseKey
149
+ * @param {boolean} topLevel
150
+ * @param {string} statusCode
151
+ * @returns {void}
152
+ */
153
+ #addDistribution (distributions, sketch, startNano, endNano, baseAttributes, baseKey, topLevel, statusCode) {
127
154
  if (!sketch || sketch.count === 0) return
155
+
156
+ const key = `${baseKey},${topLevel},${statusCode}`
157
+ const existing = distributions.get(key)
158
+ if (existing) {
159
+ existing.sketch.merge(sketch)
160
+ } else {
161
+ const attributes = this.transformAttributes(baseAttributes)
162
+ attributes.push(
163
+ { key: 'datadog.span.top_level', value: { boolValue: topLevel } },
164
+ { key: 'status.code', value: { stringValue: statusCode } }
165
+ )
166
+ distributions.set(key, {
167
+ sketch,
168
+ startNano,
169
+ endNano,
170
+ attributes,
171
+ })
172
+ }
173
+ }
174
+
175
+ #pushPoint (points, sketch, startNano, endNano, attributes) {
128
176
  points.push({
129
177
  attributes,
130
178
  startTimeUnixNano: startNano,
@@ -140,15 +188,18 @@ class OtlpStatsTransformer extends OtlpTransformerBase {
140
188
 
141
189
  /**
142
190
  * @param {import('../../span_stats').SpanAggKey} aggKey
191
+ * @returns {import('@opentelemetry/api').Attributes}
143
192
  */
144
193
  #buildAttributes (aggKey) {
145
- const raw = { 'span.name': aggKey.resource }
146
-
147
- if (aggKey.service && aggKey.service !== this.#defaultService) {
148
- raw['service.name'] = aggKey.service
194
+ const spanKind = Object.hasOwn(SPAN_KIND_METRIC_MAP, aggKey.spanKind)
195
+ ? SPAN_KIND_METRIC_MAP[aggKey.spanKind]
196
+ : 'SPAN_KIND_INTERNAL'
197
+ const raw = {
198
+ 'span.name': aggKey.resource,
199
+ 'service.name': aggKey.service,
200
+ 'span.kind': spanKind,
149
201
  }
150
202
 
151
- if (aggKey.spanKind) raw['span.kind'] = aggKey.spanKind
152
203
  if (aggKey.statusCode) raw['http.response.status_code'] = Number(aggKey.statusCode)
153
204
  if (aggKey.method) raw['http.request.method'] = aggKey.method
154
205
  if (aggKey.endpoint) raw['http.route'] = aggKey.endpoint
@@ -159,13 +210,15 @@ class OtlpStatsTransformer extends OtlpTransformerBase {
159
210
  : String(aggKey.rpcStatusCode).toUpperCase()
160
211
  }
161
212
 
162
- if (!this.#otelSemanticsEnabled) {
163
- raw['datadog.operation.name'] = aggKey.name
164
- if (aggKey.type) raw['datadog.span.type'] = aggKey.type
165
- if (aggKey.synthetics) raw['datadog.origin'] = 'synthetics'
166
- }
213
+ // TODO: additional_metric_tags support is still evolving/TBD across most SDKs; not implemented here yet.
214
+
215
+ raw['datadog.operation.name'] = aggKey.name
216
+ if (aggKey.type) raw['datadog.span.type'] = aggKey.type
217
+ if (aggKey.synthetics) raw['datadog.origin'] = 'synthetics'
218
+ if (aggKey.srvSrc) raw['datadog.svc_src'] = aggKey.srvSrc
219
+ if (typeof aggKey.isTraceRoot === 'boolean') raw['datadog.is_trace_root'] = aggKey.isTraceRoot
167
220
 
168
- return this.transformAttributes(raw)
221
+ return raw
169
222
  }
170
223
  }
171
224
 
@@ -543,7 +543,8 @@ class TextMapPropagator {
543
543
  this._extractOrigin(carrier, spanContext)
544
544
  this._extractLegacyBaggageItems(carrier, spanContext)
545
545
  this._extractSamplingPriority(carrier, spanContext)
546
- this._extractTags(carrier, spanContext)
546
+ const traceTags = this._extractTags(carrier)
547
+ if (traceTags) spanContext._trace.tags = traceTags
547
548
 
548
549
  if (this._config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) return spanContext
549
550
 
@@ -865,37 +866,45 @@ class TextMapPropagator {
865
866
  }
866
867
  }
867
868
 
868
- _extractTags (carrier, spanContext) {
869
+ /**
870
+ * @param {Record<string, unknown>} carrier
871
+ * @returns {Record<string, string> | undefined}
872
+ */
873
+ _extractTags (carrier) {
869
874
  const header = readDatadogTags(carrier)
870
875
  if (!header) return
871
876
 
872
- const trace = spanContext._trace
873
-
874
877
  if (this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) {
875
878
  log.debug('Trace tag propagation is disabled, skipping extraction.')
876
879
  } else if (header.length > this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
877
880
  log.error('Trace tags from carrier are too large, skipping extraction.')
878
881
  } else {
879
- const pairs = header.split(',')
880
882
  const tags = {}
883
+ let start = 0
884
+ let separator = header.indexOf('=')
881
885
 
882
- for (const pair of pairs) {
883
- const [key, ...rest] = pair.split('=')
884
- const value = rest.join('=')
886
+ while (start <= header.length) {
887
+ const comma = header.indexOf(',', start)
888
+ const end = comma === -1 ? header.length : comma
889
+ const hasSeparator = separator !== -1 && separator < end
890
+ const key = header.slice(start, hasSeparator ? separator : end)
891
+ const value = hasSeparator ? header.slice(separator + 1, end) : ''
885
892
 
886
893
  if (!this._validateTagKey(key) || !this._validateTagValue(value)) {
887
894
  log.error('Trace tags from carrier are invalid, skipping extraction.')
888
895
  return
889
896
  }
890
- // Check if value is a valid 16 character lower-case hexadecimal encoded number as per spec
891
- if (key === '_dd.p.tid' && !(hex16.test(value))) {
897
+ if (key === '_dd.p.tid' && !hex16.test(value)) {
892
898
  log.error('Invalid _dd.p.tid tag %s, skipping', value)
893
- continue
899
+ } else {
900
+ tags[key] = value
894
901
  }
895
- tags[key] = value
902
+
903
+ start = end + 1
904
+ if (hasSeparator) separator = header.indexOf('=', start)
896
905
  }
897
906
 
898
- Object.assign(trace.tags, tags)
907
+ return tags
899
908
  }
900
909
  }
901
910
 
@@ -36,8 +36,11 @@ class DatadogTracer {
36
36
  // (test_session/test_module/ test_suite/test) belong on the citestcycle
37
37
  // endpoint, not on an OTLP traces endpoint — otherwise users with OTEL_*
38
38
  // vars set in their environment (e.g. for a separate telemetry integration)
39
- // silently lose all test spans.
40
- if (config.OTEL_TRACES_EXPORTER === 'otlp' && !config.isCiVisibility) {
39
+ // silently lose all test spans. The same applies to the Electron exporter:
40
+ // spans must reach the Electron SDK's IPC bridge, not an OTLP endpoint,
41
+ // even when OTEL_* vars are set for unrelated telemetry.
42
+ if (config.OTEL_TRACES_EXPORTER === 'otlp' && !config.isCiVisibility &&
43
+ config.experimental.exporter !== 'electron') {
41
44
  const { createOtlpTraceExporter } = require('../opentelemetry/trace')
42
45
  this._exporter = createOtlpTraceExporter(config)
43
46
  } else {