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
@@ -2,16 +2,17 @@
2
2
 
3
3
  const dc = require('dc-polyfill')
4
4
  const { storage } = require('../../datadog-core')
5
+ const { isACFActive } = require('../../datadog-core/src/storage')
5
6
 
6
7
  // Channels that surface tracer-storage events to interested consumers
7
8
  // (currently the wall profiler and the OTEP-4947 thread context writer).
8
9
  //
9
10
  // dd-trace:storage:enter — fires when the active legacy-storage store
10
- // changes. Published by the shimmer installed in ensureChannelsActivated().
11
- // dd-trace:storage:before — fires from an async_hooks "before" callback in
12
- // non-AsyncContextFrame mode. Used by the wall profiler to refresh the
13
- // sample context at the start of each async resource callback. Has no
14
- // publisher in ACF mode.
11
+ // changes. Published by the shimmer installed in acquireChannels().
12
+ // dd-trace:storage:before — fires from an async_hooks "before" callback while
13
+ // at least one consumer that acquired the channels needs it. Used by the
14
+ // wall profiler in its non-AsyncContextFrame (single holder) mode to refresh
15
+ // the sample context at the start of each async resource callback.
15
16
  // dd-trace:span:finish, dd-trace:span:tags:update — published by the
16
17
  // tracer core (opentracing/span.js). Re-exported here as a convenience
17
18
  // so consumers have a single import path for storage-related channels.
@@ -25,55 +26,119 @@ function getActiveSpan () {
25
26
  return store && store.span
26
27
  }
27
28
 
