dd-trace 6.20.0 → 6.21.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 (45) hide show
  1. package/index.d.ts +91 -3
  2. package/package.json +6 -7
  3. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +57 -4
  4. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
  5. package/packages/datadog-instrumentations/src/jest.js +119 -1
  6. package/packages/datadog-instrumentations/src/mocha/utils.js +124 -36
  7. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +61 -34
  8. package/packages/datadog-instrumentations/src/mocha/worker.js +6 -3
  9. package/packages/datadog-instrumentations/src/playwright.js +3 -0
  10. package/packages/datadog-instrumentations/src/vitest-main.js +0 -2
  11. package/packages/datadog-instrumentations/src/webdriverio.js +115 -15
  12. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +17 -0
  13. package/packages/datadog-plugin-mocha/src/index.js +216 -29
  14. package/packages/datadog-plugin-mocha/src/webdriverio-video.js +449 -0
  15. package/packages/dd-trace/src/config/supported-configurations.json +2 -2
  16. package/packages/dd-trace/src/debugger/constants.js +1 -0
  17. package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -3
  18. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +17 -12
  19. package/packages/dd-trace/src/debugger/devtools_client/send.js +6 -2
  20. package/packages/dd-trace/src/debugger/devtools_client/worker-error.js +21 -0
  21. package/packages/dd-trace/src/debugger/index.js +24 -2
  22. package/packages/dd-trace/src/debugger/probe_sampler.js +108 -11
  23. package/packages/dd-trace/src/exporters/common/request.js +1 -0
  24. package/packages/dd-trace/src/exporters/common/url.js +1 -1
  25. package/packages/dd-trace/src/id.js +1 -4
  26. package/packages/dd-trace/src/llmobs/agent-manifest.js +296 -0
  27. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
  28. package/packages/dd-trace/src/llmobs/sdk.js +23 -6
  29. package/packages/dd-trace/src/llmobs/span_processor.js +17 -6
  30. package/packages/dd-trace/src/llmobs/tagger.js +54 -1
  31. package/packages/dd-trace/src/llmobs/writers/base.js +3 -1
  32. package/packages/dd-trace/src/llmobs/writers/util.js +3 -1
  33. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +30 -1
  34. package/packages/dd-trace/src/openfeature/configuration_source.js +17 -2
  35. package/packages/dd-trace/src/openfeature/debug-logging-hook.js +25 -0
  36. package/packages/dd-trace/src/openfeature/flagging_provider.js +29 -0
  37. package/packages/dd-trace/src/openfeature/remote_config.js +9 -0
  38. package/packages/dd-trace/src/openfeature/writers/util.js +43 -4
  39. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +1 -1
  40. package/packages/dd-trace/src/otel-thread-ctx.js +2 -2
  41. package/packages/dd-trace/src/plugins/util/url.js +6 -1
  42. package/packages/dd-trace/src/profiling/profilers/wall.js +4 -10
  43. package/packages/dd-trace/src/proxy.js +2 -0
  44. package/packages/dd-trace/src/serverless.js +5 -0
  45. package/packages/dd-trace/src/storage-channels.js +112 -46
