dd-trace 5.109.0 → 5.111.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 (208) hide show
  1. package/README.md +13 -8
  2. package/ci/init.js +25 -5
  3. package/ci/vitest-no-worker-init-setup.mjs +430 -0
  4. package/ext/tags.js +2 -0
  5. package/index.d.ts +79 -24
  6. package/initialize.mjs +5 -6
  7. package/loader-hook.mjs +154 -47
  8. package/package.json +20 -16
  9. package/packages/datadog-esbuild/index.js +26 -0
  10. package/packages/datadog-esbuild/src/utils.js +46 -3
  11. package/packages/datadog-instrumentations/src/connect.js +4 -3
  12. package/packages/datadog-instrumentations/src/fastify.js +4 -12
  13. package/packages/datadog-instrumentations/src/fs.js +8 -6
  14. package/packages/datadog-instrumentations/src/graphql.js +26 -484
  15. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
  17. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
  18. package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
  19. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
  20. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
  23. package/packages/datadog-instrumentations/src/koa.js +3 -2
  24. package/packages/datadog-instrumentations/src/mariadb.js +4 -2
  25. package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
  26. package/packages/datadog-instrumentations/src/mongoose.js +14 -2
  27. package/packages/datadog-instrumentations/src/next.js +10 -10
  28. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
  29. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  30. package/packages/datadog-instrumentations/src/restify.js +4 -3
  31. package/packages/datadog-instrumentations/src/router.js +18 -5
  32. package/packages/datadog-instrumentations/src/selenium.js +1 -2
  33. package/packages/datadog-instrumentations/src/tedious.js +28 -0
  34. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
  35. package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
  36. package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
  37. package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
  38. package/packages/datadog-instrumentations/src/vitest.js +11 -1598
  39. package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
  40. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
  41. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
  43. package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
  44. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
  45. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
  46. package/packages/datadog-plugin-cucumber/src/index.js +1 -1
  47. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -5
  48. package/packages/datadog-plugin-cypress/src/support.js +45 -1
  49. package/packages/datadog-plugin-express/src/code_origin.js +1 -1
  50. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
  51. package/packages/datadog-plugin-graphql/src/execute.js +627 -11
  52. package/packages/datadog-plugin-graphql/src/index.js +20 -8
  53. package/packages/datadog-plugin-graphql/src/parse.js +24 -4
  54. package/packages/datadog-plugin-graphql/src/validate.js +16 -5
  55. package/packages/datadog-plugin-http/src/client.js +8 -3
  56. package/packages/datadog-plugin-http/src/index.js +6 -8
  57. package/packages/datadog-plugin-http2/src/client.js +5 -2
  58. package/packages/datadog-plugin-jest/src/index.js +1 -1
  59. package/packages/datadog-plugin-mocha/src/index.js +1 -1
  60. package/packages/datadog-plugin-mysql/src/index.js +7 -2
  61. package/packages/datadog-plugin-openai/src/services.js +2 -2
  62. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  63. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
  64. package/packages/datadog-plugin-undici/src/index.js +5 -2
  65. package/packages/datadog-plugin-vitest/src/index.js +76 -64
  66. package/packages/datadog-shimmer/src/shimmer.js +37 -16
  67. package/packages/datadog-webpack/index.js +17 -1
  68. package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
  69. package/packages/dd-trace/src/aiguard/sdk.js +15 -3
  70. package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
  71. package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
  72. package/packages/dd-trace/src/appsec/channels.js +3 -1
  73. package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
  74. package/packages/dd-trace/src/appsec/graphql.js +5 -5
  75. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
  76. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  77. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
  78. package/packages/dd-trace/src/appsec/index.js +20 -7
  79. package/packages/dd-trace/src/appsec/lambda.js +135 -0
  80. package/packages/dd-trace/src/appsec/reporter.js +50 -10
  81. package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
  82. package/packages/dd-trace/src/appsec/telemetry/index.js +27 -1
  83. package/packages/dd-trace/src/appsec/waf/index.js +16 -4
  84. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  85. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
  86. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
  87. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  88. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  89. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
  90. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
  91. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
  92. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
  93. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
  94. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
  95. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
  96. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
  97. package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
  98. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
  99. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
  100. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
  101. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
  102. package/packages/dd-trace/src/config/defaults.js +25 -5
  103. package/packages/dd-trace/src/config/generated-config-types.d.ts +596 -58
  104. package/packages/dd-trace/src/config/helper.js +134 -33
  105. package/packages/dd-trace/src/config/index.js +39 -23
  106. package/packages/dd-trace/src/config/major-overrides.js +4 -2
  107. package/packages/dd-trace/src/config/parsers.js +19 -1
  108. package/packages/dd-trace/src/config/remote_config.js +1 -1
  109. package/packages/dd-trace/src/config/supported-configurations.json +109 -57
  110. package/packages/dd-trace/src/constants.js +7 -0
  111. package/packages/dd-trace/src/datastreams/processor.js +11 -3
  112. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
  113. package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
  114. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
  115. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
  116. package/packages/dd-trace/src/debugger/index.js +5 -0
  117. package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
  118. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
  119. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
  120. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  121. package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
  122. package/packages/dd-trace/src/exporters/common/request.js +1 -20
  123. package/packages/dd-trace/src/exporters/common/url.js +34 -0
  124. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
  125. package/packages/dd-trace/src/guardrails/index.js +8 -5
  126. package/packages/dd-trace/src/index.js +6 -4
  127. package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
  128. package/packages/dd-trace/src/llmobs/index.js +17 -6
  129. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
  130. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
  131. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
  132. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  133. package/packages/dd-trace/src/llmobs/sdk.js +8 -6
  134. package/packages/dd-trace/src/llmobs/span_processor.js +5 -1
  135. package/packages/dd-trace/src/llmobs/tagger.js +58 -3
  136. package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
  137. package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
  138. package/packages/dd-trace/src/log/index.js +18 -20
  139. package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
  140. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
  141. package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
  142. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
  143. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
  144. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
  145. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
  146. package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
  147. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
  148. package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
  149. package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
  150. package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
  151. package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
  152. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +44 -38
  153. package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
  154. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  155. package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
  156. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  157. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
  158. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
  159. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  160. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
  161. package/packages/dd-trace/src/opentracing/span_context.js +17 -0
  162. package/packages/dd-trace/src/opentracing/tracer.js +6 -1
  163. package/packages/dd-trace/src/plugin_manager.js +14 -8
  164. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -4
  165. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
  166. package/packages/dd-trace/src/plugins/util/llm.js +6 -1
  167. package/packages/dd-trace/src/plugins/util/test.js +91 -9
  168. package/packages/dd-trace/src/plugins/util/url.js +68 -1
  169. package/packages/dd-trace/src/plugins/util/web.js +19 -36
  170. package/packages/dd-trace/src/priority_sampler.js +1 -13
  171. package/packages/dd-trace/src/profiling/config.js +129 -198
  172. package/packages/dd-trace/src/profiling/constants.js +4 -7
  173. package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
  174. package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
  175. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
  176. package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
  177. package/packages/dd-trace/src/profiling/oom.js +71 -0
  178. package/packages/dd-trace/src/profiling/profiler.js +35 -23
  179. package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
  180. package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
  181. package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
  182. package/packages/dd-trace/src/proxy.js +8 -8
  183. package/packages/dd-trace/src/ritm.js +4 -2
  184. package/packages/dd-trace/src/serverless.js +1 -2
  185. package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
  186. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
  187. package/packages/dd-trace/src/span_format.js +2 -1
  188. package/packages/dd-trace/src/span_processor.js +11 -6
  189. package/packages/dd-trace/src/span_stats.js +96 -78
  190. package/packages/dd-trace/src/startup-log.js +2 -1
  191. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  192. package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
  193. package/packages/dd-trace/src/telemetry/index.js +2 -2
  194. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  195. package/packages/dd-trace/src/telemetry/send-data.js +20 -18
  196. package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
  197. package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
  198. package/packages/dd-trace/src/tracer.js +15 -1
  199. package/packages/dd-trace/src/util.js +14 -0
  200. package/register.js +58 -1
  201. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  202. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  203. package/vendor/dist/pprof-format/index.js +1 -1
  204. package/vendor/dist/protobufjs/index.js +1 -1
  205. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  206. package/vendor/dist/shell-quote/index.js +1 -1
  207. package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
  208. package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
  const { version: ddTraceVersion } = require('../../../../package.json')
