dd-trace 5.110.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 (115) hide show
  1. package/README.md +13 -8
  2. package/ci/init.js +21 -2
  3. package/ci/vitest-no-worker-init-setup.mjs +430 -0
  4. package/ext/tags.js +2 -0
  5. package/index.d.ts +13 -0
  6. package/initialize.mjs +5 -6
  7. package/loader-hook.mjs +76 -55
  8. package/package.json +9 -9
  9. package/packages/datadog-instrumentations/src/fs.js +8 -6
  10. package/packages/datadog-instrumentations/src/graphql.js +26 -495
  11. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
  12. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  13. package/packages/datadog-instrumentations/src/mongoose.js +14 -2
  14. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
  15. package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
  16. package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
  17. package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
  18. package/packages/datadog-instrumentations/src/vitest.js +11 -1855
  19. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
  20. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
  21. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
  22. package/packages/datadog-plugin-cucumber/src/index.js +1 -1
  23. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +10 -4
  24. package/packages/datadog-plugin-cypress/src/support.js +45 -1
  25. package/packages/datadog-plugin-express/src/code_origin.js +1 -1
  26. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
  27. package/packages/datadog-plugin-graphql/src/execute.js +627 -11
  28. package/packages/datadog-plugin-graphql/src/index.js +20 -8
  29. package/packages/datadog-plugin-graphql/src/parse.js +24 -4
  30. package/packages/datadog-plugin-graphql/src/validate.js +16 -5
  31. package/packages/datadog-plugin-http/src/index.js +6 -8
  32. package/packages/datadog-plugin-jest/src/index.js +1 -1
  33. package/packages/datadog-plugin-mocha/src/index.js +1 -1
  34. package/packages/datadog-plugin-openai/src/services.js +2 -2
  35. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  36. package/packages/datadog-plugin-vitest/src/index.js +69 -63
  37. package/packages/datadog-shimmer/src/shimmer.js +37 -16
  38. package/packages/dd-trace/src/aiguard/sdk.js +2 -2
  39. package/packages/dd-trace/src/appsec/api_security/sampler.js +3 -3
  40. package/packages/dd-trace/src/appsec/channels.js +3 -1
  41. package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
  42. package/packages/dd-trace/src/appsec/graphql.js +5 -5
  43. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
  44. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  45. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
  46. package/packages/dd-trace/src/appsec/index.js +10 -1
  47. package/packages/dd-trace/src/appsec/lambda.js +135 -0
  48. package/packages/dd-trace/src/appsec/reporter.js +49 -10
  49. package/packages/dd-trace/src/appsec/telemetry/index.js +1 -1
  50. package/packages/dd-trace/src/appsec/waf/index.js +16 -4
  51. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  52. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
  53. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
  54. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  55. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  56. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
  57. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
  58. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
  59. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
  60. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
  61. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
  62. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
  63. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
  64. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
  65. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
  66. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
  67. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
  68. package/packages/dd-trace/src/config/defaults.js +3 -2
  69. package/packages/dd-trace/src/config/generated-config-types.d.ts +61 -57
  70. package/packages/dd-trace/src/config/helper.js +1 -0
  71. package/packages/dd-trace/src/config/index.js +35 -22
  72. package/packages/dd-trace/src/config/major-overrides.js +4 -2
  73. package/packages/dd-trace/src/config/remote_config.js +1 -1
  74. package/packages/dd-trace/src/config/supported-configurations.json +90 -56
  75. package/packages/dd-trace/src/constants.js +7 -0
  76. package/packages/dd-trace/src/datastreams/processor.js +11 -3
  77. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  78. package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
  79. package/packages/dd-trace/src/llmobs/index.js +1 -1
  80. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
  81. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  82. package/packages/dd-trace/src/llmobs/sdk.js +3 -3
  83. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  84. package/packages/dd-trace/src/llmobs/tagger.js +3 -3
  85. package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
  86. package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
  87. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
  88. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
  89. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +38 -39
  90. package/packages/dd-trace/src/opentelemetry/trace/index.js +4 -0
  91. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  92. package/packages/dd-trace/src/opentracing/tracer.js +6 -1
  93. package/packages/dd-trace/src/plugin_manager.js +7 -7
  94. package/packages/dd-trace/src/plugins/ci_plugin.js +12 -3
  95. package/packages/dd-trace/src/plugins/util/llm.js +6 -1
  96. package/packages/dd-trace/src/plugins/util/test.js +11 -9
  97. package/packages/dd-trace/src/profiling/exporter_cli.js +2 -2
  98. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -3
  99. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
  100. package/packages/dd-trace/src/proxy.js +8 -8
  101. package/packages/dd-trace/src/span_processor.js +5 -6
  102. package/packages/dd-trace/src/span_stats.js +96 -78
  103. package/packages/dd-trace/src/startup-log.js +2 -1
  104. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  105. package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
  106. package/packages/dd-trace/src/telemetry/index.js +2 -2
  107. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  108. package/packages/dd-trace/src/telemetry/send-data.js +8 -8
  109. package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
  110. package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
  111. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  112. package/vendor/dist/protobufjs/index.js +1 -1
  113. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  114. package/vendor/dist/shell-quote/index.js +1 -1
  115. package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