@@ -18,6 +18,7 @@ const {
18
18
  OUTPUT_VALUE,
19
19
  METADATA,
20
20
  COST_TAGS,
21
+ AGENT_MANIFEST,
21
22
  METRICS,
22
23
  TOOL_DEFINITIONS,
23
24
  PARENT_ID_KEY,
@@ -53,6 +54,7 @@ const {
53
54
  TRACE_ID,
54
55
  PROPAGATED_TRACE_ID_KEY,
55
56
  } = require('./constants/tags')
57
+ const { buildAgentDeclaration } = require('./agent-manifest')
56
58
  const { storage } = require('./storage')
57
59
  const {
58
60
  findGenAIAncestorSpanId,
@@ -187,6 +189,11 @@ class LLMObsTagger {
187
189
  this.tagCostTags(span, annotationContext.costTags, 'annotation_context')
188
190
  }
189
191
 
192
+ const agentDeclarations = /** @type {import('./agent-manifest').AgentDeclaration[] | undefined} */ (
193
+ storage.getStore()?.agentDeclarations
194
+ )
195
+ if (agentDeclarations) this.#applyAgentDeclarations(span, agentDeclarations)
196
+
190
197
  // apply annotation context name
191
198
  const annotationContextName = annotationContext?.name
192
199
  if (annotationContextName) this._setTag(span, NAME, annotationContextName)
@@ -204,6 +211,43 @@ class LLMObsTagger {
204
211
  }
205
212
  }
206
213
 
214
+ /**
215
+ * Applies the agents declared by the enclosing annotation contexts, outermost first, to every span in the block.
216
+ * The manifest is emitted only if the span is an agent when it finishes, since some integrations promote a span
217
+ * to an agent after registration.
218
+ *
219
+ * @param {import('../opentracing/span')} span
220
+ * @param {import('./agent-manifest').AgentDeclaration[]} declarations
221
+ */
222
+ #applyAgentDeclarations (span, declarations) {
223
+ for (const declaration of declarations) {
224
+ this.#tagAgentManifestFields(span, declaration.manifest)
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Tags the agent a caller declared on an agent span, merged onto what earlier annotations declared.
230
+ *
231
+ * @param {import('../opentracing/span')} span
232
+ * @param {unknown} agent
233
+ */
234
+ tagAgent (span, agent) {
235
+ const declaration = buildAgentDeclaration(agent)
236
+ if (declaration) this.#tagAgentManifestFields(span, declaration.manifest)
237
+ }
238
+
239
+ /**
240
+ * @param {import('../opentracing/span')} span
241
+ * @param {import('./agent-manifest').AgentManifestFields} manifest
242
+ */
243
+ #tagAgentManifestFields (span, manifest) {
244
+ // A shallow update, matching dd-trace-py: each declaration is already validated, so a later field replaces an
245
+ // earlier one whole (`model_settings` included) and an unset or invalid one leaves it in place. Declarations are
246
+ // never mutated, so the first one is stored without a copy.
247
+ const existing = registry.get(span)?.[AGENT_MANIFEST]
248
+ this._setTag(span, AGENT_MANIFEST, existing ? { ...existing, ...manifest } : manifest)
249
+ }
250
+
207
251
  #tagSamplingDecision (span, parent) {
208
252
  const traceTags = span.context()._trace.tags
209
253
  const parentTags = registry.get(parent)
@@ -337,7 +381,16 @@ class LLMObsTagger {
337
381
  if (currentTags) {
338
382
  Object.assign(currentTags, tags)
339
383
  } else {
340
- this._setTag(span, TAGS, tags)
384
+ // Copied so a later annotation cannot write into a caller's object shared by an annotation context. The copy
385
+ // reads caller getters and Proxy traps during span registration, so it must not throw into the application.
386
+ let copy
387
+ try {
388
+ copy = { ...tags }
389
+ } catch {
390
+ log.warn('Dropping span tags that could not be read.')
391
+ return
392
+ }
393
+ this._setTag(span, TAGS, copy)
341
394
  }
342
395
  }
343
396
 
@@ -4,7 +4,7 @@ const { URL, format } = require('node:url')
4
4
  const path = require('node:path')
5
5
  const request = require('../../exporters/common/request')
6
6
  const { getEnvironmentVariable } = require('../../config/helper')
7
- const { createServerlessDeliveryTracker } = require('../../serverless')
7
+ const { createServerlessDeliveryTracker, IS_STANDARD_AWS_LAMBDA } = require('../../serverless')
8
8
 
9
9
  const logger = require('../../log')
10
10
 
@@ -118,6 +118,8 @@ class BaseLLMObsWriter {
118
118
 
119
119
  buffer.size += eventSize
120
120
  buffer.events.push(event)
121
+ // The standard Lambda sandbox can freeze before the periodic flush runs.
122
+ if (IS_STANDARD_AWS_LAMBDA) this.flush()
121
123
  return true
122
124
  }
123
125
 
@@ -24,7 +24,9 @@ function setAgentStrategy (config, setWritersAgentlessValue) {
24
24
  }
25
25
 
26
26
  const endpoints = agentInfo.endpoints
27
- const hasEndpoint = Array.isArray(endpoints) && endpoints.includes(EVP_PROXY_AGENT_BASE_PATH)
27
+ // The Lambda extension lists full proxy paths (e.g. /evp_proxy/v2/api/v2/llmobs), not the bare prefix.
28
+ const hasEndpoint = Array.isArray(endpoints) &&
29
+ endpoints.some(endpoint => typeof endpoint === 'string' && endpoint.startsWith(EVP_PROXY_AGENT_BASE_PATH))
28
30
  setWritersAgentlessValue(!hasEndpoint)
29
31
  })