3
- const { ITR_CORRELATION_ID } = require('../../src/plugins/util/test')
3
+ const { ITR_CORRELATION_ID, TEST_LEVELS_METADATA } = require('../../src/plugins/util/test')
4
4
  const id = require('../../src/id')
5
5
  const {
6
6
  distributionMetric,
@@ -9,10 +9,15 @@ const {
9
9
  } = require('../ci-visibility/telemetry')
10
10
  const { MsgpackChunk } = require('../msgpack')
11
11
  const { AgentEncoder } = require('./0.4')
12
- const { truncateSpanTestOpt, normalizeSpan } = require('./tags-processors')
12
+ const {
13
+ truncateSpanTestOpt,
14
+ normalizeSpan,
15
+ MAX_META_VALUE_LENGTH_TEST_OPTIMIZATION,
16
+ } = require('./tags-processors')
13
17
 
14
18
  const ENCODING_VERSION = 1
15
19
  const ALLOWED_CONTENT_TYPES = new Set(['test_session_end', 'test_module_end', 'test_suite_end', 'test'])
20
+ const ALLOWED_METADATA_TARGETS = new Set([...ALLOWED_CONTENT_TYPES, TEST_LEVELS_METADATA])
16
21
 
17
22
  const TEST_SUITE_KEYS_LENGTH = 12
18
23
  const TEST_MODULE_KEYS_LENGTH = 11
@@ -36,6 +41,33 @@ function formatSpan (span) {
36
41
  }
37
42
  }
