dd-trace 6.9.0 → 6.11.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 (175) hide show
  1. package/README.md +0 -17
  2. package/ci/vitest-no-worker-init-setup.mjs +112 -10
  3. package/index.d.ts +150 -3
  4. package/initialize.mjs +28 -17
  5. package/openfeature.js +2 -2
  6. package/package.json +14 -10
  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/confluentinc-kafka-javascript.js +111 -6
  13. package/packages/datadog-instrumentations/src/cucumber.js +26 -11
  14. package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
  15. package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
  16. package/packages/datadog-instrumentations/src/cypress.js +2 -0
  17. package/packages/datadog-instrumentations/src/fastify.js +2 -0
  18. package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
  20. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +634 -0
  21. package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +81 -0
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
  26. package/packages/datadog-instrumentations/src/jest.js +26 -18
  27. package/packages/datadog-instrumentations/src/mariadb.js +278 -21
  28. package/packages/datadog-instrumentations/src/mocha/main.js +540 -56
  29. package/packages/datadog-instrumentations/src/mocha/utils.js +132 -25
  30. package/packages/datadog-instrumentations/src/mysql.js +45 -15
  31. package/packages/datadog-instrumentations/src/mysql2.js +119 -18
  32. package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
  33. package/packages/datadog-instrumentations/src/pg.js +116 -92
  34. package/packages/datadog-instrumentations/src/playwright-reporter.js +99 -0
  35. package/packages/datadog-instrumentations/src/playwright.js +149 -2
  36. package/packages/datadog-instrumentations/src/router.js +2 -0
  37. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -4
  38. package/packages/datadog-instrumentations/src/vitest-main.js +180 -20
  39. package/packages/datadog-instrumentations/src/vitest-util.js +64 -5
  40. package/packages/datadog-instrumentations/src/vitest-worker.js +150 -25
  41. package/packages/datadog-instrumentations/src/webdriverio.js +31 -17
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
  43. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
  44. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
  45. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -13
  46. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
  47. package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
  48. package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
  49. package/packages/datadog-plugin-bunyan/src/index.js +5 -8
  50. package/packages/datadog-plugin-cucumber/src/index.js +11 -0
  51. package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
  52. package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
  53. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +82 -30
  54. package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
  55. package/packages/datadog-plugin-cypress/src/index.js +12 -6
  56. package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
  57. package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
  58. package/packages/datadog-plugin-electron/src/index.js +2 -0
  59. package/packages/datadog-plugin-fs/src/index.js +1 -1
  60. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
  61. package/packages/datadog-plugin-jest/src/index.js +11 -4
  62. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
  63. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
  64. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -2
  65. package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
  66. package/packages/datadog-plugin-mocha/src/index.js +606 -40
  67. package/packages/datadog-plugin-mysql/src/index.js +37 -0
  68. package/packages/datadog-plugin-nats/src/index.js +1 -1
  69. package/packages/datadog-plugin-next/src/index.js +1 -19
  70. package/packages/datadog-plugin-openai/src/tracing.js +41 -30
  71. package/packages/datadog-plugin-openai-agents/src/integration.js +279 -69
  72. package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
  73. package/packages/datadog-plugin-pg/src/index.js +1 -0
  74. package/packages/datadog-plugin-playwright/src/index.js +42 -13
  75. package/packages/datadog-plugin-vitest/src/index.js +14 -20
  76. package/packages/datadog-plugin-ws/src/util.js +2 -1
  77. package/packages/datadog-webpack/index.js +0 -11
  78. package/packages/dd-trace/index.js +0 -1
  79. package/packages/dd-trace/src/agent/info.js +6 -5
  80. package/packages/dd-trace/src/aiguard/client.js +100 -1
  81. package/packages/dd-trace/src/aiguard/errors.js +41 -0
  82. package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
  83. package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
  84. package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
  85. package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
  86. package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
  87. package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
  88. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
  89. package/packages/dd-trace/src/aiguard/sdk.js +22 -278
  90. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
  91. package/packages/dd-trace/src/appsec/handlers/http-request.js +13 -0
  92. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
  93. package/packages/dd-trace/src/carrier.js +356 -0
  94. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
  95. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +27 -3
  96. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +22 -6
  97. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
  98. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
  99. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +22 -6
  100. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
  101. package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
  102. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
  103. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
  104. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
  105. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
  106. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
  107. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +6 -2
  108. package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -0
  109. package/packages/dd-trace/src/config/index.js +13 -2
  110. package/packages/dd-trace/src/config/major-overrides.js +7 -0
  111. package/packages/dd-trace/src/config/supported-configurations.json +24 -0
  112. package/packages/dd-trace/src/datastreams/context.js +6 -1
  113. package/packages/dd-trace/src/datastreams/index.js +2 -0
  114. package/packages/dd-trace/src/datastreams/manager.js +14 -3
  115. package/packages/dd-trace/src/datastreams/pathway.js +23 -22
  116. package/packages/dd-trace/src/datastreams/processor.js +16 -13
  117. package/packages/dd-trace/src/datastreams/size.js +5 -0
  118. package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -2
  119. package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
  120. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
  121. package/packages/dd-trace/src/exporters/common/writer.js +11 -6
  122. package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
  123. package/packages/dd-trace/src/llmobs/experiments/client.js +163 -5
  124. package/packages/dd-trace/src/llmobs/experiments/dataset.js +241 -87
  125. package/packages/dd-trace/src/llmobs/experiments/experiment.js +280 -20
  126. package/packages/dd-trace/src/llmobs/experiments/index.js +58 -59
  127. package/packages/dd-trace/src/llmobs/experiments/noop.js +71 -3
  128. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  129. package/packages/dd-trace/src/llmobs/experiments/util.js +41 -5
  130. package/packages/dd-trace/src/llmobs/index.js +34 -2
  131. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
  132. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
  133. package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
  134. package/packages/dd-trace/src/llmobs/tagger.js +96 -0
  135. package/packages/dd-trace/src/llmobs/util.js +78 -0
  136. package/packages/dd-trace/src/noop/proxy.js +3 -4
  137. package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
  138. package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
  139. package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
  140. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
  141. package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
  142. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
  143. package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
  144. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
  145. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
  146. package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
  147. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
  148. package/packages/dd-trace/src/opentracing/tracer.js +5 -2
  149. package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
  150. package/packages/dd-trace/src/plugin_manager.js +5 -3
  151. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
  152. package/packages/dd-trace/src/plugins/index.js +1 -0
  153. package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +5 -65
  154. package/packages/dd-trace/src/plugins/util/test.js +93 -14
  155. package/packages/dd-trace/src/plugins/util/web.js +56 -2
  156. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  157. package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
  158. package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
  159. package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
  160. package/packages/dd-trace/src/proxy.js +38 -29
  161. package/packages/dd-trace/src/ritm.js +9 -2
  162. package/packages/dd-trace/src/serverless.js +37 -0
  163. package/packages/dd-trace/src/span_format.js +1 -0
  164. package/packages/dd-trace/src/span_stats.js +30 -3
  165. package/packages/dd-trace/src/storage-channels.js +86 -0
  166. package/packages/dd-trace/src/tracer.js +9 -2
  167. package/packages/dd-trace/src/tracer_metadata.js +14 -1
  168. package/packages/dd-trace/src/web-tags-cache.js +132 -0
  169. package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
  170. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
  171. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
  172. package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
  173. package/packages/dd-trace/src/feature-registry.js +0 -29
  174. package/packages/dd-trace/src/openfeature/register.js +0 -35
  175. package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
