dd-trace 6.13.0 → 6.14.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 (201) hide show
  1. package/LICENSE-3rdparty.csv +3 -1
  2. package/ci/init.js +8 -3
  3. package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
  4. package/ci/test-optimization-validation/ci-remediation.js +6 -6
  5. package/ci/test-optimization-validation/command-blocker.js +7 -5
  6. package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
  7. package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
  8. package/ci/test-optimization-validation/package-check.js +1 -1
  9. package/ci/test-optimization-validation/report-writer.js +7 -5
  10. package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
  11. package/ci/vitest-no-worker-init-setup.mjs +13 -13
  12. package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
  13. package/package.json +13 -10
  14. package/packages/datadog-esbuild/index.js +3 -1
  15. package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
  16. package/packages/datadog-instrumentations/src/fs.js +24 -20
  17. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
  18. package/packages/datadog-instrumentations/src/graphql.js +16 -16
  19. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
  20. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  21. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +265 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +251 -0
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +11 -1
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
  33. package/packages/datadog-instrumentations/src/jest.js +25 -20
  34. package/packages/datadog-instrumentations/src/knex.js +76 -0
  35. package/packages/datadog-instrumentations/src/mariadb.js +6 -0
  36. package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
  37. package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
  38. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  39. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
  40. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
  41. package/packages/datadog-instrumentations/src/net.js +14 -17
  42. package/packages/datadog-instrumentations/src/next.js +212 -62
  43. package/packages/datadog-instrumentations/src/openai.js +42 -40
  44. package/packages/datadog-instrumentations/src/pg.js +15 -0
  45. package/packages/datadog-instrumentations/src/playwright.js +109 -13
  46. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
  47. package/packages/datadog-instrumentations/src/sequelize.js +94 -2
  48. package/packages/datadog-instrumentations/src/undici.js +53 -4
  49. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +37 -3
  50. package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
  51. package/packages/datadog-instrumentations/src/vitest-worker.js +71 -14
  52. package/packages/datadog-instrumentations/src/webdriverio.js +965 -6
  53. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  54. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  55. package/packages/datadog-plugin-amqp10/src/util.js +2 -1
  56. package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
  57. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
  58. package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
  59. package/packages/datadog-plugin-cucumber/src/index.js +0 -2
  60. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
  61. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
  62. package/packages/datadog-plugin-graphql/src/execute.js +998 -196
  63. package/packages/datadog-plugin-graphql/src/index.js +27 -7
  64. package/packages/datadog-plugin-graphql/src/jit.js +136 -0
  65. package/packages/datadog-plugin-graphql/src/request.js +18 -27
  66. package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
  67. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
  68. package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
  69. package/packages/datadog-plugin-graphql/src/utils.js +208 -120
  70. package/packages/datadog-plugin-graphql/src/validate.js +10 -23
  71. package/packages/datadog-plugin-jest/src/index.js +0 -6
  72. package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
  73. package/packages/datadog-plugin-mocha/src/index.js +403 -75
  74. package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
  75. package/packages/datadog-plugin-mysql/src/index.js +41 -15
  76. package/packages/datadog-plugin-net/src/tcp.js +3 -1
  77. package/packages/datadog-plugin-next/src/index.js +63 -29
  78. package/packages/datadog-plugin-playwright/src/index.js +167 -27
  79. package/packages/datadog-plugin-undici/src/index.js +27 -41
  80. package/packages/datadog-plugin-vitest/src/index.js +6 -12
  81. package/packages/dd-trace/src/aiguard/evaluation.js +2 -1
  82. package/packages/dd-trace/src/aiguard/messages/anthropic.js +8 -1
  83. package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
  84. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
  85. package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
  86. package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
  87. package/packages/dd-trace/src/appsec/graphql.js +6 -2
  88. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  89. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
  90. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
  91. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
  92. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
  93. package/packages/dd-trace/src/appsec/lambda.js +44 -26
  94. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
  95. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
  96. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
  97. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
  98. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +5 -173
  99. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
  100. package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
  101. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
  102. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +394 -225
  103. package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
  104. package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
  105. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
  106. package/packages/dd-trace/src/ci-visibility/final-flush.js +2 -1
  107. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
  108. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
  109. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
  110. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
  111. package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
  112. package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
  113. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
  114. package/packages/dd-trace/src/config/index.js +73 -13
  115. package/packages/dd-trace/src/config/parsers.js +11 -1
  116. package/packages/dd-trace/src/config/stable.js +3 -3
  117. package/packages/dd-trace/src/config/supported-configurations.json +20 -2
  118. package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
  119. package/packages/dd-trace/src/debugger/config.js +12 -1
  120. package/packages/dd-trace/src/debugger/constants.js +1 -0
  121. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
  122. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
  123. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -40
  124. package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
  125. package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
  126. package/packages/dd-trace/src/debugger/index.js +23 -3
  127. package/packages/dd-trace/src/dogstatsd.js +510 -76
  128. package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
  129. package/packages/dd-trace/src/encode/0.4.js +5 -5
  130. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
  131. package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
  132. package/packages/dd-trace/src/exporters/agent/index.js +2 -1
  133. package/packages/dd-trace/src/exporters/agent/writer.js +24 -7
  134. package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
  135. package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
  136. package/packages/dd-trace/src/exporters/common/agents.js +28 -6
  137. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
  138. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
  139. package/packages/dd-trace/src/exporters/common/request.js +31 -11
  140. package/packages/dd-trace/src/exporters/common/url.js +40 -1
  141. package/packages/dd-trace/src/exporters/common/writer.js +9 -7
  142. package/packages/dd-trace/src/flush.js +6 -3
  143. package/packages/dd-trace/src/id.js +14 -0
  144. package/packages/dd-trace/src/index.js +5 -2
  145. package/packages/dd-trace/src/llmobs/experiments/dataset.js +2 -1
  146. package/packages/dd-trace/src/llmobs/index.js +32 -32
  147. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
  148. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
  149. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
  150. package/packages/dd-trace/src/llmobs/util.js +0 -33
  151. package/packages/dd-trace/src/llmobs/writers/base.js +49 -32
  152. package/packages/dd-trace/src/log/index.js +7 -1
  153. package/packages/dd-trace/src/{ci-visibility/log-submission → log-submission}/log-submission-plugin.js +48 -19
  154. package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
  155. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  156. package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -24
  157. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
  158. package/packages/dd-trace/src/opentelemetry/metrics/index.js +15 -22
  159. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +14 -4
  160. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +10 -0
  161. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
  162. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
  163. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  164. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
  165. package/packages/dd-trace/src/opentracing/propagation/text_map.js +93 -18
  166. package/packages/dd-trace/src/payload-tagging/index.js +2 -1
  167. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
  168. package/packages/dd-trace/src/plugins/tracing.js +12 -10
  169. package/packages/dd-trace/src/plugins/util/ci.js +3 -1
  170. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
  171. package/packages/dd-trace/src/plugins/util/test.js +83 -20
  172. package/packages/dd-trace/src/plugins/util/url.js +2 -2
  173. package/packages/dd-trace/src/plugins/util/web.js +4 -9
  174. package/packages/dd-trace/src/profiling/config.js +1 -0
  175. package/packages/dd-trace/src/profiling/oom.js +7 -2
  176. package/packages/dd-trace/src/profiling/profiler.js +23 -13
  177. package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
  178. package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
  179. package/packages/dd-trace/src/proxy.js +75 -5
  180. package/packages/dd-trace/src/remote_config/index.js +319 -101
  181. package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
  182. package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
  183. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -1
  184. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -1
  185. package/packages/dd-trace/src/sampling_rule.js +36 -9
  186. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +4 -8
  187. package/packages/dd-trace/src/serverless/vercel.js +53 -20
  188. package/packages/dd-trace/src/serverless.js +10 -3
  189. package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
  190. package/packages/dd-trace/src/span_stats.js +24 -28
  191. package/packages/dd-trace/src/startup-log.js +3 -2
  192. package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
  193. package/packages/dd-trace/src/telemetry/send-data.js +18 -16
  194. package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
  195. package/packages/dd-trace/src/tracer.js +4 -5
  196. package/packages/dd-trace/src/tracer_metadata.js +11 -3
  197. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  198. package/vendor/dist/protobufjs/index.js +1 -1
  199. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  200. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
  201. package/packages/dd-trace/src/encode/agentless-json.js +0 -210