28
- let channelsActivated = false
29
- // Idempotent. The asyncContextFrameEnabled argument from the first caller
30
- // wins; subsequent calls are no-ops regardless of their argument. In
31
- // practice all callers observe the same global ACF state.
32
- function ensureChannelsActivated (asyncContextFrameEnabled) {
33
- if (channelsActivated) return
29
+ // The legacy storage instrumentation feeding enterCh and beforeCh is installed
30
+ // lazily via acquireChannels() / releaseChannels(), ref-counted across
31
+ // consumers, so that once the last consumer stops (e.g. the profiler is turned
32
+ // off remotely), instrumentation is undone.
33
+ // Consumers MUST balance every acquireChannels() call with a matching
34
+ // releaseChannels() when they no longer use them.
35
+ let refCount = 0
36
+ // The async hook has a separate count: it's only needed by some consumers, and
37
+ // is a per-callback cost, so it's disabled as soon as the last of them releases.
38
+ let beforeRefCount = 0
39
+ let inRun = false
40
+ let beforeHook
34
41
 
35
- const shimmer = require('../../datadog-shimmer')
42
+ // Wrappers currently installed on legacy storage, keyed by method name. Each
43
+ // entry records the method's own property descriptor from before wrapping
44
+ // (undefined when the method was inherited from the prototype) so it can be
45
+ // restored exactly.
46
+ const installedWrappers = new Map()
47
+
48
+ function wrapEnterWith (original) {
49
+ return function (store) {
50
+ const retVal = original.call(this, store)
51
+ if (!inRun && refCount !== 0) enterCh.publish()
52
+ return retVal
53
+ }
54
+ }
36
55
 
37
- // We need to instrument enterWith() on the legacy storage — that's the storage
38
- // carrying span data and the only one consumers of these channels care about.
39
- const legacyStorage = storage('legacy')
40
- let inRun = false
41
- shimmer.wrap(legacyStorage, 'enterWith', function (original) {
42
- return function (store) {
43
- const retVal = original.call(this, store)
44
- if (!inRun) enterCh.publish()
56
+ function wrapRun (original) {
57
+ const shimmer = require('../../datadog-shimmer')
58
+ return function (store, callback, ...args) {
59
+ if (refCount === 0) return original.call(this, store, callback, ...args)
60
+ const wrappedCb = shimmer.wrapFunction(callback, cb => function (...args) {
61
+ inRun = false
62
+ enterCh.publish()
63
+ const retVal = cb.apply(this, args)
64
+ inRun = true
45
65
  return retVal
46
- }
47
- })
66
+ })
67
+ inRun = true
68
+ const retVal = original.call(this, store, wrappedCb, ...args)
69
+ enterCh.publish()
70
+ inRun = false
71
+ return retVal
72
+ }
73
+ }
48
74
 
49
- // When not using AsyncContextFrame, we need additional instrumentation.
50
- if (!asyncContextFrameEnabled) {
51
- // We need async_hooks.createHook to create a "before" callback.
52
- const { createHook } = require('async_hooks')
53
- createHook({ before: () => beforeCh.publish() }).enable()
75
+ function installWrapper (target, name, wrapper) {
76
+ if (installedWrappers.has(name)) return
77
+ const descriptor = Object.getOwnPropertyDescriptor(target, name)
78
+ require('../../datadog-shimmer').wrap(target, name, wrapper)
79
+ installedWrappers.set(name, { descriptor, wrapped: target[name] })
80
+ }
54
81
 
82
+ function uninstallWrappers (target) {
83
+ for (const [name, { descriptor, wrapped }] of installedWrappers) {
84
+ // If someone else wrapped the method on top of ours, restoring would
85
+ // silently drop their wrapper too. Leave ours in place instead; it
86
+ // short-circuits while released, and gets reused on the next acquisition.
87
+ if (target[name] !== wrapped) continue
88
+ if (descriptor === undefined) {
89
+ delete target[name]
90
+ } else {
91
+ Object.defineProperty(target, name, descriptor)
92
+ }
93
+ installedWrappers.delete(name)
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Acquires a reference to the channel publishers. The first reference installs
99
+ * the legacy storage instrumentation publishing on enterCh.
100
+ *
101
+ * @param {boolean} needsBeforeHook Whether the consumer needs beforeCh events.
102
+ * The same value must be passed to the matching releaseChannels() call.
103
+ */
104
+ function acquireChannels (needsBeforeHook) {
105
+ if (refCount++ === 0) {
106
+ // We need to instrument enterWith() on the legacy storage — that's the storage
107
+ // carrying span data and the only one consumers of these channels care about.
108
+ const legacyStorage = storage('legacy')
109
+ installWrapper(legacyStorage, 'enterWith', wrapEnterWith)
55
110
  // In ACF-based implementation run() delegates to enterWith() so it doesn't
56
- // need to be separately instrumented. in non-ACF implementation run()
111
+ // need to be separately instrumented. In non-ACF implementation run()
57
112
  // doesn't delegate to enterWith(), so separate instrumentation is necessary.
58
- shimmer.wrap(legacyStorage, 'run', function (original) {
59
- return function (store, callback, ...args) {
60
- const wrappedCb = shimmer.wrapFunction(callback, cb => function (...args) {
61
- inRun = false
62
- enterCh.publish()
63
- const retVal = cb.apply(this, args)
64
- inRun = true
65
- return retVal
66
- })
67
- inRun = true
68
- const retVal = original.call(this, store, wrappedCb, ...args)
69
- enterCh.publish()
70
- inRun = false
71
- return retVal
72
- }
73
- })
113
+ if (!isACFActive) {
114
+ installWrapper(legacyStorage, 'run', wrapRun)
115
+ }
74
116
  }
75
117
 
76
- channelsActivated = true
118
+ if (needsBeforeHook && beforeRefCount++ === 0) {
119
+ beforeHook ??= require('async_hooks').createHook({ before: () => beforeCh.publish() })
120
+ beforeHook.enable()
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Releases one acquireChannels() reference. The async hook is disabled when the
126
+ * last reference needing it is released, and the legacy storage instrumentation
127
+ * is removed when the last reference overall is released.
128
+ *
129
+ * @param {boolean} needsBeforeHook The value passed to the matching
130
+ * acquireChannels() call.
131
+ */
132
+ function releaseChannels (needsBeforeHook) {
133
+ if (refCount === 0) return
134
+
135
+ if (needsBeforeHook && beforeRefCount !== 0 && --beforeRefCount === 0) {
136
+ beforeHook.disable()
137
+ }
138
+
139
+ if (--refCount === 0) {
140
+ uninstallWrappers(storage('legacy'))
141
+ }
77
142
  }
78
143
 
79
144
  module.exports = {
@@ -82,5 +147,6 @@ module.exports = {
82
147
  spanFinishCh,
83
148
  tagsUpdateCh,
84
149
  getActiveSpan,
85
- ensureChannelsActivated,
150
+ acquireChannels,
151
+ releaseChannels,
86
152
  }