@@ -1,35 +1,90 @@
1
1
  'use strict'
2
+
3
+ const { DsmPathwayCodec, PATHWAY_FIELD_BYTES } = require('../../../dd-trace/src/datastreams')
2
4
  const log = require('../../../dd-trace/src/log')
3
5
  const BaseAwsSdkPlugin = require('../base')
4
6
 
7
+ const DEFAULT_EVENT_BUS = 'default'
8
+ const DEFAULT_DETAIL_TYPE = 'unknown'
5
9
  // EventBridge enforces this limit over the whole PutEvents request (the sum of
6
- // every entry), not over a single entry. 1 MiB == 1,048,576 bytes.
10
+ // every entry), not over a single entry.
7
11
  const MAX_PUT_EVENTS_BYTES = 1024 * 1024
12
+ // Sizing runs before any checkpoint exists, so entries that carry nothing but a pathway are
13
+ // measured against this stand-in.
14
+ const PATHWAY_SIZE_PROBE = { hash: Buffer.alloc(8), pathwayStartNs: 0, edgeStartNs: 0 }
15
+
16
+ /**
17
+ * @typedef {object} PutEventsRequestEntry
18
+ * @property {string} [Detail]
19
+ * @property {string} [DetailType]
20
+ * @property {string} [EventBusName]
21
+ * @property {string[]} [Resources]
22
+ * @property {string} [Source]
23
+ * @property {Date} [Time]
24
+ * @property {string} [TraceHeader]
25
+ */
26
+
27
+ /**
28
+ * Size a single entry the way DSM reports payloads across tracers: the UTF-8 byte length of every
29
+ * string field the caller supplied. `Time` is excluded because it is not part of the message the
30
+ * consumer receives, which is also why this differs from `putEventEntrySize`.
31
+ *
32
+ * @param {PutEventsRequestEntry} entry whose `Detail` the caller has already checked is a string.
33
+ * @returns {number}
34
+ */
35
+ function dsmPayloadSize (entry) {
36
+ let size = Buffer.byteLength(entry.Detail)
37
+ if (typeof entry.DetailType === 'string') size += Buffer.byteLength(entry.DetailType)
38
+ if (typeof entry.EventBusName === 'string') size += Buffer.byteLength(entry.EventBusName)
39
+ if (typeof entry.Source === 'string') size += Buffer.byteLength(entry.Source)
40
+ if (typeof entry.TraceHeader === 'string') size += Buffer.byteLength(entry.TraceHeader)
41
+ if (Array.isArray(entry.Resources)) {
42
+ for (const resource of entry.Resources) {
43
+ if (typeof resource === 'string') size += Buffer.byteLength(resource)
44
+ }
45
+ }
46
+ return size
47
+ }
8
48
 