30
32
  }
@@ -21,6 +21,7 @@ const RETRY_JITTER = 0.2
21
21
  * @property {number} pollIntervalMs
22
22
  * @property {number} requestTimeoutMs
23
23
  * @property {string | undefined} apiKey
24
+ * @property {string | undefined} env
24
25
  */
25
26
 
26
27
  /**
@@ -106,9 +107,23 @@ class AgentlessConfigurationSource {
106
107
  */
107
108
  async #pollOnce (abortController) {
108
109
  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
110
+ log.debug('Feature Flags: agentless configuration poll attempt %d/%d', attempt, MAX_ATTEMPTS)
109
111
  const response = await this.#request(abortController.signal)
110
112
  if (this.#abortController !== abortController) return
111
113
 
114
+ if (response.statusCode === 304) {
115
+ // 304 means the cached configuration is still valid, not a failure.
116
+ log.debug('Feature Flags: agentless configuration poll attempt %d/%d returned HTTP 304 (unchanged)',
117
+ attempt, MAX_ATTEMPTS)
118
+ } else if (response.statusCode === undefined) {
119
+ log.debug('Feature Flags: agentless configuration poll attempt %d/%d failed: %s',
120
+ attempt, MAX_ATTEMPTS, errorMessage(response.error))
121
+ } else {
122
+ // Avoids errorMessage(response.error), which embeds the full request URL.
123
+ log.debug('Feature Flags: agentless configuration poll attempt %d/%d returned HTTP %d',
124
+ attempt, MAX_ATTEMPTS, response.statusCode)
125
+ }
126
+
112
127
  const retryable = response.statusCode === undefined || isRetryableStatus(response.statusCode)
113
128
  if (!retryable) {
114
129
  this.#apply(response)
@@ -205,6 +220,13 @@ class AgentlessConfigurationSource {
205
220
  const etag = response.headers?.etag
206
221
  const value = Array.isArray(etag) ? etag[0] : etag
207
222
  this.#etag = value?.trim() || undefined
223
+
224
+ // eslint-disable-next-line eslint-rules/eslint-log-printf-style
225
+ log.debug(() => {
226
+ const flagCount = Object.keys(configuration.flags ?? {}).length
227
+ return 'Feature Flags: agentless configuration applied successfully ' +
228
+ `(${flagCount} flag(s), env=${this.#config.env ?? 'unset'})`
229
+ })
208
230
  }
209
231
 
210
232
  /**
@@ -270,10 +292,17 @@ function parseConfiguration (body) {
270
292
  }
271
293
 
272
294
  /**
295
+ * Formats a request error, including the underlying network error code
296
+ * (e.g. ENOTFOUND, ECONNREFUSED, ETIMEDOUT) when available, since "request
297
+ * failed" alone does not tell a customer whether the problem is DNS, a
298
+ * refused connection, a timeout, or something else.
299
+ *
273
300
  * @param {unknown} error
274
301
  */
275
302
  function errorMessage (error) {
276
- return error instanceof Error ? error.message : String(error ?? 'request was not sent')
303
+ if (!(error instanceof Error)) return String(error ?? 'request was not sent')
304
+ const code = /** @type {{ code?: string }} */ (error).code
305
+ return code ? `${error.message} (${code})` : error.message
277
306
  }
278
307
 
279
308
  /**
@@ -23,7 +23,10 @@ function create (config, applyConfiguration) {
23
23
  DD_FEATURE_FLAGS_ENABLED: enabled,
24
24
  } = config.featureFlags
25
25
 
26
- if (!enabled || source !== 'agentless') {
26
+ if (!enabled) return
27
+
28
+ if (source !== 'agentless') {
29
+ log.debug('Feature Flags: configuration source is %s, not agentless; skipping agentless setup', source)
27
30
  return
28
31
  }
29
32
 
@@ -35,11 +38,14 @@ function create (config, applyConfiguration) {
35
38
  }
36
39
 
37
40
  const AgentlessConfigurationSource = require('./agentless_configuration_source')
41
+ const resolvedEndpoint = endpoint(config, baseUrl)
42
+ log.debug('Feature Flags: starting agentless configuration source at %s', redactedUrl(resolvedEndpoint))
38
43
  return new AgentlessConfigurationSource({
39
- endpoint: endpoint(config, baseUrl),
44
+ endpoint: resolvedEndpoint,
40
45
  pollIntervalMs: Math.min(pollIntervalSeconds, MAX_POLL_INTERVAL_SECONDS) * 1000,
41
46
  requestTimeoutMs: requestTimeoutSeconds * 1000,
42
47
  apiKey: hasCustomEndpoint ? undefined : config.DD_API_KEY,
48
+ env: config.env,
43
49
  }, applyConfiguration)
44
50
  } catch (error) {
45
51
  log.error('Unable to configure Feature Flagging configuration source', error)
@@ -85,6 +91,15 @@ function endpoint (config, configuredBaseUrl) {
85
91
  return url
86
92
  }
87
93
 
94
+ /**
95
+ * Drops everything but the origin; a custom base URL may embed a signed token in its path.
96
+ *
97
+ * @param {URL} url
98
+ */
99
+ function redactedUrl (url) {
100
+ return `${url.protocol}//${url.host}`
101
+ }
102
+
88
103
  module.exports = {
89
104
  create,
90
105
  }
@@ -0,0 +1,25 @@
1
+ 'use strict'
2
+
3
+ const log = require('../log')
4
+
5
+ /**
6
+ * Logs full evaluation details under DD_TRACE_DEBUG, matching EvalMetricsHook's use of `finally`.
7
+ */
8
+ class DebugLoggingHook {
9
+ /**
10
+ * @param {{ flagKey: string }} hookContext - Hook context containing the flag key
11
+ * @param {object} evaluationDetails - Full evaluation details
12
+ */
13
+ finally (hookContext, evaluationDetails) {
14
+ try {
15
+ log.debug('Feature Flags: evaluated %s: %o', hookContext?.flagKey, evaluationDetails)
16
+ } catch (error) {
17
+ try {
18
+ // Defense in depth; the OpenFeature SDK already guards finally hooks.
19
+ log.warn('DebugLoggingHook: error in finally hook: %s', error.message)
20
+ } catch { /* logging failure must not crash evaluation */ }
21
+ }
22
+ }
23
+ }
24
+
25
+ module.exports = DebugLoggingHook
@@ -4,8 +4,10 @@ const { channel } = require('dc-polyfill')
4
4
 
5
5
  const { DatadogNodeServerProvider } = require('../../../../vendor/dist/@datadog/openfeature-node-server')
6
6
  const log = require('../log')
7
+ const { debugChannel } = require('../log/channels')
7
8
  const configurationSource = require('./configuration_source')
8
9
  const { EXPOSURE_CHANNEL } = require('./constants/constants')
10
+ const DebugLoggingHook = require('./debug-logging-hook')
9
11
  const EvalMetricsHook = require('./eval-metrics-hook')
10
12
  const SpanEnrichmentHook = require('./span-enrichment-hook')
11
13
  const FlagEvalEVPHook = require('./writers/flag-eval-evp-hook')
@@ -36,6 +38,12 @@ class FlaggingProvider extends DatadogNodeServerProvider {
36
38
 
37
39
  if (config.DD_METRICS_OTEL_ENABLED === true) {
38
40
  this.hooks.push(new EvalMetricsHook(config))
41
+ } else {
42
+ log.debug('Feature Flags: evaluation metrics disabled; set %s=true to enable', 'DD_METRICS_OTEL_ENABLED')
43
+ }
44
+
45
+ if (config.debug && debugChannel.hasSubscribers) {
46
+ this.hooks.push(new DebugLoggingHook())
39
47
  }
40
48
 
41
49
  if (config.featureFlags.DD_EXPERIMENTAL_FLAGGING_PROVIDER_SPAN_ENRICHMENT_ENABLED) {
@@ -64,6 +72,8 @@ class FlaggingProvider extends DatadogNodeServerProvider {
64
72
  * @returns {Promise<void>}
65
73
  */
66
74
  initialize (context) {
75
+ log.debug('Feature Flags: waiting for provider initialization...')
76
+
67
77
  const promise = super.initialize(context)
68
78
 
69
79
  // `DatadogNodeServerProvider#initialize` starts a timer that is never unref'd, which would
@@ -72,6 +82,25 @@ class FlaggingProvider extends DatadogNodeServerProvider {
72
82
  // TODO: remove once `@datadog/openfeature-node-server` unrefs this timer itself.
73
83
  this.initController?.timeoutId?.unref?.()
74
84
 
85
+ // Only observes the outcome for logging; `promise` itself is returned unmodified below.
86
+ // Guarded so a logger failure can never leave the derived promise unhandled.
87
+ promise.then(
88
+ () => {
89
+ try {
90
+ log.debug('Feature Flags: provider initialized successfully')
91
+ } catch { /* logging failure must not crash the process */ }
92
+ },
93
+ (error) => {
94
+ try {
95
+ // errorWithoutTelemetry avoids inflating telemetry volume for every routine init timeout.
96
+ log.errorWithoutTelemetry(
97
+ 'Feature Flags: provider failed to initialize: %s',
98
+ error instanceof Error ? error.message : String(error)
99
+ )
100
+ } catch { /* logging failure must not crash the process */ }
101
+ }
102
+ )
103
+
75
104
  return promise
76
105
  }
77
106
 
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const log = require('../log')
3
4
  const RemoteConfigCapabilities = require('../remote_config/capabilities')
4
5
 
5
6
  /**
@@ -12,6 +13,8 @@ const RemoteConfigCapabilities = require('../remote_config/capabilities')
12
13
  function enable (rc, getOpenfeatureProxy, subscribe) {
13
14
  if (!subscribe) return
14
15
 
16
+ log.debug('Feature Flags: starting remote_config configuration source (Agent Remote Configuration)')
17
+
15
18
  rc.updateCapabilities(RemoteConfigCapabilities.FFE_FLAG_CONFIGURATION_RULES, true)
16
19
 
17
20
  /**
@@ -21,7 +24,13 @@ function enable (rc, getOpenfeatureProxy, subscribe) {
21
24
  const updateConfiguration = (action, conf) => {
22
25
  if (action === 'apply' || action === 'modify') {
23
26
  getOpenfeatureProxy().setConfiguration(conf)
27
+ // eslint-disable-next-line eslint-rules/eslint-log-printf-style
28
+ log.debug(() => {
29
+ const flagCount = Object.keys(conf?.flags ?? {}).length
30
+ return `Feature Flags: remote_config configuration ${action} applied successfully (${flagCount} flag(s))`
31
+ })
24
32
  } else if (action === 'unapply') {
33
+ log.debug('Feature Flags: remote_config configuration removed')
25
34
  getOpenfeatureProxy().setConfiguration(undefined)
26
35
  }
27
36
  }
@@ -16,6 +16,18 @@ const ROUTE_DISCOVERY_COOLDOWN_MS = 60_000
16
16
  const REQUIRED_LOCAL_HEADERS = [EVP_ORIGIN_HEADER_NAME, EVP_ORIGIN_VERSION_HEADER_NAME]
17
17
  const STOP_NOOP = () => {}
18
18
 
19
+ /**
20
+ * @typedef {(enabled: boolean, route?: import('./flag-evaluations').FlagEvaluationRoute) => void} DeliverySubscriber
21
+ */
22
+ /**
23
+ * @typedef {object} DeliveryStrategy
24
+ * @property {Set<{notify: DeliverySubscriber}>} subscribers
25
+ * @property {() => void} stop
26
+ * @property {[boolean, import('./flag-evaluations').FlagEvaluationRoute?]} [result]
27
+ */
28
+ /** @type {WeakMap<import('../../config/config-base'), DeliveryStrategy>} */
29
+ const deliveryStrategies = new WeakMap()
30
+
19
31
  let missingRouteWarningLogged = false
20
32
 
21
33
  /**
@@ -167,18 +179,45 @@ function setAgentlessStrategy (config, setWriterEnabledValue) {
167
179
  }
168
180
 
169
181
  /**
170
- * Applies one event-delivery strategy that can be shared by all Feature Flags writers.
182
+ * Subscribes to the delivery strategy owned by this tracer configuration.
183
+ * The provider and exposure module receive the same configuration object from
184
+ * the tracer. Share their route transitions, but never share credentials across
185
+ * distinct configurations. The last subscriber owns discovery cleanup.
171
186
  *
172
187
  * @param {import('../../config/config-base')} config - Tracer configuration object
173
188
  * @param {(enabled: boolean, route?: import('./flag-evaluations').FlagEvaluationRoute) => void} setWriterEnabledValue
174
189
  * Callback to set the writer enabled state
175
190
  */
176
191
  function setEventDeliveryStrategy (config, setWriterEnabledValue) {
177
- if (config.featureFlags?.DD_FEATURE_FLAGS_CONFIGURATION_SOURCE === 'agentless') {
178
- return setAgentlessStrategy(config, setWriterEnabledValue)
192
+ let strategy = deliveryStrategies.get(config)
193
+ const subscriber = { notify: setWriterEnabledValue }
194
+
195
+ if (strategy) {
196
+ strategy.subscribers.add(subscriber)
197
+ if (strategy.result) setWriterEnabledValue(...strategy.result)
198
+ } else {
199
+ strategy = { subscribers: new Set([subscriber]), stop: STOP_NOOP }
200
+ deliveryStrategies.set(config, strategy)
201
+ /** @type {DeliverySubscriber} */
202
+ const notify = (enabled, route) => {
203
+ // A Remote Config discovery already in flight cannot be canceled. Ignore
204
+ // its result after shutdown, including when the same config is reused.
205
+ if (deliveryStrategies.get(config) !== strategy) return
206
+ strategy.result = route === undefined ? [enabled] : [enabled, route]
207
+ for (const subscriber of strategy.subscribers) {
208
+ subscriber.notify(...strategy.result)
209
+ }
210
+ }
211
+ strategy.stop = config.featureFlags?.DD_FEATURE_FLAGS_CONFIGURATION_SOURCE === 'agentless'
212
+ ? setAgentlessStrategy(config, notify)
213
+ : setAgentStrategy(config, notify)
179
214
  }
180
215
 
181
- return setAgentStrategy(config, setWriterEnabledValue)
216
+ return () => {
217
+ if (!strategy.subscribers.delete(subscriber) || strategy.subscribers.size !== 0) return
218
+ deliveryStrategies.delete(config)
219
+ strategy.stop()
220
+ }
182
221
  }
183
222
 
184
223
  module.exports = {
@@ -30,7 +30,7 @@ function buildResourceAttributes (config) {
30
30
  const resourceAttributes = {
31
31
  'service.name': config.service,
32
32
  'service.version': config.version,
33
- 'deployment.environment': config.env,
33
+ 'deployment.environment.name': config.env,
34
34
  ...tags,
35
35
  }
36
36
 
@@ -32,7 +32,7 @@ const {
32
32
  enterCh,
33
33
  spanFinishCh,
34
34
  getActiveSpan,
35
- ensureChannelsActivated,
35
+ acquireChannels,
36
36
  } = require('./storage-channels')
37
37
  const {
38
38
  finalEndpoint,
@@ -371,7 +371,7 @@ function start () {
371
371
  getContext = ns.getContext
372
372
  clearContext = ns.clearContext
373
373
 
374
- ensureChannelsActivated(isACFActive)
374
+ acquireChannels(false)
375
375
  enterCh.subscribe(onEnter)
376
376
  spanFinishCh.subscribe(onSpanFinished)
377
377
  // Endpoint updates come from the shared web-tags cache's transition channels
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { URL } = require('url')
4
4
 
5
+ const { defaults } = require('../../config/defaults')
5
6
  const log = require('../../log')
6
7
 
7
8
  const HTTP2_HEADER_AUTHORITY = ':authority'
@@ -55,12 +56,15 @@ function obfuscateQs (config, url) {
55
56
 
56
57
  let qs = url.slice(i + 1)
57
58
 
58
- qs = qs.replace(queryStringObfuscation, '<redacted>')
59
+ // Group 1 of the default regex is the delimiter in front of a JWT. A user-provided regex may capture the secret.
60
+ const replacement = queryStringObfuscation === defaultQsObfuscator ? '$1<redacted>' : '<redacted>'
61
+ qs = qs.replace(queryStringObfuscation, replacement)
59
62
 
60
63
  return `${path}?${qs}`
61
64
  }
62
65
 
63
66
  const qsObfuscatorCache = new Map()
67
+ let defaultQsObfuscator
64
68
 
65
69
  /**
66
70
  * Compile the configured query-string obfuscator (a regex string, or a boolean)
@@ -85,6 +89,7 @@ function getQsObfuscator (config) {
85
89
  } else if (obfuscator !== '.*') { // '.*' optimizes to a full redact (true)
86
90
  try {
87
91
  compiled = new RegExp(obfuscator, 'gi')
92
+ if (obfuscator === defaults.DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP) defaultQsObfuscator = compiled
88
93
  } catch (err) {
89
94
  log.error('Error getting qs obfuscator', err)
90
95
  }
@@ -10,7 +10,8 @@ const {
10
10
  beforeCh,
11
11
  spanFinishCh,
12
12
  getActiveSpan,
13
- ensureChannelsActivated,
13
+ acquireChannels,
14
+ releaseChannels,
14
15
  } = require('../../storage-channels')
15
16
  const webTagsCache = require('../../web-tags-cache')
16
17
 
@@ -128,14 +129,6 @@ class NativeWallProfiler {
128
129
  this.#pprof = require('@datadog/pprof')
129
130
  kSampleCount = this.#pprof.time.constants.kSampleCount
130
131
 
131
- // pprof otherwise crashes in worker threads
132
- if (!process._startProfilerIdleNotifier) {
133
- process._startProfilerIdleNotifier = () => {}
134
- }
135
- if (!process._stopProfilerIdleNotifier) {
136
- process._stopProfilerIdleNotifier = () => {}
137
- }
138
-
139
132
  this.#pprof.time.start({
140
133
  collectCpuTime: this.#cpuProfilingEnabled,
141
134
  columnNumbers: 'emit',
@@ -157,7 +150,7 @@ class NativeWallProfiler {
157
150
  this.#profilerState = this.#pprof.time.getState()
158
151
  this.#lastSampleCount = 0
159
152
 
160
- ensureChannelsActivated(this.#asyncContextFrameEnabled)
153
+ acquireChannels(!this.#asyncContextFrameEnabled)
161
154
 
162
155
  if (this.#asyncContextFrameEnabled) {
163
156
  this.#setupTelemetryMetrics()
@@ -346,6 +339,7 @@ class NativeWallProfiler {
346
339
  }
347
340
  enterCh.unsubscribe(this.#boundEnter)
348
341
  spanFinishCh.unsubscribe(this.#boundSpanFinished)
342
+ releaseChannels(!this.#asyncContextFrameEnabled)
349
343
  if (this.#endpointCollectionEnabled) {
350
344
  webTagsCache.resolvedCh.unsubscribe(this.#boundSpanTagsUpdated)
351
345
  webTagsCache.deactivate()
@@ -6,6 +6,7 @@ const NoopProxy = require('./noop/proxy')
6
6
  const DatadogTracer = require('./tracer')
7
7
  const getConfig = require('./config')
8
8
  const { getEnvironmentVariable } = require('./config/helper')
9
+ const { reseed } = require('./id')
9
10
  const runtimeMetrics = require('./runtime_metrics')
10
11
  const log = require('./log')
11
12
  const { setStartupLogPluginManager, startupLog } = require('./startup-log')
@@ -353,6 +354,7 @@ class Tracer extends NoopProxy {
353
354
  if (request.method === 'POST' && request.url === '/aws/lambda-microvms/runtime/v1/run') {
354
355
  ch.unsubscribe(onHttpRequest)
355
356
  drainUuidPool()
357
+ reseed()
356
358
  channel('datadog:identity:update').publish(config)
357
359
  if (this._tracingInitialized) {
358
360
  const storeTracerMetadata = require('./tracer_metadata')
@@ -3,6 +3,10 @@
3
3
  const { getEnvironmentVariable, getValueFromEnvSources } = require('./config/helper')
4
4
 
5
5
  const IS_AWS_LAMBDA_MICROVM = getEnvironmentVariable('AWS_LAMBDA_MICROVM_IMAGE_ARN') !== undefined
6
+ // Standard Lambda, whose sandbox freezes between invocations. Excludes Lambda Lite (native-http),
7
+ // Managed Instances (lambda-managed-instances) and MicroVMs.
8
+ const IS_STANDARD_AWS_LAMBDA = !IS_AWS_LAMBDA_MICROVM && ['on-demand', 'provisioned-concurrency', 'snap-start']
9
+ .includes(getEnvironmentVariable('AWS_LAMBDA_INITIALIZATION_TYPE'))
6
10
  const isVercelAtStartup = getEnvironmentVariable('VERCEL') === '1'
7
11
 
8
12
  function getIsGCPFunction () {
@@ -104,6 +108,7 @@ module.exports = {
104
108
  getIsFlexConsumptionAzureFunction,
105
109
  initializeServerlessTelemetry,
106
110
  IS_AWS_LAMBDA_MICROVM,
111
+ IS_STANDARD_AWS_LAMBDA,
107
112
  // true only for a Node that bundles its own OpenSSL, whose CSPRNG keeps the snapshot's DRBG
108
113
  // state across a MicroVM clone resume
109
114
  NODE_BUNDLES_OPENSSL: process.config.variables.node_shared_openssl === false,