Skip to content

Commit 680248b

Browse files
committed
Add telemetry for number of live CPED native sample context objects
1 parent 1be0573 commit 680248b

File tree

4 files changed

+57
-11
lines changed

4 files changed

+57
-11
lines changed

integration-tests/profiler/profiler.spec.js

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ describe('profiler', () => {
650650
})
651651
})
652652

653-
context('Profiler API telemetry', () => {
653+
context('Profiler telemetry', () => {
654654
beforeEach(async () => {
655655
agent = await new FakeAgent().start()
656656
})
@@ -678,21 +678,20 @@ describe('profiler', () => {
678678
const pp = payload.payload
679679
assert.equal(pp.namespace, 'profilers')
680680
const series = pp.series
681-
assert.lengthOf(series, 2)
682-
assert.equal(series[0].metric, 'profile_api.requests')
683-
assert.equal(series[0].type, 'count')
681+
const requests = series.find(s => s.metric === 'profile_api.requests')
682+
assert.equal(requests.type, 'count')
684683
// There's a race between metrics and on-shutdown profile, so metric
685684
// value will be between 1 and 3
686-
requestCount = series[0].points[0][1]
685+
requestCount = requests.points[0][1]
687686
assert.isAtLeast(requestCount, 1)
688687
assert.isAtMost(requestCount, 3)
689688

690-
assert.equal(series[1].metric, 'profile_api.responses')
691-
assert.equal(series[1].type, 'count')
692-
assert.include(series[1].tags, 'status_code:200')
689+
const responses = series.find(s => s.metric === 'profile_api.responses')
690+
assert.equal(responses.type, 'count')
691+
assert.include(responses.tags, 'status_code:200')
693692

694693
// Same number of requests and responses
695-
assert.equal(series[1].points[0][1], requestCount)
694+
assert.equal(responses.points[0][1], requestCount)
696695
}, 'generate-metrics', timeout)
697696

698697
const checkDistributions = agent.assertTelemetryReceived(({ _, payload }) => {
@@ -713,6 +712,34 @@ describe('profiler', () => {
713712
// Same number of requests and points
714713
assert.equal(requestCount, pointsCount)
715714
})
715+
716+
it('sends wall profiler sample context telemetry', async function () {
717+
if (satisfies(process.versions.node, '<24.0.0')) {
718+
this.skip() // Wall profiler context count telemetry is not supported in Node < 24
719+
}
720+
proc = fork(profilerTestFile, {
721+
cwd,
722+
env: {
723+
DD_TRACE_AGENT_PORT: agent.port,
724+
DD_PROFILING_ENABLED: 1,
725+
DD_PROFILING_UPLOAD_PERIOD: 1,
726+
DD_PROFILING_USE_ASYNC_CONTEXT_FRAME: 1,
727+
DD_TELEMETRY_HEARTBEAT_INTERVAL: 1, // every second
728+
TEST_DURATION_MS: 1500
729+
}
730+
})
731+
732+
const checkMetrics = agent.assertTelemetryReceived(({ _, payload }) => {
733+
const pp = payload.payload
734+
assert.equal(pp.namespace, 'profilers')
735+
const sampleContexts = pp.series.find(s => s.metric === 'wall.sample_contexts')
736+
assert.isDefined(sampleContexts)
737+
assert.equal(sampleContexts.type, 'gauge')
738+
assert.isAtLeast(sampleContexts.points[0][1], 1)
739+
}, 'generate-metrics', timeout)
740+
741+
await Promise.all([checkProfiles(agent, proc, timeout), checkMetrics])
742+
})
716743
})
717744

718745
function forkSsi (args) {

packages/dd-trace/src/profiler.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ module.exports = {
1010
start: config => {
1111
const { service, version, env, url, hostname, port, tags, repositoryUrl, commitSHA, injectionEnabled } = config
1212
const { enabled, sourceMap, exporters } = config.profiling
13+
const { heartbeatInterval } = config.telemetry
14+
1315
const logger = {
1416
debug: (message) => log.debug(message),
1517
info: (message) => log.info(message),
@@ -39,7 +41,8 @@ module.exports = {
3941
repositoryUrl,
4042
commitSHA,
4143
libraryInjected,
42-
activation
44+
activation,
45+
heartbeatInterval
4346
})
4447
},
4548

packages/dd-trace/src/profiling/config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ class Config {
232232
}
233233
}
234234

235+
this.heartbeatInterval = options.heartbeatInterval || 60 * 1000 // 1 minute
236+
235237
this.profilers = ensureProfilers(profilers, this)
236238
}
237239
}

packages/dd-trace/src/profiling/profilers/wall.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ class NativeWallProfiler {
108108
this._timelineEnabled = !!options.timelineEnabled
109109
this._cpuProfilingEnabled = !!options.cpuProfilingEnabled
110110
this._useAsyncContextFrame = !!options.useAsyncContextFrame
111+
this._telemetryHeartbeatIntervalMillis = options.heartbeatInterval || 60 * 1e3 // 60 seconds
111112

112113
// We need to capture span data into the sample context for either code hotspots
113114
// or endpoint collection.
@@ -177,7 +178,9 @@ class NativeWallProfiler {
177178

178179
ensureChannelsActivated(this._useAsyncContextFrame)
179180

180-
if (!this._useAsyncContextFrame) {
181+
if (this._useAsyncContextFrame) {
182+
this._setupTelemetryMetrics()
183+
} else {
181184
beforeCh.subscribe(this._enter)
182185
}
183186
enterCh.subscribe(this._enter)
@@ -188,6 +191,16 @@ class NativeWallProfiler {
188191
this._started = true
189192
}
190193

194+
_setupTelemetryMetrics () {
195+
const contextCountGauge = profilerTelemetryMetrics.gauge('wall.sample_contexts')
196+
197+
const that = this
198+
this._contextCountGaugeUpdater = setInterval(() => {
199+
contextCountGauge.mark(that._profilerState[kCPEDContextCount])
200+
}, that._telemetryHeartbeatIntervalMillis)
201+
this._contextCountGaugeUpdater.unref()
202+
}
203+
191204
_enter () {
192205
if (!this._started) return
193206

@@ -315,6 +328,7 @@ class NativeWallProfiler {
315328
this._reportV8bug(v8BugDetected === 1)
316329
}
317330
} else {
331+
clearInterval(this._contextCountGaugeUpdater)
318332
if (this._captureSpanData) {
319333
if (!this._useAsyncContextFrame) {
320334
beforeCh.unsubscribe(this._enter)

0 commit comments

Comments
 (0)