9
49
  /**
10
50
  * Size a single `PutEventsRequestEntry` the way EventBridge does server-side:
11
51
  * the UTF-8 byte length of `Source`, `DetailType`, `Detail`, and each
12
52
  * `Resources` ARN, plus a flat 14 bytes when `Time` is set.
13
53
  *
14
- * @param {object} entry a single PutEvents request entry
54
+ * @param {PutEventsRequestEntry} entry
15
55
  * @param {string} [detail] overrides `entry.Detail`, used to size the entry as
16
56
  * it would be sent with the injected `_datadog` context
17
- * @returns {number}
18
57
  * @see https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-putevents.html
19
58
  */
20
- function putEventEntrySize (entry, detail = entry.Detail) {
59
+ function putEventEntrySize (entry, detail) {
60
+ if (entry == null) return 0
61
+
62
+ detail ??= entry.Detail
21
63
  let size = entry.Time == null ? 0 : 14
22
- if (entry.Source != null) size += Buffer.byteLength(entry.Source)
23
- if (entry.DetailType != null) size += Buffer.byteLength(entry.DetailType)
24
- if (detail != null) size += Buffer.byteLength(detail)
25
- if (entry.Resources != null) {
64
+ if (typeof entry.Source === 'string') size += Buffer.byteLength(entry.Source)
65
+ if (typeof entry.DetailType === 'string') size += Buffer.byteLength(entry.DetailType)
66
+ if (typeof detail === 'string') size += Buffer.byteLength(detail)
67
+ if (Array.isArray(entry.Resources)) {
26
68
  for (const resource of entry.Resources) {
27
- if (resource != null) size += Buffer.byteLength(resource)
69
+ if (typeof resource === 'string') size += Buffer.byteLength(resource)
28
70
  }
29
71
  }
30
72
  return size
31
73
  }
32
74
 
75
+ /**
76
+ * @param {string} detail
77
+ * @param {Record<string, string>} carrier
78
+ * @returns {string|undefined} `undefined` when `detail` is not a JSON object
79
+ */
80
+ function injectDetail (detail, carrier) {
81
+ try {
82
+ return BaseAwsSdkPlugin.injectFieldIntoJsonObject(detail, '_datadog', carrier)
83
+ } catch (error) {
84
+ log.error('EventBridge error injecting request', error)
85
+ }
86
+ }
87
+
33
88
  class EventBridge extends BaseAwsSdkPlugin {
34
89
  static id = 'eventbridge'
35
90
  static isPayloadReporter = true
@@ -46,44 +101,31 @@ class EventBridge extends BaseAwsSdkPlugin {
46
101
  }
47
102
 
48
103
  /**
49
- * requestInject
50
- * @param {import('../../../..').Span} span
51
- * @param {object} request
104
+ * The context goes into `Detail` because EventBridge reserves an entry's `TraceHeader` for X-Ray.
52
105
  *
53
- * Docs: https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEventsRequestEntry.html
54
- * We cannot use the traceHeader field as that's reserved for X-Ray.
55
- * Detail must be a valid JSON string
56
- * Max PutEvents request size is 1mb, summed over all entries
57
- * (https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-putevent-size.html)
106
+ * @param {import('../../../..').Span} span
107
+ * @param {{ operation: string, params?: { Entries?: PutEventsRequestEntry[] } }} request
108
+ * @see https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEventsRequestEntry.html
109
+ * @see https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-putevent-size.html
58
110
  */
59
111
  requestInject (span, request) {
60
- const operation = request.operation
61
- if (operation === 'putEvents' &&
62
- request.params &&
63
- request.params.Entries &&
64
- request.params.Entries.length > 0 &&
65
- request.params.Entries[0].Detail) {
66
- const entries = request.params.Entries
67
- const injected = {}
68
- this.tracer.inject(span, 'text_map', injected)
69
-
70
- // Only `injectFieldIntoJsonObject` can throw (the slow path
71
- // `JSON.parse` for non-`{...}` payloads). Tighten the catch around
72
- // it so the rest of the body stays in V8's optimisable surface.
73
- let finalData
74
- try {
75
- finalData = BaseAwsSdkPlugin.injectFieldIntoJsonObject(entries[0].Detail, '_datadog', injected)
76
- } catch (error) {
77
- log.error('EventBridge error injecting request', error)
78
- return
79
- }
112
+ const { operation, params } = request
113
+ if (operation !== 'putEvents' || !params?.Entries?.length) return
114
+
115
+ const entries = params.Entries
116
+ const dsmEnabled = this.config.dsmEnabled
117
+ const batchPropagationEnabled = this.config.batchPropagationEnabled
118
+ const traceCarrier = this.tracer.inject(span, 'text_map')
119
+ if (traceCarrier === undefined && !dsmEnabled) return
80
120
 
81
- // EventBridge applies the 1 MiB cap to the whole request, so size every
82
- // entry as it would be sent (the first with `_datadog` injected) and skip
83
- // rather than tip a request AWS would otherwise accept over the limit. The
84
- // running total only needs to clear the cap, so stop summing the moment it
85
- // does instead of byte-counting the rest of a batch we already know is over.
86
- let requestSize = putEventEntrySize(entries[0], finalData)
121
+ // The default configuration injects the first entry alone and records no checkpoint, so it
122
+ // needs none of the deferred-write bookkeeping the paths below depend on.
123
+ if (!dsmEnabled && !batchPropagationEnabled) {
124
+ const entry = entries[0]
125
+ if (typeof entry?.Detail !== 'string') return
126
+ const detail = injectDetail(entry.Detail, traceCarrier)
127
+ if (detail === undefined) return
128
+ let requestSize = putEventEntrySize(entry, detail)
87
129
  for (let i = 1; requestSize < MAX_PUT_EVENTS_BYTES && i < entries.length; i++) {
88
130
  requestSize += putEventEntrySize(entries[i])
89
131
  }
@@ -91,8 +133,85 @@ class EventBridge extends BaseAwsSdkPlugin {
91
133
  log.info('Payload size too large to pass context')
92
134
  return
93
135
  }
94
- entries[0].Detail = finalData
136
+ entry.Detail = detail
137
+ return
95
138
  }
139
+
140
+ // Both carriers are reused across entries; the write pass overwrites the pathway on each one
141
+ // immediately before serializing it, so nothing may come between the two.
142
+ let pathwayOnlyCarrier
143
+ const injectable = []
144
+ let requestSize = 0
145
+
146
+ for (let i = 0; i < entries.length && requestSize < MAX_PUT_EVENTS_BYTES; i++) {
147
+ const entry = entries[i]
148
+ const tracePropagated = traceCarrier !== undefined && (i === 0 || batchPropagationEnabled)
149
+ let carrier = traceCarrier
150
+ if (!tracePropagated) {
151
+ if (dsmEnabled) pathwayOnlyCarrier ??= DsmPathwayCodec.encode(PATHWAY_SIZE_PROBE)
152
+ carrier = pathwayOnlyCarrier
153
+ }
154
+ if (carrier !== undefined && typeof entry?.Detail === 'string') {
155
+ // A malformed Detail is skipped rather than fatal: EventBridge partial-fails that entry,
156
+ // so the rest of the batch still propagates.
157
+ const traceOnlyDetail = injectDetail(entry.Detail, carrier)
158
+ if (traceOnlyDetail !== undefined) {
159
+ // The pathway field is a fixed width, so reserving it is exact and saves building the
160
+ // detail a second time just to measure it.
161
+ const reserved = dsmEnabled && tracePropagated ? PATHWAY_FIELD_BYTES : 0
162
+ requestSize += putEventEntrySize(entry, traceOnlyDetail) + reserved
163
+ injectable.push({
164
+ entry,
165
+ carrier,
166
+ payloadSize: dsmEnabled ? dsmPayloadSize(entry) : 0,
167
+ traceOnlyDetail: tracePropagated ? traceOnlyDetail : undefined,
168
+ })
169
+ continue
170
+ }
171
+ }
172
+ requestSize += putEventEntrySize(entry)
173
+ }
174
+
175
+ if (injectable.length === 0) return
176
+ if (requestSize >= MAX_PUT_EVENTS_BYTES) {
177
+ log.info('Payload size too large to pass context')
178
+ return
179
+ }
180
+
181
+ for (const { entry, carrier, payloadSize, traceOnlyDetail } of injectable) {
182
+ let detail = traceOnlyDetail
183
+ if (dsmEnabled) {
184
+ const dataStreamsContext = this.setDSMCheckpoint(span, entry, payloadSize)
185
+ // Anything short of a real pathway leaves the carrier on the sizing stand-in, which must
186
+ // never ship; a pathway-only entry then has no fallback and keeps the caller's detail.
187
+ if (DsmPathwayCodec.encode(dataStreamsContext, carrier) !== undefined) {
188
+ detail = injectDetail(entry.Detail, carrier)
189
+ }
190
+ }
191
+ if (detail !== undefined) entry.Detail = detail
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Edge tags feed the pathway hash, so they are a cross-tracer contract: renaming or merging one
197
+ * splits the DSM pathway instead of extending it.
198
+ *
199
+ * @param {import('../../../..').Span} span
200
+ * @param {PutEventsRequestEntry} entry
201
+ * @param {number} payloadSize `dsmPayloadSize(entry)`, taken before the `_datadog` context is
202
+ * injected. The trailing `0` opts out of the pathway estimate the processor adds by default,
203
+ * because that context is propagation overhead rather than the caller's payload.
204
+ * @returns {object|undefined}
205
+ */
206
+ setDSMCheckpoint (span, entry, payloadSize) {
207
+ const eventBus = entry.EventBusName || DEFAULT_EVENT_BUS
208
+ const detailType = entry.DetailType ?? DEFAULT_DETAIL_TYPE
209
+ return this.tracer.setCheckpoint(
210
+ ['direction:out', `exchange:${eventBus}`, `topic:${detailType}`, 'type:eventbridge'],
211
+ span,
212
+ payloadSize,
213
+ 0,
214
+ )
96
215
  }
97
216
  }
98
217
  module.exports = EventBridge
@@ -1,5 +1,5 @@
1
1
  'use strict'
2
- const { DsmPathwayCodec, getSizeOrZero } = require('../../../dd-trace/src/datastreams')
2
+ const { DsmPathwayCodec, getSizeOrZero, PATHWAY_FIELD_BYTES } = require('../../../dd-trace/src/datastreams')
3
3
  const log = require('../../../dd-trace/src/log')
4
4
  const BaseAwsSdkPlugin = require('../base')
5
5
 
@@ -16,12 +16,6 @@ const MAX_TRACKED_SHARD_ITERATORS = 1000
16
16
  // conservatively skip propagation above this point because the request does not expose that limit.
17
17
  const KINESIS_DEFAULT_MAX_RECORD_BYTES = 1_048_576
18
18
 
19
- // The DSM pathway field (`dd-pathway-ctx-base64`) always serializes to a fixed 55 bytes: a
20
- // 21-char key, a 28-char base64 value, and 6 bytes of JSON framing. Mirrors PATHWAY_HEADER_BYTES
21
- // in dd-trace/src/datastreams/processor.js. Reserved in the size gate so setDSMCheckpoint never
22
- // runs for a record that would ship over the cap once the pathway context is attached.
23
- const DSM_PATHWAY_FIELD_BYTES = 55
24
-
25
19
  class Kinesis extends BaseAwsSdkPlugin {
26
20
  static id = 'kinesis'
27
21
  static peerServicePrecursors = ['streamname']
@@ -196,14 +190,11 @@ class Kinesis extends BaseAwsSdkPlugin {
196
190
  try {
197
191
  parsedAttributes = JSON.parse(recordDataAsString(record.Data))
198
192
  } catch {
199
- // Non-JSON record. Skip DSM context for this entry; the
200
- // checkpoint payload size below is still reported.
193
+ // Non-JSON record: it carries no context, so the checkpoint below starts a new pathway.
201
194
  }
202
195
 
203
196
  const payloadSize = getSizeOrZero(record.Data)
204
- if (parsedAttributes?._datadog) {
205
- this.tracer.decodeDataStreamsContext(parsedAttributes._datadog)
206
- }
197
+ this.tracer.decodeDataStreamsContext(parsedAttributes?._datadog)
207
198
  this.tracer.setCheckpoint(tags, span, payloadSize)
208
199
  }
209
200
  }
@@ -279,7 +270,7 @@ class Kinesis extends BaseAwsSdkPlugin {
279
270
  // Gate before setDSMCheckpoint: a record we can't ship must not record a checkpoint.
280
271
  // Reserve the pathway field that DSM appends after the gate.
281
272
  let serialized = JSON.stringify(parsedData)
282
- const reservedBytes = dsmEnabled ? DSM_PATHWAY_FIELD_BYTES : 0
273
+ const reservedBytes = dsmEnabled ? PATHWAY_FIELD_BYTES : 0
283
274
  const partitionKeyBytes = Buffer.byteLength(params.PartitionKey ?? '', 'utf8')
284
275
  if (
285
276
  Buffer.byteLength(serialized, 'utf8') + partitionKeyBytes + reservedBytes >
@@ -275,11 +275,7 @@ class Sqs extends BaseAwsSdkPlugin {
275
275
  for (let i = 0; i < messages.length; i++) {
276
276
  const message = messages[i]
277
277
  const carrier = carriers === undefined ? this.parseMessageCarrier(message) : carriers[i]
278
- if (carrier) {
279
- // Inert for EventBridge until its producer emits a pathway (separate
280
- // change) — no `dd-pathway-ctx-base64` to decode yet; SQS/SNS decode now.
281
- this.tracer.decodeDataStreamsContext(carrier)
282
- }
278
+ this.tracer.decodeDataStreamsContext(carrier)
283
279
  const payloadSize = getHeadersSize({
284
280
  Body: message.Body,
285
281
  MessageAttributes: message.MessageAttributes,
@@ -156,7 +156,7 @@ function setSpanLinks (triggerType, tracer, span, ctx) {
156
156
  : triggerMetadata.propertiesArray
157
157
 
158
158
  const addLinkFromProperties = (props) => {
159
- if (!props || Object.keys(props).length === 0) return
159
+ if (!props) return
160
160
  const spanContext = tracer.extract('text_map', props)
161
161
  if (spanContext) {
162
162
  span.addLink({ context: spanContext })
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ const BunyanPlugin = require('../../datadog-plugin-bunyan/src')
4
+
5
+ class BrowserBunyanPlugin extends BunyanPlugin {
6
+ static id = 'browser-bunyan'
7
+ }
8
+
9
+ module.exports = BrowserBunyanPlugin
@@ -8,17 +8,14 @@ class BunyanPlugin extends LogPlugin {
8
8
 
9
9
  constructor (...args) {
10
10
  super(...args)
11
- this.addSub('apm:bunyan:log', (arg) => this.handleLog(arg))
11
+ this.addSub(`apm:${this.constructor.id}:log`, (arg) => this.handleLog(arg))
12
12
  }
13
13
 
14
14
  /**
15
- * Inject `dd` directly on the record bunyan hands us. bunyan builds the
16
- * record inside `mkRecord` via `objCopy(log.fields)` and then copies the
17
- * caller's fields onto the result, so the `rec` object that flows
18
- * through `_emit` is always bunyan-owned, has `Object.prototype` for its
19
- * prototype, and is never the caller's input directly. Mutating it adds
20
- * `dd` for every consumer (JSON streams via `JSON.stringify(rec)`, raw
21
- * streams via the record reference) without paying for a Proxy view.
15
+ * Inject `dd` directly on the record Bunyan implementations hand us. They build the record
16
+ * inside `mkRecord` from a copy of the logger and caller fields, so the `rec` object that flows
17
+ * through `_emit` is always logger-owned. Mutating it adds `dd` for every consumer without
18
+ * paying for a Proxy view.
22
19
  *
23
20
  * @param {{ message: object }} arg
24
21
  */
@@ -72,6 +72,7 @@ class CucumberPlugin extends CiPlugin {
72
72
  isEarlyFlakeDetectionFaulty,
73
73
  isTestManagementTestsEnabled,
74
74
  isParallel,
75
+ error,
75
76
  onDone,
76
77
  }) => {
77
78
  this._exportPendingWorkerTraces()
@@ -115,6 +116,15 @@ class CucumberPlugin extends CiPlugin {
115
116
 
116
117
  this.testSessionSpan.setTag(TEST_STATUS, status)
117
118
  this.testModuleSpan.setTag(TEST_STATUS, status)
119
+ if (error) {
120
+ for (const testSuiteSpan of this._testSuiteSpansByTestSuite.values()) {
121
+ testSuiteSpan.setTag(TEST_STATUS, 'fail')
122
+ testSuiteSpan.setTag('error', error)
123
+ }
124
+ this.testSessionSpan.setTag('error', error)
125
+ this.testModuleSpan.setTag('error', error)
126
+ }
127
+ this.tracer._exporter.exportDeferredTestSuiteSpans?.()
118
128
  this.testModuleSpan.finish()
119
129
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
120
130
  this.testSessionSpan.finish()
@@ -192,6 +202,7 @@ class CucumberPlugin extends CiPlugin {
192
202
  this.addSub('ci:cucumber:test-suite:finish', ({ status, testSuitePath }) => {
193
203
  const testSuiteSpan = this._testSuiteSpansByTestSuite.get(testSuitePath)
194
204
  testSuiteSpan.setTag(TEST_STATUS, status)
205
+ this.tracer._exporter.deferTestSuiteSpan?.(testSuiteSpan)
195
206
  testSuiteSpan.finish()
196
207
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
197
208
  })
@@ -1,5 +1,9 @@
1
1
  'use strict'
2
2
 
3
3
  const cypressPlugin = require('./cypress-plugin')
4
+ const { shouldDeferLegacyFinalization } = require('./finalization')
4
5
 
5
- module.exports = cypressPlugin.afterRun.bind(cypressPlugin)
6
+ module.exports = function afterRun () {
7
+ if (shouldDeferLegacyFinalization()) return
8
+ return cypressPlugin.afterRun.apply(cypressPlugin, arguments)
9
+ }
@@ -1,5 +1,9 @@
1
1
  'use strict'
2
2
 
3
3
  const cypressPlugin = require('./cypress-plugin')
4
+ const { shouldDeferLegacyFinalization } = require('./finalization')
4
5
 
5
- module.exports = cypressPlugin.afterSpec.bind(cypressPlugin)
6
+ module.exports = function afterSpec () {
7
+ if (shouldDeferLegacyFinalization()) return
8
+ return cypressPlugin.afterSpec.apply(cypressPlugin, arguments)
9
+ }