@@ -17,13 +17,20 @@ const {
17
17
  } = require('../constants/writers')
18
18
  const { parseResponseAndLog } = require('./util')
19
19
 
20
+ const MAX_BUFFER_EVENTS = 1000
21
+
20
22
  class LLMObsBuffer {
21
- constructor ({ events, size, routing = {}, isDefault = false, limit = 1000 }) {
23
+ /**
24
+ * @param {{
25
+ * events: Record<string, unknown>[],
26
+ * size: number,
27
+ * routing?: { apiKey?: string, site?: string }
28
+ * }} options
29
+ */
30
+ constructor ({ events, size, routing = {} }) {
22
31
  this.events = events
23
32
  this.size = size
24
33
  this.routing = routing
25
- this.isDefault = isDefault
26
- this.limit = limit
27
34
  }
28
35
 
29
36
  clear () {
@@ -46,7 +53,7 @@ class BaseLLMObsWriter {
46
53
  this._eventType = eventType
47
54
 
48
55
  /** @type {LLMObsBuffer} */
49
- this._buffer = new LLMObsBuffer({ events: [], size: 0, isDefault: true })
56
+ this._buffer = new LLMObsBuffer({ events: [], size: 0 })
50
57
 
51
58
  /** @type {import('../../config/config-base')} */
52
59
  this._config = config
@@ -102,8 +109,8 @@ class BaseLLMObsWriter {
102
109
  append (event, routing, byteLength) {
103
110
  const buffer = this._getBuffer(routing)
104
111
 
105
- if (buffer.events.length >= buffer.limit) {
106
- logger.warn(`${this.constructor.name} event buffer full (limit is ${buffer.limit}), dropping event`)
112
+ if (buffer.events.length >= MAX_BUFFER_EVENTS) {
113
+ logger.warn(`${this.constructor.name} event buffer full (limit is ${MAX_BUFFER_EVENTS}), dropping event`)
107
114
  telemetry.recordDroppedPayload(1, this._eventType, 'buffer_full')
108
115
  return false
109
116
  }
@@ -158,30 +165,48 @@ class BaseLLMObsWriter {
158
165
  }
159
166
 
160
167
  for (const [apiKey, buffer] of this.#multiTenantBuffers) {
161
- if (buffer.events.length === 0) continue
162
- const events = buffer.events
163
- buffer.clear()
168
+ if (buffer.events.length === 0) {
169
+ this.#multiTenantBuffers.delete(apiKey)
170
+ continue
171
+ }
164
172
  const site = buffer.routing.site || this._config.site
165
- const options = {
166
- headers: {
167
- 'Content-Type': 'application/json',
168
- 'DD-API-KEY': apiKey,
169
- },
170
- method: 'POST',
171
- timeout: this._timeout,
172
- url: new URL(format({
173
- protocol: 'https:',
174
- hostname: `${this._intake}.${site}`,
175
- })),
176
- path: this._baseEndpoint,
173
+ const maskedApiKey = `****${apiKey.slice(-4)}`
174
+ let options
175
+ let url
176
+ try {
177
+ options = {
178
+ headers: {
179
+ 'Content-Type': 'application/json',
180
+ 'DD-API-KEY': apiKey,
181
+ },
182
+ method: 'POST',
183
+ timeout: this._timeout,
184
+ url: new URL(format({
185
+ protocol: 'https:',
186
+ hostname: `${this._intake}.${site}`,
187
+ })),
188
+ path: this._baseEndpoint,
189
+ }
190
+ url = this.#buildUrl(options.url.href, options.path)
191
+ } catch (error) {
192
+ buffer.clear()
193
+ this.#multiTenantBuffers.delete(apiKey)
194
+ logger.error(
195
+ 'Failed to route LLMObs %s events for API key %s: %s',
196
+ this._eventType,
197
+ maskedApiKey,
198
+ error.message
199
+ )
200
+ continue
177
201
  }
178
- const url = this.#buildUrl(options.url.href, options.path)
179
- const maskedApiKey = apiKey ? `****${apiKey.slice(-4)}` : ''
202
+
203
+ const events = buffer.events
204
+ buffer.clear()
205
+ this.#multiTenantBuffers.delete(apiKey)
180
206
  log.debug('Encoding and flushing multi-tenant buffer for %s', maskedApiKey)
181
207
  requests.push({ events, options, url })
182
208
  }
183
209
 
184
- this.#cleanupEmptyBuffers()
185
210
  return requests
186
211
  }
187
212
 
@@ -194,14 +219,6 @@ class BaseLLMObsWriter {
194
219
  })
195
220
  }
196
221
 
197
- #cleanupEmptyBuffers () {
198
- for (const [key, buffer] of this.#multiTenantBuffers) {
199
- if (buffer.events.length === 0) {
200
- this.#multiTenantBuffers.delete(key)
201
- }
202
- }
203
- }
204
-
205
222
  makePayload (events) {}
206
223
 
207
224
  /**
@@ -29,7 +29,13 @@ const log = {
29
29
  const stack = logRecord.stack.split('\n')
30
30
  const fn = stack[1].replace(/^\s+at ([^\s]+) .+/, '$1')
31
31
  const options = { depth: 2, breakLength: Infinity, compact: true, maxArrayLength: Infinity }
32
- const params = args.map(a => inspect(a, options)).join(', ')
32
+ let params = ''
33
+ let isFirstParameter = true
34
+ for (const argument of args) {
35
+ if (!isFirstParameter) params += ', '
36
+ params += inspect(argument, options)
37
+ isFirstParameter = false
38
+ }
33
39
 
34
40
  stack[0] = `Trace: ${fn}(${params})`
35
41
 
@@ -2,19 +2,29 @@
2
2
 
3
3
  const { Writable } = require('node:stream')
4
4
 
5
- const request = require('../../exporters/common/request')
6
- const log = require('../../log')
7
- const Plugin = require('../../plugins/plugin')
8
- const { FinalFlushRequestTracker } = require('../exporters/agentless/request-tracker')
9
- const { FINAL_FLUSH_TIMEOUT } = require('../final-flush')
5
+ const FinalFlushRequestTracker = require('../exporters/common/final-flush-request-tracker')
6
+ const request = require('../exporters/common/request')
7
+ const log = require('../log')
8
+ const Plugin = require('../plugins/plugin')
10
9
 
11
10
  const MAX_BATCH_BYTES = 5 * 1024 * 1024
12
11
  const MAX_BATCH_LOGS = 1000
13
12
  const BATCH_FLUSH_INTERVAL = 1000
13
+ const FINAL_FLUSH_TIMEOUT = 60_000
14
14
 
15
15
  /**
16
- * @param {import('../../config/config-base')} config
17
- * @returns {URL | undefined}
16
+ * @returns {Error & { code: string }}
17
+ */
18
+ function createFinalFlushTimeoutError () {
19
+ return Object.assign(
20
+ new Error('Timed out waiting for automatic log submission to flush'),
21
+ { code: 'ERR_DD_LOG_SUBMISSION_FLUSH_TIMEOUT' }
22
+ )
23
+ }
24
+
25
+ /**
26
+ * @param {import('../config/config-base')} config
27
+ * @returns {URL | void}
18
28
  */
19
29
  function getLogSubmissionUrl (config) {
20
30
  if (config.DD_AGENTLESS_LOG_SUBMISSION_URL) {
@@ -39,7 +49,15 @@ function getLogSubmissionUrl (config) {
39
49
  }
40
50
 
41
51
  /**
42
- * @param {import('../../config/config-base')} config
52
+ * @param {unknown} config
53
+ * @returns {import('../config/config-base')}
54
+ */
55
+ function asTracerConfig (config) {
56
+ return /** @type {import('../config/config-base')} */ (config)
57
+ }
58
+
59
+ /**
60
+ * @param {import('../config/config-base')} config
43
61
  * @param {string} source
44
62
  * @returns {string}
45
63
  */
@@ -54,6 +72,8 @@ class LogSubmissionPlugin extends Plugin {
54
72
  #batch = []
55
73
  #batchBytes = 2
56
74
  #batchSource
75
+ /** @type {import('../config/config-base') | undefined} */
76
+ #config
57
77
  #logSubmissionUrl
58
78
  #requestTracker
59
79
  #timer
@@ -71,12 +91,19 @@ class LogSubmissionPlugin extends Plugin {
71
91
  },
72
92
  })
73
93
 
74
- constructor (...args) {
75
- super(...args)
76
- this.#requestTracker = new FinalFlushRequestTracker((done) => {
77
- this.#flushLogs()
78
- done?.()
79
- })
94
+ /**
95
+ * @param {object} tracer
96
+ * @param {unknown} tracerConfig
97
+ */
98
+ constructor (tracer, tracerConfig) {
99
+ super(tracer, asTracerConfig(tracerConfig))
100
+ this.#requestTracker = new FinalFlushRequestTracker(
101
+ (done) => {
102
+ this.#flushLogs()
103
+ done?.()
104
+ },
105
+ createFinalFlushTimeoutError
106
+ )
80
107
 
81
108
  // The main-module hook (configure) and the logger.js hook (add-transport) can fire in either
82
109
  // order depending on how Winston is required, so buffer loggers that arrive before configure.
@@ -128,8 +155,9 @@ class LogSubmissionPlugin extends Plugin {
128
155
  if (this._enabled) this.#flushLogs()
129
156
 
130
157
  const isEnabled = typeof config === 'boolean' ? config : config.enabled
131
- this.#logSubmissionUrl = isEnabled && typeof config !== 'boolean'
132
- ? getLogSubmissionUrl(config)
158
+ this.#config = isEnabled && typeof config !== 'boolean' ? asTracerConfig(config) : undefined
159
+ this.#logSubmissionUrl = this.#config
160
+ ? getLogSubmissionUrl(this.#config)
133
161
  : undefined
134
162
  super.configure(config)
135
163
 
@@ -189,7 +217,8 @@ class LogSubmissionPlugin extends Plugin {
189
217
  clearTimeout(this.#timer)
190
218
  this.#timer = undefined
191
219
 
192
- if (this.#batch.length === 0 || !this.#logSubmissionUrl) return
220
+ const config = this.#config
221
+ if (this.#batch.length === 0 || !this.#logSubmissionUrl || !config) return
193
222
 
194
223
  const source = this.#batchSource
195
224
  const data = `[${this.#batch.join(',')}]`
@@ -197,10 +226,10 @@ class LogSubmissionPlugin extends Plugin {
197
226
  this.#batchBytes = 2
198
227
  this.#batchSource = undefined
199
228
  const options = {
200
- path: getLogSubmissionPath(this.config, source),
229
+ path: getLogSubmissionPath(config, source),
201
230
  method: 'POST',
202
231
  headers: {
203
- 'DD-API-KEY': this.config.DD_API_KEY,
232
+ 'DD-API-KEY': config.DD_API_KEY,
204
233
  'Content-Type': 'application/json',
205
234
  },
206
235
  url: this.#logSubmissionUrl,
@@ -214,21 +214,23 @@ class BaseFFEWriter {
214
214
  * @returns {ActiveWriterRoute} Active route state
215
215
  */
216
216
  #createRoute (route) {
217
+ const requestOptions = {
218
+ headers: {
219
+ ...route.headers,
220
+ 'Content-Type': 'application/json',
221
+ },
222
+ method: 'POST',
223
+ retry: true,
224
+ timeout: this._timeout,
225
+ url: route.url,
226
+ path: route.endpoint,
227
+ }
228
+ if (route.agent) requestOptions.agent = route.agent
229
+
217
230
  return {
218
231
  url: route.url,
219
232
  endpoint: route.endpoint,
220
- requestOptions: {
221
- ...(route.agent && { agent: route.agent }),
222
- headers: {
223
- ...route.headers,
224
- 'Content-Type': 'application/json',
225
- },
226
- method: 'POST',
227
- retry: true,
228
- timeout: this._timeout,
229
- url: route.url,
230
- path: route.endpoint,
231
- },
233
+ requestOptions,
232
234
  }
233
235
  }
234
236
 
@@ -75,8 +75,8 @@ function setAgentlessStrategy (config, setWriterEnabledValue) {
75
75
  headers: {
76
76
  [EVP_SUBDOMAIN_HEADER_NAME]: EVP_EVENT_PLATFORM_SUBDOMAIN,
77
77
  },
78
- ...(directRoute && { fallback: directRoute }),
79
78
  }
79
+ if (directRoute) route.fallback = directRoute
80
80
  logger.debug('FFE Writer enabled with local EVP route %s', route.basePath)
81
81
  setWriterEnabledValue(true, route)
82
82
  return
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const os = require('os')
3
+ const { buildResourceAttributes, registerResourceAttributeRefresh } = require('../resource-attributes')
4
4
 
5
5
  /**
6
6
  * @typedef {import('../../config')} Config
@@ -37,35 +37,13 @@ const OtlpHttpLogExporter = require('./otlp_http_log_exporter')
37
37
  * @param {import('../../config/config-base')} config - Tracer configuration instance
38
38
  */
39
39
  function initializeOpenTelemetryLogs (config) {
40
- // Build resource attributes
41
- const resourceAttributes = {
42
- 'service.name': config.service,
43
- 'service.version': config.version,
44
- 'deployment.environment': config.env,
45
- }
46
-
47
- // Add all tracer tags (includes DD_TAGS, OTEL_RESOURCE_ATTRIBUTES, DD_TRACE_TAGS, etc.)
48
- // Exclude Datadog-style keys that duplicate OpenTelemetry standard keys
49
- if (config.tags) {
50
- const filteredTags = { ...config.tags }
51
- delete filteredTags.service
52
- delete filteredTags.version
53
- delete filteredTags.env
54
- Object.assign(resourceAttributes, filteredTags)
55
- }
56
-
57
- // Add host.name if reportHostname is enabled
58
- if (config.reportHostname) {
59
- resourceAttributes['host.name'] = os.hostname()
60
- }
61
-
62
40
  // Create OTLP exporter using resolved config values
63
41
  const exporter = new OtlpHttpLogExporter(
64
42
  config.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
65
43
  config.OTEL_EXPORTER_OTLP_LOGS_HEADERS,
66
44
  config.OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
67
45
  config.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
68
- resourceAttributes
46
+ buildResourceAttributes(config)
69
47
  )
70
48
 
71
49
  // Create batch processor for exporting logs to Datadog Agent
@@ -80,8 +58,11 @@ function initializeOpenTelemetryLogs (config) {
80
58
 
81
59
  // Expose this provider to application calls through the OpenTelemetry Logs API.
82
60
  loggerProvider.register()
61
+
83
62
  // Include final log batches in lifecycle retention with trace delivery.
84
63
  registerTelemetryFlusher(done => loggerProvider.forceFlush(done))
64
+
65
+ registerResourceAttributeRefresh(exporter, () => buildResourceAttributes(config))
85
66
  }
86
67
 
87
68
  module.exports = {
@@ -4,7 +4,7 @@ const OtlpHttpExporterBase = require('../otlp/otlp_http_exporter_base')
4
4
  const OtlpTransformer = require('./otlp_transformer')
5
5
 
6
6
  /**
7
- * @typedef {import('@opentelemetry/resources').Resource} Resource
7
+ * @typedef {import('@opentelemetry/api').Attributes} Attributes
8
8
  * @typedef {import('@opentelemetry/api-logs').LogRecord} LogRecord
9
9
  */
10
10
 
@@ -26,11 +26,21 @@ class OtlpHttpLogExporter extends OtlpHttpExporterBase {
26
26
  * corresponding `OTEL_EXPORTER_OTLP_*_HEADERS` env by the MAP parser.
27
27
  * @param {number} timeout - Request timeout in milliseconds
28
28
  * @param {string} protocol - OTLP protocol (http/protobuf or http/json)
29
- * @param {Resource} resource - Resource attributes
29
+ * @param {Attributes} resourceAttributes - Resource attributes
30
30
  */
31
- constructor (url, headers, timeout, protocol, resource) {
31
+ constructor (url, headers, timeout, protocol, resourceAttributes) {
32
32
  super(url, headers, timeout, protocol, 'logs')
33
- this.transformer = new OtlpTransformer(resource, protocol)
33
+ this.transformer = new OtlpTransformer(resourceAttributes, protocol)
34
+ }
35
+
36
+ /**
37
+ * Recomputes the resource attributes baked into the transformer (e.g. after a MicroVM clone
38
+ * resume regenerates `runtime-id`).
39
+ *
40
+ * @param {Attributes} resourceAttributes - Resource attributes
41
+ */
42
+ updateResourceAttributes (resourceAttributes) {
43
+ this.transformer.updateResourceAttributes(resourceAttributes)
34
44
  }
35
45
 
36
46
  /**
@@ -7,6 +7,10 @@ const { metrics } = require('@opentelemetry/api')
7
7
  const { VERSION } = require('../../../../../version')
8
8
  const processTags = require('../../process-tags')
9
9
  const { registerTelemetryFlusher } = require('../../flush')
10
+ const {
11
+ buildResourceAttributes: buildGeneralResourceAttributes,
12
+ registerResourceAttributeRefresh,
13
+ } = require('../resource-attributes')
10
14
  const MeterProvider = require('./meter_provider')
11
15
  const PeriodicMetricReader = require('./periodic_metric_reader')
12
16
  const OtlpHttpMetricExporter = require('./otlp_http_metric_exporter')
@@ -43,30 +47,12 @@ const RESERVED_TRACER_TAGS = new Set(['service', 'env', 'version', 'runtime_id',
43
47
  * @param {import('../../config/config-base')} config - Tracer configuration instance
44
48
  */
45
49
  function initializeOpenTelemetryMetrics (config) {
46
- const resourceAttributes = {
47
- 'service.name': config.service,
48
- 'service.version': config.version,
49
- 'deployment.environment': config.env,
50
- }
51
-
52
- if (config.tags) {
53
- const filteredTags = { ...config.tags }
54
- delete filteredTags.service
55
- delete filteredTags.version
56
- delete filteredTags.env
57
- Object.assign(resourceAttributes, filteredTags)
58
- }
59
-
60
- if (config.reportHostname) {
61
- resourceAttributes['host.name'] = os.hostname()
62
- }
63
-
64
50
  const exporter = new OtlpHttpMetricExporter(
65
51
  config.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
66
52
  config.OTEL_EXPORTER_OTLP_METRICS_HEADERS,
67
53
  config.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
68
54
  config.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
69
- resourceAttributes
55
+ buildGeneralResourceAttributes(config)
70
56
  )
71
57
 
72
58
  const reader = new PeriodicMetricReader(
@@ -78,8 +64,11 @@ function initializeOpenTelemetryMetrics (config) {
78
64
 
79
65
  const meterProvider = new MeterProvider({ reader })
80
66
  metrics.setGlobalMeterProvider(meterProvider)
67
+
81
68
  // Include the final metric collection and export in lifecycle retention.
82
69
  registerTelemetryFlusher(done => meterProvider.forceFlush(done))
70
+
71
+ registerResourceAttributeRefresh(exporter, () => buildGeneralResourceAttributes(config))
83
72
  }
84
73
 
85
74
  /**
@@ -122,19 +111,23 @@ function buildResourceAttributes (tags, { reportHostname, service, env, serviceV
122
111
  function createOtlpSpanStatsExporter (config) {
123
112
  const { OtlpStatsExporter } = require('./otlp_span_stats_exporter')
124
113
  const protocol = config.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL || 'http/json'
125
- const resourceAttributes = buildResourceAttributes(config.tags, {
114
+ const buildSpanStatsResourceAttributes = () => buildResourceAttributes(config.tags, {
126
115
  reportHostname: config.reportHostname,
127
116
  service: config.service,
128
117
  env: config.env,
129
118
  serviceVersion: config.version,
130
119
  })
131
- return new OtlpStatsExporter(
120
+ const exporter = new OtlpStatsExporter(
132
121
  config.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
133
122
  protocol,
134
- resourceAttributes,
123
+ buildSpanStatsResourceAttributes(),
135
124
  config.OTEL_EXPORTER_OTLP_METRICS_HEADERS,
136
125
  config.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT
137
126
  )
127
+
128
+ registerResourceAttributeRefresh(exporter, buildSpanStatsResourceAttributes)
129
+
130
+ return exporter
138
131
  }
139
132
 
140
133
  module.exports = {
@@ -4,7 +4,7 @@ const OtlpHttpExporterBase = require('../otlp/otlp_http_exporter_base')
4
4
  const OtlpTransformer = require('./otlp_transformer')
5
5
 
6
6
  /**
7
- * @typedef {import('@opentelemetry/resources').Resource} Resource
7
+ * @typedef {import('@opentelemetry/api').Attributes} Attributes
8
8
  * @typedef {import('./periodic_metric_reader').AggregatedMetric} AggregatedMetric
9
9
  */
10
10
 
@@ -22,11 +22,21 @@ class OtlpHttpMetricExporter extends OtlpHttpExporterBase {
22
22
  * corresponding `OTEL_EXPORTER_OTLP_*_HEADERS` env by the MAP parser.
23
23
  * @param {number} timeout - Request timeout in milliseconds
24
24
  * @param {string} protocol - OTLP protocol (http/protobuf or http/json)
25
- * @param {Resource} resource - Resource attributes
25
+ * @param {Attributes} resourceAttributes - Resource attributes
26
26
  */
27
- constructor (url, headers, timeout, protocol, resource) {
27
+ constructor (url, headers, timeout, protocol, resourceAttributes) {
28
28
  super(url, headers, timeout, protocol, 'metrics')
29
- this.transformer = new OtlpTransformer(resource, protocol)
29
+ this.transformer = new OtlpTransformer(resourceAttributes, protocol)
30
+ }
31
+
32
+ /**
33
+ * Recomputes the resource attributes baked into the transformer (e.g. after a MicroVM clone
34
+ * resume regenerates `runtime-id`). Leaves the reader's export interval untouched.
35
+ *
36
+ * @param {Attributes} resourceAttributes - Resource attributes
37
+ */
38
+ updateResourceAttributes (resourceAttributes) {
39
+ this.transformer.updateResourceAttributes(resourceAttributes)
30
40
  }
31
41
 
32
42
  /**
@@ -19,6 +19,16 @@ class OtlpStatsExporter extends OtlpHttpExporterBase {
19
19
  this.#transformer = new OtlpStatsTransformer(resourceAttributes, protocol)
20
20
  }
21
21
 
22
+ /**
23
+ * Recomputes the resource attributes baked into the transformer (e.g. after a MicroVM clone
24
+ * resume regenerates `runtime-id`).
25
+ *
26
+ * @param {import('@opentelemetry/api').Attributes} resourceAttributes
27
+ */
28
+ updateResourceAttributes (resourceAttributes) {
29
+ this.#transformer.updateResourceAttributes(resourceAttributes)
30
+ }
31
+
22
32
  /**
23
33
  * @param {Array<{timeNs: number, bucket: import('../../span_stats').SpanBuckets}>} drained
24
34
  * @param {number} bucketSizeNs
@@ -75,6 +75,16 @@ class OtlpTransformerBase {
75
75
  }
76
76
  }
77
77
 
78
+ /**
79
+ * Recomputes the cached OTLP resource attributes (e.g. after `config.tags` changes post-
80
+ * construction, such as a MicroVM clone resume regenerating its runtime-id). Leaves the
81
+ * reader/exporter/protocol otherwise untouched.
82
+ * @param {Attributes} resourceAttributes - Resource attributes
83
+ */
84
+ updateResourceAttributes (resourceAttributes) {
85
+ this.#resourceAttributes = this.transformAttributes(resourceAttributes)
86
+ }
87
+
78
88
  /**
79
89
  * Transforms attributes to OTLP KeyValue format.
80
90
  * @param {Attributes} attributes - Attributes to transform
@@ -170,10 +180,15 @@ function stableStringify (attributes) {
170
180
  // Attributes are sorted by key to ensure consistent serialization regardless of key order.
171
181
  // Keys are always strings and values are always strings, numbers, booleans,
172
182
  // or arrays of strings, numbers, or booleans.
173
- return Object.keys(attributes)
174
- .sort()
175
- .map(key => `${key}:${JSON.stringify(attributes[key])}`)
176
- .join(',')
183
+ const keys = Object.keys(attributes).sort()
184
+ let serialized = ''
185
+ let isFirstKey = true
186
+ for (const key of keys) {
187
+ if (!isFirstKey) serialized += ','
188
+ serialized += `${key}:${JSON.stringify(attributes[key])}`
189
+ isFirstKey = false
190
+ }
191
+ return serialized
177
192
  }
178
193
 
179
194
  module.exports = OtlpTransformerBase
@@ -0,0 +1,55 @@
1
+ 'use strict'
2
+
3
+ const os = require('node:os')
4
+
5
+ const { channel } = require('dc-polyfill')
6
+
7
+ const identityRefreshChannel = channel('datadog:identity:refresh')
8
+ const resourceAttributeRefreshers = new Map()
9
+
10
+ function refreshActiveResourceAttributes () {
11
+ for (const refreshResourceAttributes of resourceAttributeRefreshers.values()) {
12
+ refreshResourceAttributes()
13
+ }
14
+ }
15
+
16
+ /**
17
+ * @typedef {import('@opentelemetry/api').Attributes} Attributes
18
+ * @typedef {{
19
+ * signalType: string,
20
+ * updateResourceAttributes: (resourceAttributes: Attributes) => void
21
+ * }} ResourceAttributeExporter
22
+ */
23
+
24
+ /**
25
+ * @param {import('../config/config-base')} config
26
+ * @returns {Attributes}
27
+ */
28
+ function buildResourceAttributes (config) {
29
+ const { service, version, env, ...tags } = config.tags
30
+ const resourceAttributes = {
31
+ 'service.name': config.service,
32
+ 'service.version': config.version,
33
+ 'deployment.environment': config.env,
34
+ ...tags,
35
+ }
36
+
37
+ if (config.reportHostname) resourceAttributes['host.name'] = os.hostname()
38
+
39
+ return resourceAttributes
40
+ }
41
+
42
+ /**
43
+ * @param {ResourceAttributeExporter} exporter
44
+ * @param {() => Attributes} buildResourceAttributes
45
+ */
46
+ function registerResourceAttributeRefresh (exporter, buildResourceAttributes) {
47
+ if (resourceAttributeRefreshers.size === 0) {
48
+ identityRefreshChannel.subscribe(refreshActiveResourceAttributes)
49
+ }
50
+ resourceAttributeRefreshers.set(exporter.signalType, () => {
51
+ exporter.updateResourceAttributes(buildResourceAttributes())
52
+ })
53
+ }
54
+
55
+ module.exports = { buildResourceAttributes, registerResourceAttributeRefresh }
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { VERSION } = require('../../../../../version')
4
+ const { registerResourceAttributeRefresh } = require('../resource-attributes')
4
5
  const OtlpHttpTraceExporter = require('./otlp_http_trace_exporter')
5
6
 
6
7
  /**
@@ -59,13 +60,17 @@ function buildResourceAttributes (config) {
59
60
  * @returns {OtlpHttpTraceExporter} The OTLP HTTP/JSON exporter
60
61
  */
61
62
  function createOtlpTraceExporter (config) {
62
- return new OtlpHttpTraceExporter(
63
+ const exporter = new OtlpHttpTraceExporter(
63
64
  config.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
64
65
  config.OTEL_EXPORTER_OTLP_TRACES_HEADERS,
65
66
  config.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
66
67
  buildResourceAttributes(config),
67
68
  config.DD_TRACE_OTEL_SEMANTICS_ENABLED
68
69
  )
70
+
71
+ registerResourceAttributeRefresh(exporter, () => buildResourceAttributes(config))
72
+
73
+ return exporter
69
74
  }
70
75
 
71
76
  module.exports = {
@@ -43,6 +43,16 @@ class OtlpHttpTraceExporter extends OtlpHttpExporterBase {
43
43
  this.#transformer = new OtlpTraceTransformer(resourceAttributes, otelTraceSemanticsEnabled)
44
44
  }
45
45
 
46
+ /**
47
+ * Recomputes the resource attributes baked into the transformer (e.g. after a MicroVM clone
48
+ * resume regenerates `runtime-id`).
49
+ *
50
+ * @param {import('@opentelemetry/api').Attributes} resourceAttributes - Resource attributes
51
+ */
52
+ updateResourceAttributes (resourceAttributes) {
53
+ this.#transformer.updateResourceAttributes(resourceAttributes)
54
+ }
55
+
46
56
  /**
47
57
  * Exports DD-formatted spans via OTLP over HTTP.
48
58
  *