38
43
 
44
+ function isAllowedMetadataTarget (target) {
45
+ return ALLOWED_METADATA_TARGETS.has(target)
46
+ }
47
+
48
+ function isEncodableMetadataValue (value) {
49
+ return typeof value === 'string' || typeof value === 'number'
50
+ }
51
+
52
+ function truncateTestLevelMetadataValue (value) {
53
+ if (typeof value !== 'string' || value.length <= MAX_META_VALUE_LENGTH_TEST_OPTIMIZATION) {
54
+ return value
55
+ }
56
+ return `${value.slice(0, MAX_META_VALUE_LENGTH_TEST_OPTIMIZATION)}...`
57
+ }
58
+
59
+ function truncateTestLevelMetadataTags (tags) {
60
+ const truncatedTags = {}
61
+ let hasTags = false
62
+ for (const key of Object.keys(tags)) {
63
+ const value = truncateTestLevelMetadataValue(tags[key])
64
+ if (!isEncodableMetadataValue(value)) continue
65
+ truncatedTags[key] = value
66
+ hasTags = true
67
+ }
68
+ return hasTags ? truncatedTags : undefined
69
+ }
70
+
39
71
  class AgentlessCiVisibilityEncoder extends AgentEncoder {
40
72
  constructor (writer, { runtimeId, service, env }) {
41
73
  super(writer, INTAKE_SOFT_LIMIT)
@@ -49,6 +81,7 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
49
81
 
50
82
  this.metadataTags = {}
51
83
  this.wildcardMetadataTags = {}
84
+ this.testLevelsMetadataKeys = []
52
85
 
53
86
  this.reset()
54
87
  }
@@ -60,12 +93,39 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
60
93
  ...tags['*'],
61
94
  }
62
95
  }
