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.
- package/LICENSE-3rdparty.csv +6 -4
- package/README.md +0 -2
- package/ci/init.js +52 -54
- package/ext/exporters.d.ts +2 -1
- package/ext/exporters.js +2 -1
- package/index.d.ts +240 -3
- package/initialize.mjs +1 -1
- package/package.json +17 -11
- package/packages/datadog-core/src/storage.js +14 -13
- package/packages/datadog-esbuild/index.js +118 -26
- package/packages/datadog-instrumentations/src/aws-sdk.js +42 -4
- package/packages/datadog-instrumentations/src/azure-functions.js +1 -1
- package/packages/datadog-instrumentations/src/azure-service-bus.js +1 -1
- package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
- package/packages/datadog-instrumentations/src/connect.js +6 -2
- package/packages/datadog-instrumentations/src/cucumber.js +31 -6
- package/packages/datadog-instrumentations/src/express.js +5 -6
- package/packages/datadog-instrumentations/src/fastify.js +3 -3
- package/packages/datadog-instrumentations/src/helpers/hook.js +28 -15
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +15 -5
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -3
- package/packages/datadog-instrumentations/src/http2/client.js +1 -0
- package/packages/datadog-instrumentations/src/http2/server.js +0 -1
- package/packages/datadog-instrumentations/src/ioredis.js +12 -1
- package/packages/datadog-instrumentations/src/jest.js +48 -36
- package/packages/datadog-instrumentations/src/limitd-client.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +15 -7
- package/packages/datadog-instrumentations/src/mocha/utils.js +3 -0
- package/packages/datadog-instrumentations/src/mongoose.js +2 -1
- package/packages/datadog-instrumentations/src/oracledb.js +19 -13
- package/packages/datadog-instrumentations/src/pg.js +9 -5
- package/packages/datadog-instrumentations/src/pino.js +18 -6
- package/packages/datadog-instrumentations/src/playwright.js +15 -1
- package/packages/datadog-instrumentations/src/sequelize.js +1 -1
- package/packages/datadog-instrumentations/src/vitest.js +155 -62
- package/packages/datadog-plugin-ai/src/tracing.js +3 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +23 -8
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +101 -2
- package/packages/datadog-plugin-aws-sdk/src/util.js +1 -1
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +6 -0
- package/packages/datadog-plugin-cucumber/src/index.js +4 -56
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +6 -3
- package/packages/datadog-plugin-cypress/src/support.js +4 -0
- package/packages/datadog-plugin-express/src/code_origin.js +2 -2
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -2
- package/packages/datadog-plugin-jest/src/index.js +0 -21
- package/packages/datadog-plugin-mocha/src/index.js +3 -57
- package/packages/datadog-plugin-mongodb-core/src/index.js +38 -12
- package/packages/datadog-plugin-playwright/src/index.js +11 -5
- package/packages/datadog-plugin-vitest/src/index.js +5 -1
- package/packages/datadog-plugin-ws/src/close.js +1 -1
- package/packages/datadog-plugin-ws/src/producer.js +6 -1
- package/packages/datadog-plugin-ws/src/receiver.js +6 -1
- package/packages/dd-trace/src/aiguard/client.js +25 -0
- package/packages/dd-trace/src/aiguard/noop.js +9 -0
- package/packages/dd-trace/src/aiguard/sdk.js +173 -0
- package/packages/dd-trace/src/aiguard/tags.js +11 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +21 -4
- package/packages/dd-trace/src/appsec/iast/security-controls/parser.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +6 -3
- package/packages/dd-trace/src/appsec/stack_trace.js +20 -1
- package/packages/dd-trace/src/appsec/telemetry/waf.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -4
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +11 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +10 -1
- package/packages/dd-trace/src/config-helper.js +8 -1
- package/packages/dd-trace/src/config.js +92 -304
- package/packages/dd-trace/src/config_defaults.js +191 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -1
- package/packages/dd-trace/src/datastreams/fnv.js +2 -2
- package/packages/dd-trace/src/datastreams/index.js +23 -1
- package/packages/dd-trace/src/datastreams/writer.js +3 -2
- package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -1
- package/packages/dd-trace/src/dogstatsd.js +4 -3
- package/packages/dd-trace/src/encode/0.4.js +1 -5
- package/packages/dd-trace/src/exporter.js +1 -0
- package/packages/dd-trace/src/exporters/agent/index.js +3 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +1 -1
- package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +3 -2
- package/packages/dd-trace/src/exporters/common/request.js +2 -1
- package/packages/dd-trace/src/exporters/span-stats/index.js +3 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +15 -4
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +20 -7
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +40 -13
- package/packages/dd-trace/src/llmobs/plugins/openai.js +7 -1
- package/packages/dd-trace/src/llmobs/tagger.js +8 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
- package/packages/dd-trace/src/log/index.js +27 -16
- package/packages/dd-trace/src/log/log.js +29 -5
- package/packages/dd-trace/src/log/writer.js +5 -5
- package/packages/dd-trace/src/noop/proxy.js +4 -0
- package/packages/dd-trace/src/noop/span.js +1 -0
- package/packages/dd-trace/src/opentelemetry/span.js +14 -3
- package/packages/dd-trace/src/opentracing/span.js +19 -5
- package/packages/dd-trace/src/payload-tagging/config/index.js +16 -0
- package/packages/dd-trace/src/payload-tagging/index.js +26 -15
- package/packages/dd-trace/src/payload-tagging/tagging.js +17 -8
- package/packages/dd-trace/src/pkg.js +3 -1
- package/packages/dd-trace/src/plugin_manager.js +20 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +97 -3
- package/packages/dd-trace/src/plugins/composite.js +3 -0
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/plugin.js +67 -0
- package/packages/dd-trace/src/plugins/util/git-cache.js +129 -0
- package/packages/dd-trace/src/plugins/util/git.js +41 -27
- package/packages/dd-trace/src/plugins/util/test.js +56 -27
- package/packages/dd-trace/src/plugins/util/web.js +1 -1
- package/packages/dd-trace/src/priority_sampler.js +70 -46
- package/packages/dd-trace/src/profiler.js +4 -1
- package/packages/dd-trace/src/profiling/config.js +73 -42
- package/packages/dd-trace/src/profiling/profiler.js +3 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -8
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -0
- package/packages/dd-trace/src/profiling/profilers/wall.js +196 -117
- package/packages/dd-trace/src/proxy.js +15 -0
- package/packages/dd-trace/src/rate_limiter.js +26 -1
- package/packages/dd-trace/src/remote_config/capabilities.js +5 -0
- package/packages/dd-trace/src/remote_config/manager.js +3 -2
- package/packages/dd-trace/src/sampling_rule.js +124 -2
- package/packages/dd-trace/src/span_sampler.js +19 -0
- package/packages/dd-trace/src/standalone/product.js +9 -0
- package/packages/dd-trace/src/standalone/tracesource.js +16 -1
- package/packages/dd-trace/src/standalone/tracesource_priority_sampler.js +13 -0
- package/packages/dd-trace/src/startup-log.js +21 -2
- package/packages/dd-trace/src/supported-configurations.json +9 -0
- package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
- package/packages/dd-trace/src/util.js +1 -1
- package/register.js +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
80
|
-
this
|
|
81
|
-
this
|
|
82
|
-
this
|
|
83
|
-
this
|
|
84
|
-
this
|
|
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
|
|
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
|
|
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
|
|
157
|
+
return this.#codeHotspotsEnabled
|
|
107
158
|
}
|
|
108
159
|
|
|
109
160
|
endpointCollectionEnabled () {
|
|
110
|
-
return this
|
|
161
|
+
return this.#endpointCollectionEnabled
|
|
111
162
|
}
|
|
112
163
|
|
|
113
164
|
start ({ mapper } = {}) {
|
|
114
|
-
if (this
|
|
165
|
+
if (this.#started) return
|
|
115
166
|
|
|
116
|
-
this
|
|
117
|
-
this
|
|
118
|
-
kSampleCount = this.
|
|
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.
|
|
129
|
-
|
|
130
|
-
durationMillis: this
|
|
131
|
-
|
|
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
|
-
|
|
135
|
-
|
|
185
|
+
sourceMapper: this.#mapper,
|
|
186
|
+
useCPED: this.#asyncContextFrameEnabled,
|
|
187
|
+
withContexts: this.#withContexts,
|
|
188
|
+
workaroundV8Bug: this.#v8ProfilerBugWorkaroundEnabled
|
|
136
189
|
})
|
|
137
190
|
|
|
138
|
-
if (this
|
|
139
|
-
this
|
|
191
|
+
if (this.#withContexts) {
|
|
192
|
+
if (!this.#asyncContextFrameEnabled) {
|
|
193
|
+
this.#setNewContext()
|
|
194
|
+
}
|
|
140
195
|
|
|
141
|
-
if (this
|
|
142
|
-
this._profilerState = this.
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
|
212
|
+
this.#started = true
|
|
154
213
|
}
|
|
155
214
|
|
|
156
|
-
|
|
157
|
-
|
|
215
|
+
#setupTelemetryMetrics () {
|
|
216
|
+
const contextCountGauge = profilerTelemetryMetrics.gauge('wall.sample_contexts')
|
|
158
217
|
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
166
|
-
|
|
225
|
+
#enter () {
|
|
226
|
+
if (!this.#started) return
|
|
167
227
|
|
|
168
228
|
const span = getActiveSpan()
|
|
169
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
210
|
-
this.
|
|
298
|
+
#setNewContext () {
|
|
299
|
+
this.#pprof.time.setContext(
|
|
211
300
|
this._currentContext = {
|
|
212
301
|
ref: {}
|
|
213
302
|
}
|
|
214
303
|
)
|
|
215
304
|
}
|
|
216
305
|
|
|
217
|
-
|
|
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
|
-
|
|
238
|
-
const tag = `v8_profiler_bug_workaround_enabled:${this
|
|
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
|
|
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
|
-
|
|
248
|
-
if (!this
|
|
322
|
+
#stop (restart) {
|
|
323
|
+
if (!this.#started) return
|
|
249
324
|
|
|
250
|
-
if (this
|
|
325
|
+
if (this.#captureSpanData && !this.#asyncContextFrameEnabled) {
|
|
251
326
|
// update last sample context if needed
|
|
252
|
-
this
|
|
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.
|
|
335
|
+
const profile = this.#pprof.time.stop(restart, this.#boundGenerateLabels, lowCardinalityLabels)
|
|
261
336
|
|
|
262
337
|
if (restart) {
|
|
263
|
-
const v8BugDetected = this.
|
|
338
|
+
const v8BugDetected = this.#pprof.time.v8ProfilerStuckEventLoopDetected()
|
|
264
339
|
if (v8BugDetected !== 0) {
|
|
265
|
-
this
|
|
340
|
+
this.#reportV8bug(v8BugDetected === 1)
|
|
266
341
|
}
|
|
267
342
|
} else {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
418
|
+
this.#stop(false)
|
|
340
419
|
}
|
|
341
420
|
|
|
342
421
|
isStarted () {
|
|
343
|
-
return this
|
|
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 ||
|
|
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.
|
|
157
|
+
log.errorWithoutTelemetry('[RC] Error in request', err)
|
|
157
158
|
return cb()
|
|
158
159
|
}
|
|
159
160
|
|