@@ -112,6 +112,12 @@ const TEST_RETRY_REASON = 'test.retry_reason'
112
112
  const TEST_HAS_FAILED_ALL_RETRIES = 'test.has_failed_all_retries'
113
113
  const TEST_IS_MODIFIED = 'test.is_modified'
114
114
  const TEST_HAS_DYNAMIC_NAME = '_dd.has_dynamic_name'
115
+ const EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS = [
116
+ { limitMs: 5 * 1000, key: '5s' },
117
+ { limitMs: 10 * 1000, key: '10s' },
118
+ { limitMs: 30 * 1000, key: '30s' },
119
+ { limitMs: 5 * 60 * 1000, key: '5m' },
120
+ ]
115
121
  const CI_APP_ORIGIN = 'ciapp-test'
116
122
 
117
123
  // Matches patterns that are almost certainly runtime-generated values in test names:
@@ -493,6 +499,7 @@ module.exports = {
493
499
  removeInvalidMetadata,
494
500
  parseAnnotations,
495
501
  getIsFaultyEarlyFlakeDetection,
502
+ EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS,
496
503
  getEfdRetryCount,
497
504
  getMaxEfdRetryCount,
498
505
  TEST_BROWSER_DRIVER,
@@ -1447,13 +1454,7 @@ function parseAnnotations (annotations) {
1447
1454
  * @returns {number}
1448
1455
  */
1449
1456
  function getEfdRetryCount (durationMs, slowTestRetries) {
1450
- const thresholds = [
1451
- { limitMs: 5 * 1000, key: '5s' },
1452
- { limitMs: 10 * 1000, key: '10s' },
1453
- { limitMs: 30 * 1000, key: '30s' },
1454
- { limitMs: 5 * 60 * 1000, key: '5m' },
1455
- ]
1456
- for (const { limitMs, key } of thresholds) {
1457
+ for (const { limitMs, key } of EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS) {
1457
1458
  if (durationMs < limitMs) {
1458
1459
  return slowTestRetries[key] ?? 0
1459
1460
  }
@@ -1616,7 +1617,7 @@ function isFailedTestReplaySupported (testFramework, frameworkVersion) {
1616
1617
  : true
1617
1618
  }
1618
1619
 
1619
- function getLibraryCapabilitiesTags (testFramework, frameworkVersion) {
1620
+ function getLibraryCapabilitiesTags (testFramework, frameworkVersion, options = {}) {
1620
1621
  return {
1621
1622
  [DD_CAPABILITIES_TEST_IMPACT_ANALYSIS]: isTiaSupported(testFramework)
1622
1623
  ? '1'
@@ -1638,7 +1639,8 @@ function getLibraryCapabilitiesTags (testFramework, frameworkVersion) {
1638
1639
  isAttemptToFixSupported(testFramework, frameworkVersion)
1639
1640
  ? '5'
1640
1641
  : undefined,
1641
- [DD_CAPABILITIES_FAILED_TEST_REPLAY]: isFailedTestReplaySupported(testFramework, frameworkVersion)
1642
+ [DD_CAPABILITIES_FAILED_TEST_REPLAY]: !options.omitFailedTestReplay &&
1643
+ isFailedTestReplaySupported(testFramework, frameworkVersion)
1642
1644
  ? '1'
1643
1645
  : undefined,
1644
1646
  }
@@ -18,7 +18,7 @@ function exporterFromURL (url) {
18
18
  return new FileExporter({}, fileURLToPath(url))
19
19
  }
20
20
  // The subprocess has no tracer config, so mirror the canonical config property
21
- // names the exporters read. Normalize the raw env to the parsed profiling.enabled
21
+ // names the exporters read. Normalize the raw env to the parsed profiling.DD_PROFILING_ENABLED
22
22
  // values ('true' | 'false' | 'auto') the main process would have produced.
23
23
  const profilingEnabled = (getValueFromEnvSources('DD_PROFILING_ENABLED') ?? '').toLowerCase()
24
24
  const enabled = profilingEnabled === 'auto'
@@ -28,7 +28,7 @@ function exporterFromURL (url) {
28
28
  url,
29
29
  DD_PROFILING_UPLOAD_TIMEOUT: timeoutMs,
30
30
  DD_INJECTION_ENABLED: getValueFromEnvSources('DD_INJECTION_ENABLED'),
31
- profiling: { enabled },
31
+ profiling: { DD_PROFILING_ENABLED: enabled },
32
32
  })
33
33
  }
34
34
 
@@ -11,10 +11,10 @@ const processTags = require('../../process-tags')
11
11
  /** @typedef {import('../../config/config-base')} TracerConfig */
12
12
 
13
13
  /**
14
- * Maps the canonical profiling.enabled value to the activation reported in the
14
+ * Maps the canonical profiling.DD_PROFILING_ENABLED value to the activation reported in the
15
15
  * profile event.
16
16
  *
17
- * @param {string} [enabled] - config.profiling.enabled ('true' | 'false' | 'auto')
17
+ * @param {string} [enabled] - config.profiling.DD_PROFILING_ENABLED ('true' | 'false' | 'auto')
18
18
  * @returns {string}
19
19
  */
20
20
  function getActivation (enabled) {
@@ -38,7 +38,7 @@ class EventSerializer {
38
38
  this.#service = config.service
39
39
  this.#appVersion = config.version
40
40
  this.#libraryInjected = !!config.DD_INJECTION_ENABLED
41
- this.#activation = getActivation(config.profiling?.enabled)
41
+ this.#activation = getActivation(config.profiling?.DD_PROFILING_ENABLED)
42
42
  }
43
43
 
44
44
  /**
@@ -148,7 +148,7 @@ class NativeWallProfiler {
148
148
  this.#endpointCollectionEnabled = config.DD_PROFILING_ENDPOINT_COLLECTION_ENABLED
149
149
  this.#flushIntervalMillis = flushInterval
150
150
  this.#samplingIntervalMicros = SAMPLING_INTERVAL * 1000
151
- this.#telemetryHeartbeatIntervalMillis = config.telemetry.heartbeatInterval
151
+ this.#telemetryHeartbeatIntervalMillis = config.telemetry.DD_TELEMETRY_HEARTBEAT_INTERVAL
152
152
  this.#timelineEnabled = config.DD_PROFILING_TIMELINE_ENABLED
153
153
  this.#v8ProfilerBugWorkaroundEnabled = config.DD_PROFILING_V8_PROFILER_BUG_WORKAROUND
154
154
 
@@ -138,7 +138,7 @@ class Tracer extends NoopProxy {
138
138
  spanleak.startScrubber()
139
139
  }
140
140
 
141
- if (config.remoteConfig.enabled && !config.isCiVisibility) {
141
+ if (config.remoteConfig.DD_REMOTE_CONFIGURATION_ENABLED && !config.isCiVisibility) {
142
142
  const RemoteConfig = require('./remote_config')
143
143
  const rc = new RemoteConfig(config)
144
144
 
@@ -180,11 +180,11 @@ class Tracer extends NoopProxy {
180
180
  openfeatureRemoteConfig.enable(rc, config, () => this.openfeature)
181
181
  }
182
182
 
183
- if (config.profiling.enabled === 'true') {
183
+ if (config.profiling.DD_PROFILING_ENABLED === 'true') {
184
184
  this._profilerStarted = this._startProfiler(config)
185
185
  } else {
186
186
  this._profilerStarted = false
187
- if (config.profiling.enabled === 'auto') {
187
+ if (config.profiling.DD_PROFILING_ENABLED === 'auto') {
188
188
  const { SSIHeuristics } = require('./profiling/ssi-heuristics')
189
189
  const ssiHeuristics = new SSIHeuristics(config)
190
190
  ssiHeuristics.start()
@@ -216,7 +216,7 @@ class Tracer extends NoopProxy {
216
216
 
217
217
  this._modules.rewriter.enable(config)
218
218
 
219
- if (config.tracing && config.DD_CIVISIBILITY_MANUAL_API_ENABLED) {
219
+ if (config.DD_TRACE_ENABLED && config.testOptimization.DD_CIVISIBILITY_MANUAL_API_ENABLED) {
220
220
  const TestApiManualPlugin = require('./ci-visibility/test-api-manual/test-api-manual-plugin')
221
221
  this._testApiManualPlugin = new TestApiManualPlugin(this)
222
222
  // `shouldGetEnvironmentData` is passed as false so that we only lazily calculate it
@@ -225,7 +225,7 @@ class Tracer extends NoopProxy {
225
225
  this._testApiManualPlugin.configure({ ...config, enabled: true }, false)
226
226
  }
227
227
  if (config.DD_AGENTLESS_LOG_SUBMISSION_ENABLED) {
228
- if (config.apiKey) {
228
+ if (config.DD_API_KEY) {
229
229
  const LogSubmissionPlugin = require('./ci-visibility/log-submission/log-submission-plugin')
230
230
  const automaticLogPlugin = new LogSubmissionPlugin(this)
231
231
  automaticLogPlugin.configure({ ...config, enabled: true })
@@ -237,7 +237,7 @@ class Tracer extends NoopProxy {
237
237
  }
238
238
  }
239
239
 
240
- if (config.isTestDynamicInstrumentationEnabled) {
240
+ if (config.testOptimization.DD_TEST_FAILED_TEST_REPLAY_ENABLED) {
241
241
  const getDynamicInstrumentationClient = require('./ci-visibility/dynamic-instrumentation')
242
242
  // We instantiate the client but do not start the Worker here. The worker is started lazily
243
243
  getDynamicInstrumentationClient(config)
@@ -270,11 +270,11 @@ class Tracer extends NoopProxy {
270
270
  * @param {import('./config/config-base')} config - Tracer configuration
271
271
  */
272
272
  #updateTracing (config) {
273
- if (config.tracing !== false) {
273
+ if (config.DD_TRACE_ENABLED !== false) {
274
274
  if (config.appsec.enabled) {
275
275
  this._modules.appsec.enable(config)
276
276
  }
277
- if (config.llmobs.enabled) {
277
+ if (config.llmobs.DD_LLMOBS_ENABLED) {
278
278
  this._modules.llmobs.enable(config)
279
279
  }
280
280
  if (!this._tracingInitialized) {
@@ -11,16 +11,15 @@ const startedSpans = new WeakSet()
11
11
  const finishedSpans = new WeakSet()
12
12
 
13
13
  class SpanProcessor {
14
- constructor (exporter, prioritySampler, config) {
14
+ constructor (exporter, prioritySampler, config, otlpStatsExporter) {
15
15
  this._exporter = exporter
16
16
  this._prioritySampler = prioritySampler
17
17
  this._config = config
18
18
  this._killAll = false
19
19
 
20
- // TODO: This should already have been calculated in `config.js`.
21
- if (config.stats?.enabled && !config.appsec?.standalone?.enabled) {
20
+ if (config.stats?.DD_TRACE_STATS_COMPUTATION_ENABLED && !config.appsec?.standalone?.enabled) {
22
21
  const { SpanStatsProcessor } = require('./span_stats')
23
- this._stats = new SpanStatsProcessor(config)
22
+ this._stats = new SpanStatsProcessor(config, otlpStatsExporter)
24
23
  }
25
24
 
26
25
  this._spanSampler = new SpanSampler(config.sampler)
@@ -42,11 +41,11 @@ class SpanProcessor {
42
41
  const active = []
43
42
  const formatted = []
44
43
  const trace = spanContext._trace
45
- const { flushMinSpans, tracing } = this._config
44
+ const { flushMinSpans, DD_TRACE_ENABLED } = this._config
46
45
  const { started, finished } = trace
47
46
 
48
47
  if (trace.record === false) return
49
- if (tracing === false) {
48
+ if (DD_TRACE_ENABLED === false) {
50
49
  this._erase(trace, active)
51
50
  return
52
51
  }
@@ -10,8 +10,10 @@ const {
10
10
  HTTP_ENDPOINT,
11
11
  HTTP_ROUTE,
12
12
  HTTP_METHOD,
13
+ SPAN_KIND,
14
+ GRPC_STATUS_CODE,
13
15
  } = require('../../../ext/tags')
14
- const { ORIGIN_KEY, TOP_LEVEL_KEY, SVC_SRC_KEY } = require('./constants')
16
+ const { ORIGIN_KEY, TOP_LEVEL_KEY, SVC_SRC_KEY, GRPC_STATUS_NAMES } = require('./constants')
15
17
  const { version } = require('./pkg')
16
18
  const processTags = require('./process-tags')
17
19
 
@@ -27,43 +29,32 @@ class SpanAggStats {
27
29
  this.aggKey = aggKey
28
30
  this.hits = 0
29
31
  this.topLevelHits = 0
30
- this.errors = 0
31
- this.duration = 0
32
- this.okDistribution = new LogCollapsingLowestDenseDDSketch()
33
- this.errorDistribution = new LogCollapsingLowestDenseDDSketch()
32
+ this.topLevelOkDistribution = new LogCollapsingLowestDenseDDSketch()
33
+ this.topLevelErrorDistribution = new LogCollapsingLowestDenseDDSketch()
34
+ this.nonTopLevelOkDistribution = new LogCollapsingLowestDenseDDSketch()
35
+ this.nonTopLevelErrorDistribution = new LogCollapsingLowestDenseDDSketch()
34
36
  }
35
37
 
36
38
  record (span) {
37
39
  const durationNs = span.duration
38
40
  this.hits++
39
- this.duration += durationNs
40
-
41
- if (span.metrics[TOP_LEVEL_KEY]) {
42
- this.topLevelHits++
43
- }
44
-
41
+ const isTopLevel = Boolean(span.metrics[TOP_LEVEL_KEY])
42
+ if (isTopLevel) this.topLevelHits++
45
43
  if (span.error) {
46
- this.errors++
47
- this.errorDistribution.accept(durationNs)
44
+ if (isTopLevel) this.topLevelErrorDistribution.accept(durationNs)
45
+ else this.nonTopLevelErrorDistribution.accept(durationNs)
48
46
  } else {
49
- this.okDistribution.accept(durationNs)
47
+ if (isTopLevel) this.topLevelOkDistribution.accept(durationNs)
48
+ else this.nonTopLevelOkDistribution.accept(durationNs)
50
49
  }
51
50
  }
52
51
 
53
52
  toJSON () {
54
53
  const {
55
- name,
56
- service,
57
- resource,
58
- type,
59
- statusCode,
60
- synthetics,
61
- method,
62
- endpoint,
63
- srvSrc,
54
+ name, service, resource, type, statusCode, synthetics, method, endpoint, srvSrc,
55
+ spanKind, rpcStatusCode,
64
56
  } = this.aggKey
65
-
66
- return {
57
+ const base = {
67
58
  Name: name,
68
59
  Service: service,
69
60
  Resource: resource,
@@ -73,13 +64,34 @@ class SpanAggStats {
73
64
  HTTPMethod: method,
74
65
  HTTPEndpoint: endpoint,
75
66
  srv_src: srvSrc,
76
- Hits: this.hits,
77
- TopLevelHits: this.topLevelHits,
78
- Errors: this.errors,
79
- Duration: this.duration,
80
- OkSummary: this.okDistribution.toProto(), // TODO: custom proto encoding
81
- ErrorSummary: this.errorDistribution.toProto(), // TODO: custom proto encoding
67
+ SpanKind: spanKind,
68
+ GRPCStatusCode: rpcStatusCode,
69
+ }
70
+ const rows = []
71
+ if (this.topLevelHits > 0) {
72
+ rows.push({
73
+ ...base,
74
+ Hits: this.topLevelHits,
75
+ TopLevelHits: this.topLevelHits,
76
+ Errors: this.topLevelErrorDistribution.count,
77
+ Duration: this.topLevelOkDistribution.sum + this.topLevelErrorDistribution.sum,
78
+ OkSummary: this.topLevelOkDistribution.toProto(),
79
+ ErrorSummary: this.topLevelErrorDistribution.toProto(),
80
+ })
81
+ }
82
+ const nonTopLevelHits = this.hits - this.topLevelHits
83
+ if (nonTopLevelHits > 0) {
84
+ rows.push({
85
+ ...base,
86
+ Hits: nonTopLevelHits,
87
+ TopLevelHits: 0,
88
+ Errors: this.nonTopLevelErrorDistribution.count,
89
+ Duration: this.nonTopLevelOkDistribution.sum + this.nonTopLevelErrorDistribution.sum,
90
+ OkSummary: this.nonTopLevelOkDistribution.toProto(), // TODO: custom proto encoding
91
+ ErrorSummary: this.nonTopLevelErrorDistribution.toProto(), // TODO: custom proto encoding
92
+ })
82
93
  }
94
+ return rows
83
95
  }
84
96
  }
85
97
 
@@ -94,6 +106,12 @@ class SpanAggKey {
94
106
  this.endpoint = span.meta[HTTP_ROUTE] || span.meta[HTTP_ENDPOINT] || ''
95
107
  this.method = span.meta[HTTP_METHOD] || ''
96
108
  this.srvSrc = span.meta[SVC_SRC_KEY] || ''
109
+ this.spanKind = span.meta[SPAN_KIND] || ''
110
+ // dd gRPC plugin sets a numeric code via setTag; OTel/manual sets a string name via meta.
111
+ const grpcCode = span.meta[GRPC_STATUS_CODE] ?? span.metrics?.[GRPC_STATUS_CODE]
112
+ this.rpcStatusCode = typeof grpcCode === 'number'
113
+ ? (GRPC_STATUS_NAMES[grpcCode] ?? String(grpcCode))
114
+ : (grpcCode ?? '')
97
115
  }
98
116
 
99
117
  toString () {
@@ -107,6 +125,8 @@ class SpanAggKey {
107
125
  this.method,
108
126
  this.endpoint,
109
127
  this.srvSrc,
128
+ this.spanKind,
129
+ this.rpcStatusCode,
110
130
  ].join(',')
111
131
  }
112
132
  }
@@ -137,57 +157,60 @@ class TimeBuckets extends Map {
137
157
  class SpanStatsProcessor {
138
158
  constructor ({
139
159
  stats: {
140
- enabled = false,
160
+ DD_TRACE_STATS_COMPUTATION_ENABLED: enabled = false,
141
161
  interval = 10,
142
- },
162
+ } = {},
143
163
  hostname,
144
164
  port,
145
165
  url,
146
166
  env,
147
167
  tags,
148
- version,
149
- } = {}) {
150
- this.exporter = new SpanStatsExporter({
151
- hostname,
152
- port,
153
- tags,
154
- url,
155
- })
156
- this.interval = interval
157
- this.bucketSizeNs = interval * 1e9
168
+ version: appVersion,
169
+ _DD_TRACE_METRICS_OTEL_FLUSH_INTERVAL: flushIntervalMs,
170
+ } = {}, otlpExporter) {
171
+ if (!otlpExporter) {
172
+ this.exporter = new SpanStatsExporter({ hostname, port, tags, url })
173
+ }
174
+ const intervalMs = otlpExporter ? (flushIntervalMs ?? 10_000) : interval * 1e3
175
+ this.interval = intervalMs / 1e3
176
+ this.bucketSizeNs = intervalMs * 1e6
158
177
  this.buckets = new TimeBuckets()
159
178
  this.hostname = os.hostname()
160
179
  this.enabled = enabled
180
+ this.otlpExporter = otlpExporter || null
161
181
  this.env = env
162
182
  this.tags = tags || {}
163
183
  this.sequence = 0
164
- this.version = version
184
+ this.version = appVersion
165
185
 
166
- if (this.enabled) {
167
- this.timer = setInterval(this.onInterval.bind(this), interval * 1e3)
186
+ if (this.enabled || this.otlpExporter) {
187
+ this.timer = setInterval(this.onInterval.bind(this), intervalMs)
168
188
  this.timer.unref?.()
169
189
  }
170
190
  }
171
191
 
172
192
  onInterval () {
173
- const serialized = this._serializeBuckets()
174
- if (!serialized) return
175
-
176
- this.exporter.export({
177
- Hostname: this.hostname,
178
- Env: this.env,
179
- Version: this.version || version,
180
- Stats: serialized,
181
- Lang: 'javascript',
182
- TracerVersion: pkg.version,
183
- RuntimeID: this.tags['runtime-id'],
184
- Sequence: ++this.sequence,
185
- ProcessTags: processTags.serialized,
186
- })
193
+ const drained = this.#drainBuckets()
194
+
195
+ if (this.enabled && !this.otlpExporter) {
196
+ this.exporter.export({
197
+ Hostname: this.hostname,
198
+ Env: this.env,
199
+ Version: this.version || version,
200
+ Stats: this.#toV06Payload(drained),
201
+ Lang: 'javascript',
202
+ TracerVersion: pkg.version,
203
+ RuntimeID: this.tags['runtime-id'],
204
+ Sequence: ++this.sequence,
205
+ ProcessTags: processTags.serialized,
206
+ })
207
+ } else if (this.otlpExporter && drained.length > 0) {
208
+ this.otlpExporter.export(drained, this.bucketSizeNs)
209
+ }
187
210
  }
188
211
 
189
212
  onSpanFinished (span) {
190
- if (!this.enabled) return
213
+ if (!this.enabled && !this.otlpExporter) return
191
214
  if (!span.metrics[TOP_LEVEL_KEY] && !span.metrics[MEASURED]) return
192
215
 
193
216
  const spanEndNs = span.start + span.duration
@@ -198,27 +221,22 @@ class SpanStatsProcessor {
198
221
  .record(span)
199
222
  }
200
223
 
201
- _serializeBuckets () {
202
- const { bucketSizeNs } = this
203
- const serializedBuckets = []
204
-
224
+ #drainBuckets () {
225
+ const drained = []
205
226
  for (const [timeNs, bucket] of this.buckets.entries()) {
206
- const bucketAggStats = []
207
-
208
- for (const stats of bucket.values()) {
209
- bucketAggStats.push(stats.toJSON())
210
- }
211
-
212
- serializedBuckets.push({
213
- Start: timeNs,
214
- Duration: bucketSizeNs,
215
- Stats: bucketAggStats,
216
- })
227
+ drained.push({ timeNs, bucket })
217
228
  }
218
-
219
229
  this.buckets.clear()
230
+ return drained
231
+ }
220
232
 
221
- return serializedBuckets
233
+ #toV06Payload (drained) {
234
+ const { bucketSizeNs } = this
235
+ return drained.map(({ timeNs, bucket }) => ({
236
+ Start: timeNs,
237
+ Duration: bucketSizeNs,
238
+ Stats: [...bucket.values()].flatMap(stats => stats.toJSON()),
239
+ }))
222
240
  }
223
241
  }
224
242
 
@@ -76,6 +76,7 @@ function logGenericError (message) {
76
76
  */
77
77
  function configInfo () {
78
78
  const url = config.url
79
+ const profilingEnabled = config.profiling.DD_PROFILING_ENABLED
79
80
 
80
81
  return {
81
82
  [inspect.custom] () {
@@ -104,7 +105,7 @@ function configInfo () {
104
105
  ...(config.tags && config.tags.version && { dd_version: config.tags.version }),
105
106
  log_injection_enabled: !!config.logInjection,
106
107
  runtime_metrics_enabled: !!config.runtimeMetrics,
107
- profiling_enabled: config.profiling?.enabled === 'true' || config.profiling?.enabled === 'auto',
108
+ profiling_enabled: profilingEnabled === 'true' || profilingEnabled === 'auto',
108
109
  appsec_enabled: config.appsec.enabled,
109
110
  data_streams_enabled: !!config.dsmEnabled,
110
111
  }
@@ -32,7 +32,7 @@ function waitAndSend (config, application, host) {
32
32
  const [name, version, initialLoadModule] = dependency.split(' ')
33
33
  // If a dependency is from the initial load, *always* send the event
34
34
  // Otherwise, only send if dependencyCollection is enabled
35
- const sendModule = isTrue(initialLoadModule) || config.telemetry?.dependencyCollection
35
+ const sendModule = isTrue(initialLoadModule) || config.telemetry.DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED
36
36
 
37
37
  savedDependenciesToSend.delete(dependency)
38
38
 
@@ -11,7 +11,10 @@ const { sendData } = require('./send-data')
11
11
  */
12
12
  /**
13
13
  * @typedef {import('./send-data').TelemetryConfig & {
14
- * appsec?: { apiSecurity?: { endpointCollectionEnabled?: boolean, endpointCollectionMessageLimit?: number } }
14
+ * appsec: {
15
+ * DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED: boolean,
16
+ * DD_API_SECURITY_ENDPOINT_COLLECTION_MESSAGE_LIMIT: number
17
+ * }
15
18
  * }} TelemetryConfig
16
19
  */
17
20
 
@@ -142,8 +145,7 @@ function flushAndSend () {
142
145
  for (const [key, endpoint] of pendingEndpoints) {
143
146
  batchEndpoints.push(endpoint)
144
147
  pendingEndpoints.delete(key)
145
- // Config is set when endpoint collection is enabled; message limit is optional
146
- if (batchEndpoints.length >= (config.appsec?.apiSecurity?.endpointCollectionMessageLimit ?? 0)) break
148
+ if (batchEndpoints.length >= config.appsec.DD_API_SECURITY_ENDPOINT_COLLECTION_MESSAGE_LIMIT) break
147
149
  }
148
150
 
149
151
  const payloadObj = {
@@ -184,7 +186,7 @@ function flushAndSend () {
184
186
  * @param {import('./send-data').SendDataCallback} updateRetryDataFunction
185
187
  */
186
188
  function start (_config, _application, _host, getRetryDataFunction, updateRetryDataFunction) {
187
- if (!_config.appsec?.apiSecurity?.endpointCollectionEnabled) return
189
+ if (!_config.appsec.DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED) return
188
190
 
189
191
  config = _config
190
192
  application = _application
@@ -5,14 +5,14 @@ let telemetry
5
5
  // Lazy load the telemetry module to avoid the performance impact of loading it unconditionally
6
6
  module.exports = {
7
7
  start (config, ...args) {
8
- if (!config.telemetry.enabled) return
8
+ if (!config.telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED) return
9
9
  telemetry ??= require('./telemetry')
10
10
  telemetry.start(config, ...args)
11
11
  },
12
12
  // This might be called before `start` so we have to trigger loading the
13
13
  // underlying module here as well.
14
14
  updateConfig (changes, config, ...args) {
15
- if (!config.telemetry.enabled) return
15
+ if (!config.telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED) return
16
16
  telemetry ??= require('./telemetry')
17
17
  telemetry.updateConfig(changes, config, ...args)
18
18
  },
@@ -55,7 +55,7 @@ function onErrorLog (msg) {
55
55
  }
56
56
 
57
57
  function start (config) {
58
- if (!config.telemetry.logCollection || enabled) return
58
+ if (!config.telemetry.DD_TELEMETRY_LOG_COLLECTION_ENABLED || enabled) return
59
59
 
60
60
  enabled = true
61
61
 
@@ -87,12 +87,12 @@ function getHeaders (config, application, reqType) {
87
87
  if (config.DD_ROOT_JS_SESSION_ID) {
88
88
  headers['dd-root-session-id'] = config.DD_ROOT_JS_SESSION_ID
89
89
  }
90
- const debug = config.telemetry && config.telemetry.debug
90
+ const debug = config.telemetry && config.telemetry.DD_TELEMETRY_DEBUG
91
91
  if (debug) {
92
92
  headers['dd-telemetry-debug-enabled'] = 'true'
93
93
  }
94
- if (config.apiKey) {
95
- headers['dd-api-key'] = config.apiKey
94
+ if (config.DD_API_KEY) {
95
+ headers['dd-api-key'] = config.DD_API_KEY
96
96
  }
97
97
  return headers
98
98
  }
@@ -137,16 +137,16 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
137
137
  hostname,
138
138
  port,
139
139
  isCiVisibility,
140
- DD_CIVISIBILITY_AGENTLESS_ENABLED,
140
+ testOptimization,
141
141
  } = config
142
142
 
143
143
  let url = config.url
144
144
 
145
- const isCiVisibilityAgentlessMode = isCiVisibility && DD_CIVISIBILITY_AGENTLESS_ENABLED
145
+ const isCiVisibilityAgentlessMode = isCiVisibility && testOptimization.DD_CIVISIBILITY_AGENTLESS_ENABLED
146
146
 
147
147
  if (isCiVisibilityAgentlessMode) {
148
148
  try {
149
- url = config.DD_CIVISIBILITY_AGENTLESS_URL ?? new URL(getAgentlessTelemetryEndpoint(config.site))
149
+ url = testOptimization.DD_CIVISIBILITY_AGENTLESS_URL ?? new URL(getAgentlessTelemetryEndpoint(config.site))
150
150
  } catch (err) {
151
151
  log.error('Telemetry endpoint url is invalid', err)
152
152
  // No point to do the request if the URL is invalid
@@ -176,7 +176,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
176
176
  })
177
177
 
178
178
  request(data, options, (error) => {
179
- if (error && config.apiKey && config.site) {
179
+ if (error && config.DD_API_KEY && config.site) {
180
180
  if (agentTelemetry) {
181
181
  log.warn('Agent telemetry failed, started agentless telemetry')
182
182
  agentTelemetry = false
@@ -189,7 +189,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
189
189
  log.error('Invalid Telemetry URL')
190
190
  return
191
191
  }
192
- const backendHeader = { ...options.headers, 'DD-API-KEY': config.apiKey }
192
+ const backendHeader = { ...options.headers, 'DD-API-KEY': config.DD_API_KEY }
193
193
  const backendOptions = {
194
194
  ...options,
195
195
  url: backendUrl,
@@ -40,7 +40,7 @@ function onChildProcessStart (context) {
40
40
  }
41
41
 
42
42
  function start (config) {
43
- if (!config.telemetry?.enabled || subscribed) return
43
+ if (!config.telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED || subscribed) return
44
44
  subscribed = true
45
45
 
46
46
  runtimeId = config.DD_ROOT_JS_SESSION_ID || config.tags['runtime-id']