63
- for (const type of ALLOWED_CONTENT_TYPES) {
64
- if (tags[type]) {
65
- this.metadataTags[type] = {
66
- ...this.metadataTags[type],
67
- ...tags[type],
68
- }
96
+
97
+ for (const target of Object.keys(tags)) {
98
+ if (target === '*' || !tags[target] || !isAllowedMetadataTarget(target)) continue
99
+
100
+ const targetTags = target === TEST_LEVELS_METADATA
101
+ ? truncateTestLevelMetadataTags(tags[target])
102
+ : tags[target]
103
+ if (!targetTags) continue
104
+
105
+ this.metadataTags[target] = {
106
+ ...this.metadataTags[target],
107
+ ...targetTags,
108
+ }
109
+
110
+ if (target === TEST_LEVELS_METADATA) {
111
+ this.testLevelsMetadataKeys = Object.keys(this.metadataTags[target])
112
+ }
113
+ }
114
+ }
115
+
116
+ _removeDuplicateTestLevelsMetadata (event) {
117
+ if (!ALLOWED_CONTENT_TYPES.has(event.type)) return
118
+
119
+ const meta = event.content.meta
120
+ if (!meta) return
121
+
122
+ const testLevelsMetadataTags = this.metadataTags[TEST_LEVELS_METADATA]
123
+ const testLevelsMetadataKeys = this.testLevelsMetadataKeys
124
+
125
+ for (let i = 0; i < testLevelsMetadataKeys.length; i++) {
126
+ const key = testLevelsMetadataKeys[i]
127
+ if (meta[key] === testLevelsMetadataTags[key]) {
128
+ delete meta[key]
69
129
  }
70
130
  }
71
131
  }
@@ -276,7 +336,11 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
276
336
 
277
337
  this._eventCount += events.length
278
338
 
339
+ const hasTestLevelsMetadata = this.testLevelsMetadataKeys.length !== 0
279
340
  for (const event of events) {
341
+ if (hasTestLevelsMetadata) {
342
+ this._removeDuplicateTestLevelsMetadata(event)
343
+ }
280
344
  this._encodeEvent(bytes, event)
281
345
  }
282
346
  distributionMetric(
@@ -343,24 +407,11 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
343
407
  this._encodeString(bytes, 'version')
344
408
  bytes.writeNumber(payload.version)
345
409
  this._encodeString(bytes, 'metadata')
346
- bytes.writeMapPrefix(Object.keys(payload.metadata).length)
347
- this._encodeString(bytes, '*')
348
- this._encodeMap(bytes, payload.metadata['*'])
349
- if (payload.metadata.test) {
350
- this._encodeString(bytes, 'test')
351
- this._encodeMap(bytes, payload.metadata.test)
352
- }
353
- if (payload.metadata.test_suite_end) {
354
- this._encodeString(bytes, 'test_suite_end')
355
- this._encodeMap(bytes, payload.metadata.test_suite_end)
356
- }
357
- if (payload.metadata.test_module_end) {
358
- this._encodeString(bytes, 'test_module_end')
359
- this._encodeMap(bytes, payload.metadata.test_module_end)
360
- }
361
- if (payload.metadata.test_session_end) {
362
- this._encodeString(bytes, 'test_session_end')
363
- this._encodeMap(bytes, payload.metadata.test_session_end)
410
+ const metadataKeys = Object.keys(payload.metadata)
411
+ bytes.writeMapPrefix(metadataKeys.length)
412
+ for (const metadataKey of metadataKeys) {
413
+ this._encodeString(bytes, metadataKey)
414
+ this._encodeMap(bytes, payload.metadata[metadataKey])
364
415
  }
365
416
  this._encodeString(bytes, 'events')
366
417
  this._eventsOffset = bytes.length
@@ -13,7 +13,7 @@ class AgentExporter {
13
13
  this._url = config.url
14
14
 
15
15
  const headers = {}
16
- if (stats.enabled || apmTracingEnabled === false) {
16
+ if (stats.DD_TRACE_STATS_COMPUTATION_ENABLED || apmTracingEnabled === false) {
17
17
  headers['Datadog-Client-Computed-Stats'] = 'yes'
18
18
  }
19
19
 
@@ -40,7 +40,7 @@ class AgentlessWriter extends BaseWriter {
40
40
  }
41
41
  }
42
42
 
43
- if (!getConfig().apiKey) {
43
+ if (!getConfig().DD_API_KEY) {
44
44
  this.#apiKeyMissing = true
45
45
  log.error('DD_API_KEY is required for agentless trace intake. Set DD_API_KEY. Traces will not be sent.')
46
46
  }
@@ -109,8 +109,8 @@ class AgentlessWriter extends BaseWriter {
109
109
  return
110
110
  }
111
111
 
112
- const apiKey = getConfig().apiKey
113
- if (!apiKey) {
112
+ const { DD_API_KEY } = getConfig()
113
+ if (!DD_API_KEY) {
114
114
  if (!this.#apiKeyMissing) {
115
115
  this.#apiKeyMissing = true
116
116
  log.error('DD_API_KEY is required for agentless trace intake. Set DD_API_KEY. Traces will not be sent.')
@@ -126,7 +126,7 @@ class AgentlessWriter extends BaseWriter {
126
126
  method: 'POST',
127
127
  headers: {
128
128
  'Content-Type': 'application/json',
129
- 'dd-api-key': apiKey,
129
+ 'dd-api-key': DD_API_KEY,
130
130
  'X-Datadog-Trace-Count': String(count),
131
131
  'Datadog-Meta-Lang': 'nodejs',
132
132
  'Datadog-Meta-Lang-Version': process.version,
@@ -11,7 +11,7 @@ const zlib = require('zlib')
11
11
 
12
12
  const { storage } = require('../../../../datadog-core')
13
13
  const log = require('../../log')
14
- const { urlToHttpOptions } = require('./url-to-http-options-polyfill')
14
+ const { parseUrl } = require('./url')
15
15
  const docker = require('./docker')
16
16
  const { httpAgent, httpsAgent } = require('./agents')
17
17
  const {
@@ -27,25 +27,6 @@ const maxActiveBufferSize = 1024 * 1024 * 64
27
27
 
28
28
  let activeBufferSize = 0
29
29
 
30
- /**
31
- * @param {string|URL|object} urlObjOrString
32
- * @returns {object}
33
- */
34
- function parseUrl (urlObjOrString) {
35
- if (urlObjOrString !== null && typeof urlObjOrString === 'object') return urlToHttpOptions(urlObjOrString)
36
-
37
- const url = urlToHttpOptions(new URL(urlObjOrString))
38
-
39
- // Special handling if we're using named pipes on Windows
40
- if (url.protocol === 'unix:' && url.hostname === '.') {
41
- const udsPath = urlObjOrString.slice(5)
42
- url.path = udsPath
43
- url.pathname = udsPath
44
- }
45
-
46
- return url
47
- }
48
-
49
30
  /**
50
31
  * @param {string} hostname Host as resolved by {@link parseUrl}; IPv6 is unbracketed (`::1`).
51
32
  */
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+
3
+ const { urlToHttpOptions } = require('./url-to-http-options-polyfill')
4
+
5
+ /**
6
+ * Convert an agent/intake URL into Node http(s) request options.
7
+ *
8
+ * A Windows named pipe (`unix://./pipe/<name>`) parses the `.` as the URL
9
+ * authority, dropping it from the path. Fold it back so the socket path stays
10
+ * `//./pipe/<name>`; otherwise it collapses to `/pipe/<name>` and misses the
11
+ * pipe. Exporters receive `config.url` as a URL object, so this must run for
12
+ * both string and object input.
13
+ *
14
+ * @param {string|URL|object} urlObjOrString
15
+ * @returns {object}
16
+ */
17
+ function parseUrl (urlObjOrString) {
18
+ // `urlToHttpOptions` returns `pathname` at runtime, but @types/node narrows it
19
+ // to `ClientRequestArgs`, which omits it; cast so the named-pipe fold below can
20
+ // read and rewrite it.
21
+ const url = /** @type {import('node:http').ClientRequestArgs & { pathname: string }} */ (
22
+ urlObjOrString !== null && typeof urlObjOrString === 'object'
23
+ ? urlToHttpOptions(urlObjOrString)
24
+ : urlToHttpOptions(new URL(urlObjOrString))
25
+ )
26
+
27
+ if (url.protocol === 'unix:' && url.hostname === '.') {
28
+ url.path = url.pathname = `//.${url.pathname}`
29
+ }
30
+
31
+ return url
32
+ }
33
+
34
+ module.exports = { parseUrl }
@@ -37,9 +37,7 @@ function makeRequest (data, url, cb) {
37
37
  'Datadog-Meta-Tracer-Version': pkg.version,
38
38
  'Content-Type': 'application/msgpack',
39
39
  },
40
- protocol: url.protocol,
41
- hostname: url.hostname,
42
- port: url.port,
40
+ url,
43
41
  }
44
42
 
45
43
  log.debug('Request to the intake: %j', options)
@@ -14,11 +14,13 @@ function guard (fn) {
14
14
  var initBailout = false
15
15
  var clobberBailout = false
16
16
  var forced = isTrue(process.env.DD_INJECT_FORCE)
17
- var engines = require('../../../../package.json').engines
18
- var versions = engines.node.match(/^>=(\d+) <(\d+)$/)
17
+ var pkg = require('../../../../package.json')
18
+ var engines = pkg.engines
19
+ var versions = engines.node.match(/^>=(\d+)$/)
19
20
  var minMajor = versions[1]
20
- var nextMajor = versions[2]
21
+ var nextMajor = pkg.nodeMaxMajor
21
22
  var version = process.versions.node
23
+ var supportedRange = engines.node + ' <' + nextMajor
22
24
 
23
25
  if (process.env.DD_INJECTION_ENABLED) {
24
26
  // If we're running via single-step install, and we're in the app's
@@ -45,16 +47,17 @@ function guard (fn) {
45
47
  // should not initialize the tracer.
46
48
  if (!clobberBailout && (NODE_MAJOR < minMajor || NODE_MAJOR >= nextMajor)) {
47
49
  initBailout = true
50
+ var runtimeInfo = 'Incompatible runtime Node.js ' + version + ', supported runtimes: Node.js ' + supportedRange
48
51
  telemetry([
49
52
  { name: 'abort', tags: ['reason:incompatible_runtime'] },
50
53
  { name: 'abort.runtime', tags: [] }
51
54
  ], undefined, {
52
55
  result: 'abort',
53
56
  result_class: 'incompatible_runtime',
54
- result_reason: 'Incompatible runtime Node.js ' + version + ', supported runtimes: Node.js ' + engines.node
57
+ result_reason: runtimeInfo
55
58
  })
56
59
  log.info('Aborting application instrumentation due to incompatible_runtime.')
57
- log.info('Found incompatible runtime Node.js %s, Supported runtimes: Node.js %s.', version, engines.node)
60
+ log.info('Found incompatible runtime Node.js %s, Supported runtimes: Node.js %s.', version, supportedRange)
58
61
  if (forced) {
59
62
  log.info('DD_INJECT_FORCE enabled, allowing unsupported runtimes and continuing.')
60
63
  }
@@ -1,14 +1,16 @@
1
1
  'use strict'
2
2
 
3
3
  const { getValueFromEnvSources } = require('./config/helper')
4
- const { isFalse } = require('./util')
5
4
 
6
5
  // Global `jest` is only present in Jest workers.
7
6
  const inJestWorker = typeof jest !== 'undefined'
8
7
 
9
- const ddTraceDisabled = getValueFromEnvSources('DD_TRACE_ENABLED')
10
- ? isFalse(getValueFromEnvSources('DD_TRACE_ENABLED'))
11
- : String(getValueFromEnvSources('OTEL_TRACES_EXPORTER')).toLowerCase() === 'none'
8
+ // skipDefault: distinguish an unset DD_TRACE_ENABLED (fall back to the OTel signal) from an
9
+ // explicit value; the registered default would otherwise mask the OTEL_TRACES_EXPORTER check.
10
+ const ddTraceEnabled = getValueFromEnvSources('DD_TRACE_ENABLED', true)
11
+ const ddTraceDisabled = ddTraceEnabled === undefined
12
+ ? getValueFromEnvSources('OTEL_TRACES_EXPORTER') === 'none'
13
+ : ddTraceEnabled === false
12
14
 
13
15
  module.exports = ddTraceDisabled || inJestWorker
14
16
  ? require('./noop/proxy')
@@ -14,6 +14,12 @@ module.exports = {
14
14
  PROPAGATED_PARENT_ID_KEY: '_dd.p.llmobs_parent_id',
15
15
  PROPAGATED_ML_APP_KEY: '_dd.p.llmobs_ml_app',
16
16
  PARENT_ID_KEY: '_ml_obs.llmobs_parent_id',
17
+ PROPAGATED_SAMPLE_RATE_KEY: '_dd.p.llmobs_sr',
18
+ PROPAGATED_SAMPLING_DECISION_KEY: '_dd.p.llmobs_sd',
19
+ SAMPLE_RATE: '_ml_obs.sample_rate',
20
+ SAMPLING_DECISION: '_ml_obs.sampling_decision',
21
+ SAMPLING_DECISION_SAMPLED: '1',
22
+ SAMPLING_DECISION_DROPPED: '0',
17
23
  TAGS: '_ml_obs.tags',
18
24
  NAME: '_ml_obs.name',
19
25
  TRACE_ID: '_ml_obs.trace_id',
@@ -9,6 +9,10 @@ const {
9
9
  ML_APP,
10
10
  PROPAGATED_ML_APP_KEY,
11
11
  PROPAGATED_PARENT_ID_KEY,
12
+ SAMPLE_RATE,
13
+ SAMPLING_DECISION,
14
+ PROPAGATED_SAMPLE_RATE_KEY,
15
+ PROPAGATED_SAMPLING_DECISION_KEY,
12
16
  } = require('./constants/tags')
13
17
  const { storage } = require('./storage')
14
18
  const telemetry = require('./telemetry')
@@ -68,10 +72,10 @@ function enable (config) {
68
72
  spanFinishCh.subscribe(handleSpanProcess)
69
73
 
70
74
  // distributed tracing for llmobs
71
- injectCh.subscribe(handleLLMObsParentIdInjection)
75
+ injectCh.subscribe(handleLLMObsInjection)
72
76
 
73
77
  setAgentStrategy(config, useAgentless => {
74
- if (useAgentless && !(config.apiKey && config.site)) {
78
+ if (useAgentless && !(config.DD_API_KEY && config.site)) {
75
79
  if (DD_MAJOR < 6 || !config?.startupLogs) {
76
80
  // eslint-disable-next-line no-console
77
81
  console.error(INCOMPATIBLE_INITIALIZATION)
@@ -92,7 +96,7 @@ function disable () {
92
96
  if (evalMetricAppendCh.hasSubscribers) evalMetricAppendCh.unsubscribe(handleEvalMetricAppend)
93
97
  if (flushCh.hasSubscribers) flushCh.unsubscribe(handleFlush)
94
98
  if (spanFinishCh.hasSubscribers) spanFinishCh.unsubscribe(handleSpanProcess)
95
- if (injectCh.hasSubscribers) injectCh.unsubscribe(handleLLMObsParentIdInjection)
99
+ if (injectCh.hasSubscribers) injectCh.unsubscribe(handleLLMObsInjection)
96
100
  if (registerUserSpanProcessorCh.hasSubscribers) registerUserSpanProcessorCh.unsubscribe(handleRegisterProcessor)
97
101
 
98
102
  spanWriter?.destroy()
@@ -106,8 +110,8 @@ function disable () {
106
110
  }
107
111
 
108
112
  // since LLMObs traces can extend between services and be the same trace,
109
- // we need to propagate the parent id and mlApp.
110
- function handleLLMObsParentIdInjection ({ carrier }) {
113
+ // we need to propagate the parent id, mlApp, and sampling rate/decision.
114
+ function handleLLMObsInjection ({ carrier }) {
111
115
  // Respect the standard propagator's gate: when trace tag propagation is
112
116
  // disabled, don't write `x-datadog-tags` for LLMObs either.
113
117
  if (globalTracerConfig.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) return
@@ -122,7 +126,12 @@ function handleLLMObsParentIdInjection ({ carrier }) {
122
126
  parentContext?._trace?.tags?.[PROPAGATED_ML_APP_KEY] ||
123
127
  globalTracerConfig.llmobs.mlApp
124
128
 
125
- if (!parentId && !mlApp) return
129
+ const sampleRate =
130
+ mlObsSpanTags?.[SAMPLE_RATE] ?? parentContext?._trace?.tags?.[PROPAGATED_SAMPLE_RATE_KEY]
131
+ const samplingDecision =
132
+ mlObsSpanTags?.[SAMPLING_DECISION] ?? parentContext?._trace?.tags?.[PROPAGATED_SAMPLING_DECISION_KEY]
133
+
134
+ if (!parentId && !mlApp && samplingDecision == null) return
126
135
 
127
136
  // `_injectTags` only writes `x-datadog-tags` when the trace has `_dd.p.*`
128
137
  // tags, so it may be undefined here — coalesce before appending.
@@ -130,6 +139,8 @@ function handleLLMObsParentIdInjection ({ carrier }) {
130
139
  let tags = existing || ''
131
140
  if (parentId) tags += `${tags ? ',' : ''}${PROPAGATED_PARENT_ID_KEY}=${parentId}`
132
141
  if (mlApp) tags += `${tags ? ',' : ''}${PROPAGATED_ML_APP_KEY}=${mlApp}`
142
+ if (sampleRate != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLE_RATE_KEY}=${sampleRate}`
143
+ if (samplingDecision != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLING_DECISION_KEY}=${samplingDecision}`
133
144
  if (tags !== existing) carrier['x-datadog-tags'] = tags
134
145
  }
135
146
 
@@ -25,7 +25,7 @@ class LLMObsPlugin extends TracingPlugin {
25
25
  start (ctx) {
26
26
  // even though llmobs span events won't be enqueued if llmobs is disabled
27
27
  // we should avoid doing any computations here (these listeners aren't disabled)
28
- const enabled = this._tracerConfig.llmobs.enabled
28
+ const enabled = this._tracerConfig.llmobs.DD_LLMOBS_ENABLED
29
29
  if (!enabled) return
30
30
 
31
31
  const parentStore = llmobsStorage.getStore()
@@ -52,7 +52,7 @@ class LLMObsPlugin extends TracingPlugin {
52
52
  }
53
53
 
54
54
  end (ctx) {
55
- const enabled = this._tracerConfig.llmobs.enabled
55
+ const enabled = this._tracerConfig.llmobs.DD_LLMOBS_ENABLED
56
56
  if (!enabled) return
57
57
 
58
58
  // only attempt to restore the context if the current span was an LLMObs span
@@ -67,7 +67,7 @@ class LLMObsPlugin extends TracingPlugin {
67
67
  asyncEnd (ctx) {
68
68
  // even though llmobs span events won't be enqueued if llmobs is disabled
69
69
  // we should avoid doing any computations here (these listeners aren't disabled)
70
- const enabled = this._tracerConfig.llmobs.enabled
70
+ const enabled = this._tracerConfig.llmobs.DD_LLMOBS_ENABLED
71
71
  if (!enabled) return
72
72
 
73
73
  const apmStore = ctx.currentStore
@@ -85,7 +85,7 @@ class LLMObsPlugin extends TracingPlugin {
85
85
 
86
86
  configure (config) {
87
87
  // we do not want to enable any LLMObs plugins if it is disabled on the tracer
88
- const llmobsEnabled = this._tracerConfig.llmobs.enabled
88
+ const llmobsEnabled = this._tracerConfig.llmobs.DD_LLMOBS_ENABLED
89
89
  if (llmobsEnabled === false) {
90
90
  config = typeof config === 'boolean' ? false : { ...config, enabled: false } // override to false
91
91
  }
@@ -114,7 +114,7 @@ function extractMetrics (response) {
114
114
  }
115
115
 
116
116
  const totalTokens = tokenUsage.totalTokenCount ||
117
- (tokenUsage.promptTokenCount || 0) + (tokenUsage.candidatesTokenCount || 0)
117
+ (tokenUsage.promptTokenCount || 0) + (tokenUsage.candidatesTokenCount || 0)
118
118
  if (totalTokens) {
119
119
  metrics.totalTokens = totalTokens
120
120
  }
@@ -458,8 +458,8 @@ function formatStreamingOutput (response) {
458
458
 
459
459
  // Skip special cases in streaming (handle them as non-streaming)
460
460
  if (content.parts.some(part => part.functionCall ||
461
- part.executableCode ||
462
- part.codeExecutionResult)) {
461
+ part.executableCode ||
462
+ part.codeExecutionResult)) {
463
463
  messages.push(...formatNonStreamingCandidate(candidate))
464
464
  continue
465
465
  }
@@ -9,7 +9,11 @@ const ANTHROPIC_PROVIDER_NAME = 'anthropic'
9
9
  const BEDROCK_PROVIDER_NAME = 'amazon_bedrock'
10
10
  const OPENAI_PROVIDER_NAME = 'openai'
11
11
 
12
- const SUPPORTED_INTEGRATIONS = new Set(['openai'])
12
+ // Providers that ship their own LLMObs integration. When one of these is also
13
+ // enabled, the LangChain model span is demoted to `workflow` so the provider
14
+ // integration emits the single `llm` span — avoiding two llm spans (and
15
+ // double-counted tokens/cost) for one underlying call.
16
+ const SUPPORTED_INTEGRATIONS = new Set(['openai', 'anthropic'])
13
17
  const LLM_SPAN_TYPES = new Set(['llm', 'chat_model', 'embedding'])
14
18
  const LLM = 'llm'
15
19
  const WORKFLOW = 'workflow'
@@ -14,7 +14,7 @@ class PregelStreamLLMObsPlugin extends LLMObsPlugin {
14
14
  getLLMObsSpanRegisterOptions (ctx) {
15
15
  const name = ctx.self.name || 'LangGraph'
16
16
 
17
- const enabled = this._tracerConfig.llmobs.enabled
17
+ const enabled = this._tracerConfig.llmobs.DD_LLMOBS_ENABLED
18
18
  if (!enabled) return
19
19
 
20
20
  const span = ctx.currentStore?.span
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { channel } = require('dc-polyfill')
4
4
 
5
- const { isError, isTrue } = require('../util')
5
+ const { isError } = require('../util')
6
6
  const tracerVersion = require('../../../../package.json').version
7
7
  const logger = require('../log')
8
8
  const { getValueFromEnvSources } = require('../config/helper')
@@ -49,7 +49,7 @@ class LLMObs extends NoopLLMObs {
49
49
  }
50
50
 
51
51
  get enabled () {
52
- return this._config.llmobs.enabled ?? false
52
+ return this._config.llmobs.DD_LLMOBS_ENABLED ?? false
53
53
  }
54
54
 
55
55
  enable (options = {}) {
@@ -65,15 +65,17 @@ class LLMObs extends NoopLLMObs {
65
65
 
66
66
  logger.debug('Enabling LLMObs')
67
67
 
68
- const DD_LLMOBS_ENABLED = getValueFromEnvSources('DD_LLMOBS_ENABLED')
68
+ // skipDefault: only an explicit DD_LLMOBS_ENABLED=false blocks enable(); an unset value
69
+ // (its default is false) must still allow this programmatic opt-in.
70
+ const DD_LLMOBS_ENABLED = getValueFromEnvSources('DD_LLMOBS_ENABLED', true)
69
71
 
70
- if (DD_LLMOBS_ENABLED != null && !isTrue(DD_LLMOBS_ENABLED)) {
72
+ if (DD_LLMOBS_ENABLED === false) {
71
73
  logger.debug('LLMObs.enable() called when DD_LLMOBS_ENABLED is false. No action taken.')
72
74
  return
73
75
  }
74
76
 
75
77
  // TODO: These configs should be passed through directly at construction time instead.
76
- this._config.llmobs.enabled = true
78
+ this._config.llmobs.DD_LLMOBS_ENABLED = true
77
79
  this._config.llmobs.mlApp = options.mlApp
78
80
  this._config.llmobs.agentlessEnabled = options.agentlessEnabled
79
81
 
@@ -94,7 +96,7 @@ class LLMObs extends NoopLLMObs {
94
96
 
95
97
  logger.debug('Disabling LLMObs')
96
98
 
97
- this._config.llmobs.enabled = false
99
+ this._config.llmobs.DD_LLMOBS_ENABLED = false
98
100
 
99
101
  // disable writers and channel subscribers
100
102
  this._llmobsModule.disable()
@@ -33,6 +33,8 @@ const {
33
33
  ROUTING_API_KEY,
34
34
  ROUTING_SITE,
35
35
  LLMOBS_SUBMITTED_TAG_KEY,
36
+ SAMPLE_RATE,
37
+ SAMPLING_DECISION,
36
38
  } = require('./constants/tags')
37
39
  const { UNSERIALIZABLE_VALUE_TEXT } = require('./constants/text')
38
40
  const telemetry = require('./telemetry')
@@ -81,7 +83,7 @@ class LLMObsSpanProcessor {
81
83
 
82
84
  // TODO: instead of relying on the tagger's weakmap registry, can we use some namespaced storage correlation?
83
85
  process (span) {
84
- if (!this.#config.llmobs.enabled) return
86
+ if (!this.#config.llmobs.DD_LLMOBS_ENABLED) return
85
87
  // if the span is not in our private tagger map, it is not an llmobs span
86
88
  if (!LLMObsTagger.tagMap.has(span)) return
87
89
 
@@ -248,6 +250,8 @@ class LLMObsSpanProcessor {
248
250
  _dd: {
249
251
  span_id: span.context().toSpanId(),
250
252
  trace_id: span.context().toTraceId(true),
253
+ sample_rate: mlObsTags[SAMPLE_RATE],
254
+ sampling_decision: mlObsTags[SAMPLING_DECISION],
251
255
  },
252
256
  }
253
257