dd-trace 5.67.0 → 5.69.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 (132) hide show
  1. package/LICENSE-3rdparty.csv +6 -4
  2. package/README.md +0 -2
  3. package/ci/init.js +52 -54
  4. package/ext/exporters.d.ts +2 -1
  5. package/ext/exporters.js +2 -1
  6. package/index.d.ts +240 -3
  7. package/initialize.mjs +1 -1
  8. package/package.json +17 -11
  9. package/packages/datadog-core/src/storage.js +14 -13
  10. package/packages/datadog-esbuild/index.js +118 -26
  11. package/packages/datadog-instrumentations/src/aws-sdk.js +42 -4
  12. package/packages/datadog-instrumentations/src/azure-functions.js +1 -1
  13. package/packages/datadog-instrumentations/src/azure-service-bus.js +1 -1
  14. package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
  15. package/packages/datadog-instrumentations/src/connect.js +6 -2
  16. package/packages/datadog-instrumentations/src/cucumber.js +31 -6
  17. package/packages/datadog-instrumentations/src/express.js +5 -6
  18. package/packages/datadog-instrumentations/src/fastify.js +3 -3
  19. package/packages/datadog-instrumentations/src/helpers/hook.js +28 -15
  20. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  21. package/packages/datadog-instrumentations/src/helpers/instrument.js +15 -5
  22. package/packages/datadog-instrumentations/src/helpers/register.js +10 -3
  23. package/packages/datadog-instrumentations/src/http2/client.js +1 -0
  24. package/packages/datadog-instrumentations/src/http2/server.js +0 -1
  25. package/packages/datadog-instrumentations/src/ioredis.js +12 -1
  26. package/packages/datadog-instrumentations/src/jest.js +48 -36
  27. package/packages/datadog-instrumentations/src/limitd-client.js +2 -1
  28. package/packages/datadog-instrumentations/src/mocha/main.js +15 -7
  29. package/packages/datadog-instrumentations/src/mocha/utils.js +3 -0
  30. package/packages/datadog-instrumentations/src/mongoose.js +2 -1
  31. package/packages/datadog-instrumentations/src/oracledb.js +19 -13
  32. package/packages/datadog-instrumentations/src/pg.js +9 -5
  33. package/packages/datadog-instrumentations/src/pino.js +18 -6
  34. package/packages/datadog-instrumentations/src/playwright.js +15 -1
  35. package/packages/datadog-instrumentations/src/sequelize.js +1 -1
  36. package/packages/datadog-instrumentations/src/vitest.js +155 -62
  37. package/packages/datadog-plugin-ai/src/tracing.js +3 -3
  38. package/packages/datadog-plugin-aws-sdk/src/base.js +23 -8
  39. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +2 -2
  40. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +101 -2
  41. package/packages/datadog-plugin-aws-sdk/src/util.js +1 -1
  42. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +6 -0
  43. package/packages/datadog-plugin-cucumber/src/index.js +4 -56
  44. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +6 -3
  45. package/packages/datadog-plugin-cypress/src/support.js +4 -0
  46. package/packages/datadog-plugin-express/src/code_origin.js +2 -2
  47. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -2
  48. package/packages/datadog-plugin-jest/src/index.js +0 -21
  49. package/packages/datadog-plugin-mocha/src/index.js +3 -57
  50. package/packages/datadog-plugin-mongodb-core/src/index.js +38 -12
  51. package/packages/datadog-plugin-playwright/src/index.js +11 -5
  52. package/packages/datadog-plugin-vitest/src/index.js +5 -1
  53. package/packages/datadog-plugin-ws/src/close.js +1 -1
  54. package/packages/datadog-plugin-ws/src/producer.js +6 -1
  55. package/packages/datadog-plugin-ws/src/receiver.js +6 -1
  56. package/packages/dd-trace/src/aiguard/client.js +25 -0
  57. package/packages/dd-trace/src/aiguard/noop.js +9 -0
  58. package/packages/dd-trace/src/aiguard/sdk.js +173 -0
  59. package/packages/dd-trace/src/aiguard/tags.js +11 -0
  60. package/packages/dd-trace/src/appsec/iast/path-line.js +21 -4
  61. package/packages/dd-trace/src/appsec/iast/security-controls/parser.js +1 -1
  62. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +6 -3
  63. package/packages/dd-trace/src/appsec/stack_trace.js +20 -1
  64. package/packages/dd-trace/src/appsec/telemetry/waf.js +2 -2
  65. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -4
  66. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +11 -3
  67. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +10 -1
  68. package/packages/dd-trace/src/config-helper.js +8 -1
  69. package/packages/dd-trace/src/config.js +92 -304
  70. package/packages/dd-trace/src/config_defaults.js +191 -0
  71. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -1
  72. package/packages/dd-trace/src/datastreams/fnv.js +2 -2
  73. package/packages/dd-trace/src/datastreams/index.js +23 -1
  74. package/packages/dd-trace/src/datastreams/writer.js +3 -2
  75. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -1
  76. package/packages/dd-trace/src/dogstatsd.js +4 -3
  77. package/packages/dd-trace/src/encode/0.4.js +1 -5
  78. package/packages/dd-trace/src/exporter.js +1 -0
  79. package/packages/dd-trace/src/exporters/agent/index.js +3 -2
  80. package/packages/dd-trace/src/exporters/agent/writer.js +1 -1
  81. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +3 -2
  82. package/packages/dd-trace/src/exporters/common/request.js +2 -1
  83. package/packages/dd-trace/src/exporters/span-stats/index.js +3 -2
  84. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  85. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +15 -4
  86. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +20 -7
  87. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +40 -13
  88. package/packages/dd-trace/src/llmobs/plugins/openai.js +7 -1
  89. package/packages/dd-trace/src/llmobs/tagger.js +8 -0
  90. package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
  91. package/packages/dd-trace/src/log/index.js +27 -16
  92. package/packages/dd-trace/src/log/log.js +29 -5
  93. package/packages/dd-trace/src/log/writer.js +5 -5
  94. package/packages/dd-trace/src/noop/proxy.js +4 -0
  95. package/packages/dd-trace/src/noop/span.js +1 -0
  96. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  97. package/packages/dd-trace/src/opentracing/span.js +19 -5
  98. package/packages/dd-trace/src/payload-tagging/config/index.js +16 -0
  99. package/packages/dd-trace/src/payload-tagging/index.js +26 -15
  100. package/packages/dd-trace/src/payload-tagging/tagging.js +17 -8
  101. package/packages/dd-trace/src/pkg.js +3 -1
  102. package/packages/dd-trace/src/plugin_manager.js +20 -2
  103. package/packages/dd-trace/src/plugins/ci_plugin.js +97 -3
  104. package/packages/dd-trace/src/plugins/composite.js +3 -0
  105. package/packages/dd-trace/src/plugins/index.js +2 -0
  106. package/packages/dd-trace/src/plugins/plugin.js +67 -0
  107. package/packages/dd-trace/src/plugins/util/git-cache.js +129 -0
  108. package/packages/dd-trace/src/plugins/util/git.js +41 -27
  109. package/packages/dd-trace/src/plugins/util/test.js +56 -27
  110. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  111. package/packages/dd-trace/src/priority_sampler.js +70 -46
  112. package/packages/dd-trace/src/profiler.js +4 -1
  113. package/packages/dd-trace/src/profiling/config.js +73 -42
  114. package/packages/dd-trace/src/profiling/profiler.js +3 -1
  115. package/packages/dd-trace/src/profiling/profilers/events.js +3 -8
  116. package/packages/dd-trace/src/profiling/profilers/space.js +1 -0
  117. package/packages/dd-trace/src/profiling/profilers/wall.js +196 -117
  118. package/packages/dd-trace/src/proxy.js +15 -0
  119. package/packages/dd-trace/src/rate_limiter.js +26 -1
  120. package/packages/dd-trace/src/remote_config/capabilities.js +5 -0
  121. package/packages/dd-trace/src/remote_config/manager.js +3 -2
  122. package/packages/dd-trace/src/sampling_rule.js +124 -2
  123. package/packages/dd-trace/src/span_sampler.js +19 -0
  124. package/packages/dd-trace/src/standalone/product.js +9 -0
  125. package/packages/dd-trace/src/standalone/tracesource.js +16 -1
  126. package/packages/dd-trace/src/standalone/tracesource_priority_sampler.js +13 -0
  127. package/packages/dd-trace/src/startup-log.js +21 -2
  128. package/packages/dd-trace/src/supported-configurations.json +9 -0
  129. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  130. package/packages/dd-trace/src/util.js +1 -1
  131. package/register.js +1 -1
  132. package/version.js +4 -2
@@ -13,6 +13,7 @@ class NativeSpaceProfiler {
13
13
  _started = false
14
14
 
15
15
  constructor (options = {}) {
16
+ // TODO: Remove default value. It is only used in testing.
16
17
  this._samplingInterval = options.heapSamplingInterval || 512 * 1024
17
18
  this._stackDepth = options.stackDepth || 64
18
19
  this._oomMonitoring = options.oomMonitoring || {}
@@ -17,7 +17,7 @@ const TRACE_ENDPOINT_LABEL = 'trace endpoint'
17
17
 
18
18
  const { isWebServerSpan, endpointNameFromTags, getStartedSpans } = require('../webspan-utils')
19
19
 
20
- const beforeCh = dc.channel('dd-trace:storage:before')
20
+ let beforeCh
21
21
  const enterCh = dc.channel('dd-trace:storage:enter')
22
22
  const spanFinishCh = dc.channel('dd-trace:span:finish')
23
23
  const profilerTelemetryMetrics = telemetryMetrics.manager.namespace('profilers')
@@ -25,21 +25,53 @@ const profilerTelemetryMetrics = telemetryMetrics.manager.namespace('profilers')
25
25
  const ProfilingContext = Symbol('NativeWallProfiler.ProfilingContext')
26
26
 
27
27
  let kSampleCount
28
+ let kCPEDContextCount
28
29
 
29
30
  function getActiveSpan () {
30
31
  const store = storage('legacy').getStore()
31
32
  return store && store.span
32
33
  }
33
34
 
35
+ function toBigInt (spanId) {
36
+ return spanId !== null && typeof spanId === 'object' ? spanId.toBigInt() : spanId
37
+ }
38
+
39
+ function updateContext (context) {
40
+ // Converting spanIDs to bigints is not necessary as generateLabels will do it
41
+ // too. When we don't use async context frame, we can convert them when the
42
+ // sample is taken though so we amortize the latency of operations. It is an
43
+ // optimization.
44
+ if (context.spanId !== undefined) {
45
+ context.spanId = toBigInt(context.spanId)
46
+ }
47
+ if (context.rootSpanId !== undefined) {
48
+ context.rootSpanId = toBigInt(context.rootSpanId)
49
+ }
50
+ if (context.webTags !== undefined && context.endpoint === undefined) {
51
+ // endpoint may not be determined yet, but keep it as fallback
52
+ // if tags are not available anymore during serialization
53
+ context.endpoint = endpointNameFromTags(context.webTags)
54
+ }
55
+ }
56
+
34
57
  let channelsActivated = false
35
- function ensureChannelsActivated () {
58
+ function ensureChannelsActivated (asyncContextFrameEnabled) {
36
59
  if (channelsActivated) return
37
60
 
38
- const { AsyncLocalStorage, createHook } = require('async_hooks')
39
61
  const shimmer = require('../../../../datadog-shimmer')
62
+ const asyncHooks = require('async_hooks')
63
+
64
+ // When using AsyncContextFrame to store sample context, we do not need to use
65
+ // async_hooks.createHook to create a "before" callback anymore.
66
+ if (!asyncContextFrameEnabled) {
67
+ const { createHook } = asyncHooks
68
+ beforeCh = dc.channel('dd-trace:storage:before')
69
+ createHook({ before: () => beforeCh.publish() }).enable()
70
+ }
40
71
 
41
- createHook({ before: () => beforeCh.publish() }).enable()
72
+ const { AsyncLocalStorage } = asyncHooks
42
73
 
74
+ // We need to instrument AsyncLocalStorage.enterWith() both with and without AsyncContextFrame.
43
75
  let inRun = false
44
76
  shimmer.wrap(AsyncLocalStorage.prototype, 'enterWith', function (original) {
45
77
  return function (...args) {
@@ -49,73 +81,93 @@ function ensureChannelsActivated () {
49
81
  }
50
82
  })
51
83
 
52
- shimmer.wrap(AsyncLocalStorage.prototype, 'run', function (original) {
53
- return function (store, callback, ...args) {
54
- const wrappedCb = shimmer.wrapFunction(callback, cb => function (...args) {
55
- inRun = false
56
- enterCh.publish()
57
- const retVal = cb.apply(this, args)
84
+ // We only need to instrument AsyncLocalStorage.run() when not using AsyncContextFrame.
85
+ // AsyncContextFrame-based implementation of AsyncLocalStorage.run() delegates
86
+ // to AsyncLocalStorage.enterWith() so it doesn't need to be separately instrumented.
87
+ if (!asyncContextFrameEnabled) {
88
+ shimmer.wrap(AsyncLocalStorage.prototype, 'run', function (original) {
89
+ return function (store, callback, ...args) {
90
+ const wrappedCb = shimmer.wrapFunction(callback, cb => function (...args) {
91
+ inRun = false
92
+ enterCh.publish()
93
+ const retVal = cb.apply(this, args)
94
+ inRun = true
95
+ return retVal
96
+ })
58
97
  inRun = true
98
+ const retVal = original.call(this, store, wrappedCb, ...args)
99
+ enterCh.publish()
100
+ inRun = false
59
101
  return retVal
60
- })
61
- inRun = true
62
- const retVal = original.call(this, store, wrappedCb, ...args)
63
- enterCh.publish()
64
- inRun = false
65
- return retVal
66
- }
67
- })
102
+ }
103
+ })
104
+ }
68
105
 
69
106
  channelsActivated = true
70
107
  }
71
108
 
72
109
  class NativeWallProfiler {
73
- type = 'wall'
74
- _mapper
75
- _pprof
76
- _started = false
110
+ #asyncContextFrameEnabled = false
111
+ #captureSpanData = false
112
+ #codeHotspotsEnabled = false
113
+ #cpuProfilingEnabled = false
114
+ #endpointCollectionEnabled = false
115
+ #flushIntervalMillis = 0
116
+ #logger
117
+ #mapper
118
+ #pprof
119
+ #samplingIntervalMicros = 0
120
+ #started = false
121
+ #telemetryHeartbeatIntervalMillis = 0
122
+ #timelineEnabled = false
123
+ #v8ProfilerBugWorkaroundEnabled = false
124
+ #withContexts = false
125
+
126
+ // Bind these to this so they can be used as callbacks
127
+ #boundEnter = this.#enter.bind(this)
128
+ #boundSpanFinished = this.#spanFinished.bind(this)
129
+ #boundGenerateLabels = this._generateLabels.bind(this)
130
+
131
+ get type () { return 'wall' }
77
132
 
78
133
  constructor (options = {}) {
79
- this._samplingIntervalMicros = (options.samplingInterval || 1e3 / 99) * 1000 // 99hz
80
- this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
81
- this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
82
- this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
83
- this._timelineEnabled = !!options.timelineEnabled
84
- this._cpuProfilingEnabled = !!options.cpuProfilingEnabled
134
+ this.#asyncContextFrameEnabled = !!options.asyncContextFrameEnabled
135
+ this.#codeHotspotsEnabled = !!options.codeHotspotsEnabled
136
+ this.#cpuProfilingEnabled = !!options.cpuProfilingEnabled
137
+ this.#endpointCollectionEnabled = !!options.endpointCollectionEnabled
138
+ this.#flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
139
+ this.#logger = options.logger
140
+ // TODO: Remove default value. It is only used in testing.
141
+ this.#samplingIntervalMicros = (options.samplingInterval || 1e3 / 99) * 1000
142
+ this.#telemetryHeartbeatIntervalMillis = options.heartbeatInterval || 60 * 1e3 // 60 seconds
143
+ this.#timelineEnabled = !!options.timelineEnabled
144
+ this.#v8ProfilerBugWorkaroundEnabled = !!options.v8ProfilerBugWorkaroundEnabled
145
+
85
146
  // We need to capture span data into the sample context for either code hotspots
86
147
  // or endpoint collection.
87
- this._captureSpanData = this._codeHotspotsEnabled || this._endpointCollectionEnabled
148
+ this.#captureSpanData = this.#codeHotspotsEnabled || this.#endpointCollectionEnabled
88
149
  // We need to run the pprof wall profiler with sample contexts if we're either
89
150
  // capturing span data or timeline is enabled (so we need sample timestamps, and for now
90
151
  // timestamps require the sample contexts feature in the pprof wall profiler), or
91
152
  // cpu profiling is enabled.
92
- this._withContexts = this._captureSpanData || this._timelineEnabled || this._cpuProfilingEnabled
93
- this._v8ProfilerBugWorkaroundEnabled = !!options.v8ProfilerBugWorkaroundEnabled
94
-
95
- // Bind these to this so they can be used as callbacks
96
- if (this._withContexts && this._captureSpanData) {
97
- this._enter = this._enter.bind(this)
98
- this._spanFinished = this._spanFinished.bind(this)
99
- }
100
- this._generateLabels = this._generateLabels.bind(this)
101
-
102
- this._logger = options.logger
153
+ this.#withContexts = this.#captureSpanData || this.#timelineEnabled || this.#cpuProfilingEnabled
103
154
  }
104
155
 
105
156
  codeHotspotsEnabled () {
106
- return this._codeHotspotsEnabled
157
+ return this.#codeHotspotsEnabled
107
158
  }
108
159
 
109
160
  endpointCollectionEnabled () {
110
- return this._endpointCollectionEnabled
161
+ return this.#endpointCollectionEnabled
111
162
  }
112
163
 
113
164
  start ({ mapper } = {}) {
114
- if (this._started) return
165
+ if (this.#started) return
115
166
 
116
- this._mapper = mapper
117
- this._pprof = require('@datadog/pprof')
118
- kSampleCount = this._pprof.time.constants.kSampleCount
167
+ this.#mapper = mapper
168
+ this.#pprof = require('@datadog/pprof')
169
+ kSampleCount = this.#pprof.time.constants.kSampleCount
170
+ kCPEDContextCount = this.#pprof.time.constants.kCPEDContextCount
119
171
 
120
172
  // pprof otherwise crashes in worker threads
121
173
  if (!process._startProfilerIdleNotifier) {
@@ -125,51 +177,88 @@ class NativeWallProfiler {
125
177
  process._stopProfilerIdleNotifier = () => {}
126
178
  }
127
179
 
128
- this._pprof.time.start({
129
- intervalMicros: this._samplingIntervalMicros,
130
- durationMillis: this._flushIntervalMillis,
131
- sourceMapper: this._mapper,
132
- withContexts: this._withContexts,
180
+ this.#pprof.time.start({
181
+ collectCpuTime: this.#cpuProfilingEnabled,
182
+ durationMillis: this.#flushIntervalMillis,
183
+ intervalMicros: this.#samplingIntervalMicros,
133
184
  lineNumbers: false,
134
- workaroundV8Bug: this._v8ProfilerBugWorkaroundEnabled,
135
- collectCpuTime: this._cpuProfilingEnabled
185
+ sourceMapper: this.#mapper,
186
+ useCPED: this.#asyncContextFrameEnabled,
187
+ withContexts: this.#withContexts,
188
+ workaroundV8Bug: this.#v8ProfilerBugWorkaroundEnabled
136
189
  })
137
190
 
138
- if (this._withContexts) {
139
- this._setNewContext()
191
+ if (this.#withContexts) {
192
+ if (!this.#asyncContextFrameEnabled) {
193
+ this.#setNewContext()
194
+ }
140
195
 
141
- if (this._captureSpanData) {
142
- this._profilerState = this._pprof.time.getState()
196
+ if (this.#captureSpanData) {
197
+ this._profilerState = this.#pprof.time.getState()
143
198
  this._lastSampleCount = 0
144
199
 
145
- ensureChannelsActivated()
200
+ ensureChannelsActivated(this.#asyncContextFrameEnabled)
146
201
 
147
- beforeCh.subscribe(this._enter)
148
- enterCh.subscribe(this._enter)
149
- spanFinishCh.subscribe(this._spanFinished)
202
+ if (this.#asyncContextFrameEnabled) {
203
+ this.#setupTelemetryMetrics()
204
+ } else {
205
+ beforeCh.subscribe(this.#boundEnter)
206
+ }
207
+ enterCh.subscribe(this.#boundEnter)
208
+ spanFinishCh.subscribe(this.#boundSpanFinished)
150
209
  }
151
210
  }
152
211
 
153
- this._started = true
212
+ this.#started = true
154
213
  }
155
214
 
156
- _enter () {
157
- if (!this._started) return
215
+ #setupTelemetryMetrics () {
216
+ const contextCountGauge = profilerTelemetryMetrics.gauge('wall.sample_contexts')
158
217
 
159
- const sampleCount = this._profilerState[kSampleCount]
160
- if (sampleCount !== this._lastSampleCount) {
161
- this._lastSampleCount = sampleCount
162
- const context = this._currentContext.ref
163
- this._setNewContext()
218
+ const that = this
219
+ this._contextCountGaugeUpdater = setInterval(() => {
220
+ contextCountGauge.mark(that._profilerState[kCPEDContextCount])
221
+ }, that.#telemetryHeartbeatIntervalMillis)
222
+ this._contextCountGaugeUpdater.unref()
223
+ }
164
224
 
165
- this._updateContext(context)
166
- }
225
+ #enter () {
226
+ if (!this.#started) return
167
227
 
168
228
  const span = getActiveSpan()
169
- this._currentContext.ref = span ? this._getProfilingContext(span) : {}
229
+ const sampleContext = span ? this.#getProfilingContext(span) : {}
230
+
231
+ // Note that we store the sample context differently with and without the
232
+ // async context frame. With the async context frame, we tell the profiler
233
+ // to store the sample context directly in the frame on each enterWith.
234
+ // Without the async context frame, we store one holder object as the
235
+ // profiler's single sample context, and reassign its "ref" property on
236
+ // every async context change. Then when we detect that the profiler took a
237
+ // sample (and thus bound the holder as that sample's context), we create a
238
+ // new holder object so that we no longer mutate the old one. This is really
239
+ // an optimization to avoid going to profiler's native SetContext every
240
+ // time. With async context frame however, we can't have that optimization,
241
+ // as we can't tell from which async context frame was the sampling context
242
+ // taken. For the same reason we can't call updateContext() on the old
243
+ // context -- we simply can't tell which one it might've been across all
244
+ // possible async context frames.
245
+ if (this.#asyncContextFrameEnabled) {
246
+ this.#pprof.time.setContext(sampleContext)
247
+ } else {
248
+ const sampleCount = this._profilerState[kSampleCount]
249
+ if (sampleCount !== this._lastSampleCount) {
250
+ this._lastSampleCount = sampleCount
251
+ const context = this._currentContext.ref
252
+ this.#setNewContext()
253
+
254
+ updateContext(context)
255
+ }
256
+
257
+ this._currentContext.ref = sampleContext
258
+ }
170
259
  }
171
260
 
172
- _getProfilingContext (span) {
261
+ #getProfilingContext (span) {
173
262
  let profilingContext = span[ProfilingContext]
174
263
  if (profilingContext === undefined) {
175
264
  const context = span.context()
@@ -177,13 +266,13 @@ class NativeWallProfiler {
177
266
 
178
267
  let spanId
179
268
  let rootSpanId
180
- if (this._codeHotspotsEnabled) {
269
+ if (this.#codeHotspotsEnabled) {
181
270
  spanId = context._spanId
182
271
  rootSpanId = startedSpans.length ? startedSpans[0].context()._spanId : context._spanId
183
272
  }
184
273
 
185
274
  let webTags
186
- if (this._endpointCollectionEnabled) {
275
+ if (this.#endpointCollectionEnabled) {
187
276
  const tags = context._tags
188
277
  if (isWebServerSpan(tags)) {
189
278
  webTags = tags
@@ -193,7 +282,7 @@ class NativeWallProfiler {
193
282
  for (let i = startedSpans.length; --i >= 0;) {
194
283
  const ispan = startedSpans[i]
195
284
  if (ispan.context()._spanId === parentId) {
196
- webTags = this._getProfilingContext(ispan).webTags
285
+ webTags = this.#getProfilingContext(ispan).webTags
197
286
  break
198
287
  }
199
288
  }
@@ -206,50 +295,36 @@ class NativeWallProfiler {
206
295
  return profilingContext
207
296
  }
208
297
 
209
- _setNewContext () {
210
- this._pprof.time.setContext(
298
+ #setNewContext () {
299
+ this.#pprof.time.setContext(
211
300
  this._currentContext = {
212
301
  ref: {}
213
302
  }
214
303
  )
215
304
  }
216
305
 
217
- _updateContext (context) {
218
- if (context.spanId !== null && typeof context.spanId === 'object') {
219
- context.spanId = context.spanId.toBigInt()
220
- }
221
- if (context.rootSpanId !== null && typeof context.rootSpanId === 'object') {
222
- context.rootSpanId = context.rootSpanId.toBigInt()
223
- }
224
- if (context.webTags !== undefined && context.endpoint === undefined) {
225
- // endpoint may not be determined yet, but keep it as fallback
226
- // if tags are not available anymore during serialization
227
- context.endpoint = endpointNameFromTags(context.webTags)
228
- }
229
- }
230
-
231
- _spanFinished (span) {
306
+ #spanFinished (span) {
232
307
  if (span[ProfilingContext] !== undefined) {
233
308
  span[ProfilingContext] = undefined
234
309
  }
235
310
  }
236
311
 
237
- _reportV8bug (maybeBug) {
238
- const tag = `v8_profiler_bug_workaround_enabled:${this._v8ProfilerBugWorkaroundEnabled}`
312
+ #reportV8bug (maybeBug) {
313
+ const tag = `v8_profiler_bug_workaround_enabled:${this.#v8ProfilerBugWorkaroundEnabled}`
239
314
  const metric = `v8_cpu_profiler${maybeBug ? '_maybe' : ''}_stuck_event_loop`
240
- this._logger?.warn(`Wall profiler: ${maybeBug ? 'possible ' : ''}v8 profiler stuck event loop detected.`)
315
+ this.#logger?.warn(`Wall profiler: ${maybeBug ? 'possible ' : ''}v8 profiler stuck event loop detected.`)
241
316
  // report as runtime metric (can be removed in the future when telemetry is mature)
242
317
  runtimeMetrics.increment(`runtime.node.profiler.${metric}`, tag, true)
243
318
  // report as telemetry metric
244
319
  profilerTelemetryMetrics.count(metric, [tag]).inc()
245
320
  }
246
321
 
247
- _stop (restart) {
248
- if (!this._started) return
322
+ #stop (restart) {
323
+ if (!this.#started) return
249
324
 
250
- if (this._captureSpanData) {
325
+ if (this.#captureSpanData && !this.#asyncContextFrameEnabled) {
251
326
  // update last sample context if needed
252
- this._enter()
327
+ this.#enter()
253
328
  this._lastSampleCount = 0
254
329
  }
255
330
 
@@ -257,21 +332,24 @@ class NativeWallProfiler {
257
332
  const lowCardinalityLabels = Object.keys(getThreadLabels())
258
333
  lowCardinalityLabels.push(TRACE_ENDPOINT_LABEL)
259
334
 
260
- const profile = this._pprof.time.stop(restart, this._generateLabels, lowCardinalityLabels)
335
+ const profile = this.#pprof.time.stop(restart, this.#boundGenerateLabels, lowCardinalityLabels)
261
336
 
262
337
  if (restart) {
263
- const v8BugDetected = this._pprof.time.v8ProfilerStuckEventLoopDetected()
338
+ const v8BugDetected = this.#pprof.time.v8ProfilerStuckEventLoopDetected()
264
339
  if (v8BugDetected !== 0) {
265
- this._reportV8bug(v8BugDetected === 1)
340
+ this.#reportV8bug(v8BugDetected === 1)
266
341
  }
267
342
  } else {
268
- if (this._captureSpanData) {
269
- beforeCh.unsubscribe(this._enter)
270
- enterCh.unsubscribe(this._enter)
271
- spanFinishCh.unsubscribe(this._spanFinished)
343
+ clearInterval(this._contextCountGaugeUpdater)
344
+ if (this.#captureSpanData) {
345
+ if (!this.#asyncContextFrameEnabled) {
346
+ beforeCh.unsubscribe(this.#boundEnter)
347
+ }
348
+ enterCh.unsubscribe(this.#boundEnter)
349
+ spanFinishCh.unsubscribe(this.#boundSpanFinished)
272
350
  this._profilerState = undefined
273
351
  }
274
- this._started = false
352
+ this.#started = false
275
353
  }
276
354
 
277
355
  return profile
@@ -280,7 +358,7 @@ class NativeWallProfiler {
280
358
  _generateLabels ({ node, context }) {
281
359
  // check for special node that represents CPU time all non-JS threads.
282
360
  // In that case only return a special thread name label since we cannot associate any timestamp/span/endpoint to it.
283
- if (node.name === this._pprof.time.constants.NON_JS_THREADS_FUNCTION_NAME) {
361
+ if (node.name === this.#pprof.time.constants.NON_JS_THREADS_FUNCTION_NAME) {
284
362
  return getNonJSThreadsLabels()
285
363
  }
286
364
 
@@ -292,7 +370,7 @@ class NativeWallProfiler {
292
370
 
293
371
  const labels = { ...getThreadLabels() }
294
372
 
295
- if (this._timelineEnabled) {
373
+ if (this.#timelineEnabled) {
296
374
  // Incoming timestamps are in microseconds, we emit nanos.
297
375
  labels[END_TIMESTAMP_LABEL] = context.timestamp * 1000n
298
376
  }
@@ -303,8 +381,9 @@ class NativeWallProfiler {
303
381
  }
304
382
 
305
383
  // Native profiler doesn't set context.context for some samples, such as idle samples or when
306
- // the context was otherwise unavailable when the sample was taken.
307
- const ref = context.context?.ref
384
+ // the context was otherwise unavailable when the sample was taken. Note that with async context
385
+ // frame, we don't use the "ref" indirection.
386
+ const ref = this.#asyncContextFrameEnabled ? context.context : context.context?.ref
308
387
  if (typeof ref !== 'object') {
309
388
  return labels
310
389
  }
@@ -312,10 +391,10 @@ class NativeWallProfiler {
312
391
  const { spanId, rootSpanId, webTags, endpoint } = ref
313
392
 
314
393
  if (spanId !== undefined) {
315
- labels[SPAN_ID_LABEL] = spanId
394
+ labels[SPAN_ID_LABEL] = toBigInt(spanId)
316
395
  }
317
396
  if (rootSpanId !== undefined) {
318
- labels[LOCAL_ROOT_SPAN_ID_LABEL] = rootSpanId
397
+ labels[LOCAL_ROOT_SPAN_ID_LABEL] = toBigInt(rootSpanId)
319
398
  }
320
399
  if (webTags !== undefined && Object.keys(webTags).length !== 0) {
321
400
  labels[TRACE_ENDPOINT_LABEL] = endpointNameFromTags(webTags)
@@ -328,7 +407,7 @@ class NativeWallProfiler {
328
407
  }
329
408
 
330
409
  profile (restart) {
331
- return this._stop(restart)
410
+ return this.#stop(restart)
332
411
  }
333
412
 
334
413
  encode (profile) {
@@ -336,11 +415,11 @@ class NativeWallProfiler {
336
415
  }
337
416
 
338
417
  stop () {
339
- this._stop(false)
418
+ this.#stop(false)
340
419
  }
341
420
 
342
421
  isStarted () {
343
- return this._started
422
+ return this.#started
344
423
  }
345
424
  }
346
425
 
@@ -88,6 +88,9 @@ class Tracer extends NoopProxy {
88
88
  }
89
89
  }
90
90
 
91
+ /**
92
+ * @override
93
+ */
91
94
  init (options) {
92
95
  if (this._initialized) return this
93
96
 
@@ -241,6 +244,9 @@ class Tracer extends NoopProxy {
241
244
  this.dataStreamsCheckpointer = this._tracer.dataStreamsCheckpointer
242
245
  lazyProxy(this, 'appsec', config, () => require('./appsec/sdk'), this._tracer, config)
243
246
  lazyProxy(this, 'llmobs', config, () => require('./llmobs/sdk'), this._tracer, this._modules.llmobs, config)
247
+ if (config.experimental?.aiguard?.enabled) {
248
+ lazyProxy(this, 'aiguard', config, () => require('./aiguard/sdk'), this._tracer, config)
249
+ }
244
250
  this._tracingInitialized = true
245
251
  }
246
252
  if (config.iast.enabled) {
@@ -261,6 +267,9 @@ class Tracer extends NoopProxy {
261
267
  }
262
268
  }
263
269
 
270
+ /**
271
+ * @override
272
+ */
264
273
  profilerStarted () {
265
274
  if (!this._profilerStarted) {
266
275
  // injection hardening: this is only ever invoked from tests.
@@ -269,11 +278,17 @@ class Tracer extends NoopProxy {
269
278
  return this._profilerStarted
270
279
  }
271
280
 
281
+ /**
282
+ * @override
283
+ */
272
284
  use () {
273
285
  this._pluginManager.configurePlugin(...arguments)
274
286
  return this
275
287
  }
276
288
 
289
+ /**
290
+ * @override
291
+ */
277
292
  get TracerProvider () {
278
293
  return require('./opentelemetry/tracer_provider')
279
294
  }
@@ -3,14 +3,25 @@
3
3
  const limiter = require('limiter')
4
4
 
5
5
  class RateLimiter {
6
+ /**
7
+ * @param {number} rateLimit - Allowed units per interval. Negative means unlimited, 0 disables.
8
+ * @param {'second'|'minute'|'hour'|'day'} [interval='second'] - Time window for the limiter.
9
+ */
6
10
  constructor (rateLimit, interval = 'second') {
7
- this._rateLimit = Number.parseInt(rateLimit)
11
+ this._rateLimit = Number.parseInt(String(rateLimit))
12
+ // The limiter constructor accepts a token count number and an interval string
8
13
  this._limiter = new limiter.RateLimiter(this._rateLimit, interval)
9
14
  this._tokensRequested = 0
10
15
  this._prevIntervalTokens = 0
11
16
  this._prevTokensRequested = 0
12
17
  }
13
18
 
19
+ /**
20
+ * Attempts to consume a token and reports whether it was allowed.
21
+ * Updates internal counters used for effective rate computation.
22
+ *
23
+ * @returns {boolean}
24
+ */
14
25
  isAllowed () {
15
26
  const curIntervalStart = this._limiter.curIntervalStart
16
27
  const curIntervalTokens = this._limiter.tokensThisInterval
@@ -27,6 +38,12 @@ class RateLimiter {
27
38
  return allowed
28
39
  }
29
40
 
41
+ /**
42
+ * Returns the fraction of allowed requests over requested ones in the
43
+ * current and previous intervals combined.
44
+ *
45
+ * @returns {number}
46
+ */
30
47
  effectiveRate () {
31
48
  if (this._rateLimit < 0) return 1
32
49
  if (this._rateLimit === 0) return 0
@@ -38,6 +55,10 @@ class RateLimiter {
38
55
  return allowed / requested
39
56
  }
40
57
 
58
+ /**
59
+ * Internal token consumption without counter side-effects.
60
+ * @returns {boolean}
61
+ */
41
62
  _isAllowed () {
42
63
  if (this._rateLimit < 0) return true
43
64
  if (this._rateLimit === 0) return false
@@ -45,6 +66,10 @@ class RateLimiter {
45
66
  return this._limiter.tryRemoveTokens(1)
46
67
  }
47
68
 
69
+ /**
70
+ * Effective rate within the current interval only.
71
+ * @returns {number}
72
+ */
48
73
  _currentWindowRate () {
49
74
  if (this._rateLimit < 0) return 1
50
75
  if (this._rateLimit === 0) return 0
@@ -31,4 +31,9 @@ module.exports = {
31
31
  ASM_RASP_CMDI: 1n << 37n,
32
32
  ASM_DD_MULTICONFIG: 1n << 42n,
33
33
  ASM_TRACE_TAGGING_RULES: 1n << 43n,
34
+ /*
35
+ DO NOT ADD ARBITRARY CAPABILITIES IN YOUR CODE
36
+ UNLESS THEY ARE ALREADY DEFINED IN THE BACKEND SOURCE OF TRUTH
37
+ DOING SO TRIGGERS BACKEND ALERTS
38
+ */
34
39
  }
@@ -11,6 +11,7 @@ const { UNACKNOWLEDGED, ACKNOWLEDGED, ERROR } = require('./apply_states')
11
11
  const Scheduler = require('./scheduler')
12
12
  const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
13
13
  const tagger = require('../tagger')
14
+ const defaults = require('../config_defaults')
14
15
 
15
16
  const clientId = uuid()
16
17
 
@@ -31,7 +32,7 @@ class RemoteConfigManager extends EventEmitter {
31
32
 
32
33
  this.url = config.url || new URL(format({
33
34
  protocol: 'http:',
34
- hostname: config.hostname || 'localhost',
35
+ hostname: config.hostname || defaults.hostname,
35
36
  port: config.port
36
37
  }))
37
38
 
@@ -153,7 +154,7 @@ class RemoteConfigManager extends EventEmitter {
153
154
  if (statusCode === 404) return cb()
154
155
 
155
156
  if (err) {
156
- log.error('[RC] Error in request', err)
157
+ log.errorWithoutTelemetry('[RC] Error in request', err)
157
158
  return cb()
158
159
  }
159
160