dd-trace 5.64.0 → 5.65.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/package.json +1 -1
- package/packages/dd-trace/src/azure_metadata.js +5 -4
- package/packages/dd-trace/src/datastreams/pathway.js +1 -1
- package/packages/dd-trace/src/dogstatsd.js +17 -20
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +175 -126
- package/packages/dd-trace/src/supported-configurations.json +0 -1
package/package.json
CHANGED
|
@@ -76,10 +76,11 @@ function buildMetadata () {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function getAzureAppMetadata () {
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
// See
|
|
82
|
-
|
|
79
|
+
// WEBSITE_SITE_NAME is the unique name of the website instance within Azure App Services. Its
|
|
80
|
+
// presence is used to determine if we are running in Azure App Service
|
|
81
|
+
// See equivalent in dd-trace-dotnet:
|
|
82
|
+
// https://github.com/DataDog/dd-trace-dotnet/blob/37030168b2996e549ba23231ae732874b53a37e6/tracer/src/Datadog.Trace/Util/EnvironmentHelpers.cs#L99-L155
|
|
83
|
+
if (getEnvironmentVariable('WEBSITE_SITE_NAME') !== undefined) {
|
|
83
84
|
return buildMetadata()
|
|
84
85
|
}
|
|
85
86
|
}
|
|
@@ -17,7 +17,7 @@ const CONTEXT_PROPAGATION_KEY_BASE64 = 'dd-pathway-ctx-base64'
|
|
|
17
17
|
const logKeys = [CONTEXT_PROPAGATION_KEY, CONTEXT_PROPAGATION_KEY_BASE64]
|
|
18
18
|
|
|
19
19
|
function shaHash (checkpointString) {
|
|
20
|
-
const hash = crypto.createHash('
|
|
20
|
+
const hash = crypto.createHash('sha256').update(checkpointString).digest('hex').slice(0, 16)
|
|
21
21
|
return Buffer.from(hash, 'hex')
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -156,23 +156,19 @@ class DogStatsDClient {
|
|
|
156
156
|
return socket
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
static generateClientConfig (config
|
|
159
|
+
static generateClientConfig (config) {
|
|
160
160
|
const tags = []
|
|
161
161
|
|
|
162
162
|
if (config.tags) {
|
|
163
|
-
Object.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// Skip runtime-id unless enabled as cardinality may be too high
|
|
167
|
-
if (key !== 'runtime-id') return true
|
|
168
|
-
return config.runtimeMetricsRuntimeId
|
|
169
|
-
})
|
|
170
|
-
.forEach(key => {
|
|
163
|
+
for (const [key, value] of Object.entries(config.tags)) {
|
|
164
|
+
// Skip runtime-id unless enabled as cardinality may be too high
|
|
165
|
+
if (typeof value === 'string' && (key !== 'runtime-id' || config.runtimeMetricsRuntimeId)) {
|
|
171
166
|
// https://docs.datadoghq.com/tagging/#defining-tags
|
|
172
|
-
const
|
|
167
|
+
const valueStripped = value.replaceAll(/[^a-z0-9_:./-]/ig, '_')
|
|
173
168
|
|
|
174
|
-
tags.push(`${key}:${
|
|
175
|
-
}
|
|
169
|
+
tags.push(`${key}:${valueStripped}`)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
176
172
|
}
|
|
177
173
|
|
|
178
174
|
const clientConfig = {
|
|
@@ -216,7 +212,7 @@ class MetricsAggregationClient {
|
|
|
216
212
|
this._histograms = new Map()
|
|
217
213
|
}
|
|
218
214
|
|
|
219
|
-
// TODO:
|
|
215
|
+
// TODO: Aggregate with a histogram and send the buckets to the client.
|
|
220
216
|
distribution (name, value, tags) {
|
|
221
217
|
this._client.distribution(name, value, tags)
|
|
222
218
|
}
|
|
@@ -352,9 +348,10 @@ class MetricsAggregationClient {
|
|
|
352
348
|
* @implements {DogStatsD}
|
|
353
349
|
*/
|
|
354
350
|
class CustomMetrics {
|
|
351
|
+
#client
|
|
355
352
|
constructor (config) {
|
|
356
353
|
const clientConfig = DogStatsDClient.generateClientConfig(config)
|
|
357
|
-
this
|
|
354
|
+
this.#client = new MetricsAggregationClient(new DogStatsDClient(clientConfig))
|
|
358
355
|
|
|
359
356
|
const flush = this.flush.bind(this)
|
|
360
357
|
|
|
@@ -365,27 +362,27 @@ class CustomMetrics {
|
|
|
365
362
|
}
|
|
366
363
|
|
|
367
364
|
increment (stat, value = 1, tags) {
|
|
368
|
-
this.
|
|
365
|
+
this.#client.increment(stat, value, CustomMetrics.tagTranslator(tags))
|
|
369
366
|
}
|
|
370
367
|
|
|
371
368
|
decrement (stat, value = 1, tags) {
|
|
372
|
-
this.
|
|
369
|
+
this.#client.decrement(stat, value, CustomMetrics.tagTranslator(tags))
|
|
373
370
|
}
|
|
374
371
|
|
|
375
372
|
gauge (stat, value, tags) {
|
|
376
|
-
this.
|
|
373
|
+
this.#client.gauge(stat, value, CustomMetrics.tagTranslator(tags))
|
|
377
374
|
}
|
|
378
375
|
|
|
379
376
|
distribution (stat, value, tags) {
|
|
380
|
-
this.
|
|
377
|
+
this.#client.distribution(stat, value, CustomMetrics.tagTranslator(tags))
|
|
381
378
|
}
|
|
382
379
|
|
|
383
380
|
histogram (stat, value, tags) {
|
|
384
|
-
this.
|
|
381
|
+
this.#client.histogram(stat, value, CustomMetrics.tagTranslator(tags))
|
|
385
382
|
}
|
|
386
383
|
|
|
387
384
|
flush () {
|
|
388
|
-
return this.
|
|
385
|
+
return this.#client.flush()
|
|
389
386
|
}
|
|
390
387
|
|
|
391
388
|
/**
|
|
@@ -4,72 +4,90 @@
|
|
|
4
4
|
|
|
5
5
|
const v8 = require('v8')
|
|
6
6
|
const os = require('os')
|
|
7
|
+
const process = require('process')
|
|
7
8
|
const { DogStatsDClient, MetricsAggregationClient } = require('../dogstatsd')
|
|
8
9
|
const log = require('../log')
|
|
9
|
-
const { performance, PerformanceObserver } = require('perf_hooks')
|
|
10
|
+
const { performance, PerformanceObserver, monitorEventLoopDelay } = require('perf_hooks')
|
|
10
11
|
const { getEnvironmentVariable } = require('../config-helper')
|
|
11
12
|
|
|
12
|
-
const { NODE_MAJOR
|
|
13
|
+
const { NODE_MAJOR } = require('../../../../version')
|
|
14
|
+
// TODO: This environment variable may not be changed, since the agent expects a flush every ten seconds.
|
|
15
|
+
// It is only a variable for testing. Think about alternatives.
|
|
13
16
|
const DD_RUNTIME_METRICS_FLUSH_INTERVAL = getEnvironmentVariable('DD_RUNTIME_METRICS_FLUSH_INTERVAL') ?? '10000'
|
|
14
17
|
const INTERVAL = Number.parseInt(DD_RUNTIME_METRICS_FLUSH_INTERVAL, 10)
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
// See: https://github.com/nodejs/node/issues/39548
|
|
18
|
-
const hasGCObserver = NODE_MAJOR >= 18 || (NODE_MAJOR === 16 && NODE_MINOR >= 7)
|
|
19
|
+
const eventLoopDelayResolution = 4
|
|
19
20
|
|
|
20
21
|
let nativeMetrics = null
|
|
21
22
|
let gcObserver = null
|
|
22
|
-
|
|
23
|
-
let
|
|
24
|
-
let
|
|
25
|
-
let
|
|
26
|
-
let
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
let interval = null
|
|
24
|
+
let client = null
|
|
25
|
+
let lastTime = 0n
|
|
26
|
+
let lastCpuUsage = null
|
|
27
|
+
let eventLoopDelayObserver = null
|
|
28
|
+
|
|
29
|
+
// !!!!!!!!!!!
|
|
30
|
+
// IMPORTANT
|
|
31
|
+
// !!!!!!!!!!!
|
|
32
|
+
//
|
|
33
|
+
// ALL metrics that relate to time are handled in nanoseconds in the backend.
|
|
34
|
+
// https://github.com/DataDog/dogweb/blob/prod/integration/node/node_metadata.csv
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
32
37
|
start (config) {
|
|
38
|
+
this.stop()
|
|
33
39
|
const clientConfig = DogStatsDClient.generateClientConfig(config)
|
|
34
|
-
const watchers = []
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
startGCObserver()
|
|
39
|
-
} else {
|
|
40
|
-
watchers.push('gc')
|
|
41
|
-
}
|
|
42
|
-
}
|
|
41
|
+
const trackEventLoop = config.runtimeMetrics.eventLoop !== false
|
|
42
|
+
const trackGc = config.runtimeMetrics.gc !== false
|
|
43
43
|
|
|
44
|
-
if (
|
|
45
|
-
|
|
44
|
+
if (trackGc) {
|
|
45
|
+
startGCObserver()
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// Using no-gc prevents the native gc metrics from being tracked. Not
|
|
49
|
+
// passing any options means all metrics are tracked.
|
|
50
|
+
// TODO: This is a workaround. We should find a better solution.
|
|
51
|
+
const watchers = trackEventLoop ? ['loop'] : ['no-gc']
|
|
52
|
+
|
|
48
53
|
try {
|
|
49
54
|
nativeMetrics = require('@datadog/native-metrics')
|
|
50
55
|
nativeMetrics.start(...watchers)
|
|
51
|
-
} catch (
|
|
52
|
-
log.error('Error starting native metrics',
|
|
56
|
+
} catch (error) {
|
|
57
|
+
log.error('Error starting native metrics', error)
|
|
53
58
|
nativeMetrics = null
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
client = new MetricsAggregationClient(new DogStatsDClient(clientConfig))
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
lastTime = performance.now()
|
|
59
64
|
|
|
60
65
|
if (nativeMetrics) {
|
|
61
66
|
interval = setInterval(() => {
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
captureNativeMetrics(trackEventLoop, trackGc)
|
|
68
|
+
captureCommonMetrics(trackEventLoop)
|
|
64
69
|
client.flush()
|
|
65
70
|
}, INTERVAL)
|
|
66
71
|
} else {
|
|
67
|
-
|
|
72
|
+
lastCpuUsage = process.cpuUsage()
|
|
73
|
+
|
|
74
|
+
if (trackEventLoop) {
|
|
75
|
+
eventLoopDelayObserver = monitorEventLoopDelay({ resolution: eventLoopDelayResolution })
|
|
76
|
+
eventLoopDelayObserver.enable()
|
|
77
|
+
}
|
|
68
78
|
|
|
69
79
|
interval = setInterval(() => {
|
|
70
|
-
captureCommonMetrics()
|
|
71
80
|
captureCpuUsage()
|
|
81
|
+
captureCommonMetrics(trackEventLoop)
|
|
72
82
|
captureHeapSpace()
|
|
83
|
+
if (trackEventLoop) {
|
|
84
|
+
// Experimental: The Node.js implementation deviates from the native metrics.
|
|
85
|
+
// We normalize the metrics to the same format but the Node.js values
|
|
86
|
+
// are that way lower than they should be, while they are still nearer
|
|
87
|
+
// to the native ones that way.
|
|
88
|
+
// We use these only as fallback values.
|
|
89
|
+
captureEventLoopDelay()
|
|
90
|
+
}
|
|
73
91
|
client.flush()
|
|
74
92
|
}, INTERVAL)
|
|
75
93
|
}
|
|
@@ -78,12 +96,21 @@ const runtimeMetrics = module.exports = {
|
|
|
78
96
|
},
|
|
79
97
|
|
|
80
98
|
stop () {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
99
|
+
nativeMetrics?.stop()
|
|
100
|
+
nativeMetrics = null
|
|
84
101
|
|
|
85
102
|
clearInterval(interval)
|
|
86
|
-
|
|
103
|
+
interval = null
|
|
104
|
+
|
|
105
|
+
client = null
|
|
106
|
+
lastTime = 0n
|
|
107
|
+
lastCpuUsage = null
|
|
108
|
+
|
|
109
|
+
gcObserver?.disconnect()
|
|
110
|
+
gcObserver = null
|
|
111
|
+
|
|
112
|
+
eventLoopDelayObserver?.disable()
|
|
113
|
+
eventLoopDelayObserver = null
|
|
87
114
|
},
|
|
88
115
|
|
|
89
116
|
track (span) {
|
|
@@ -99,19 +126,19 @@ const runtimeMetrics = module.exports = {
|
|
|
99
126
|
},
|
|
100
127
|
|
|
101
128
|
boolean (name, value, tag) {
|
|
102
|
-
client
|
|
129
|
+
client?.boolean(name, value, tag)
|
|
103
130
|
},
|
|
104
131
|
|
|
105
132
|
histogram (name, value, tag) {
|
|
106
|
-
client
|
|
133
|
+
client?.histogram(name, value, tag)
|
|
107
134
|
},
|
|
108
135
|
|
|
109
136
|
count (name, count, tag, monotonic = false) {
|
|
110
|
-
client
|
|
137
|
+
client?.count(name, count, tag, monotonic)
|
|
111
138
|
},
|
|
112
139
|
|
|
113
140
|
gauge (name, value, tag) {
|
|
114
|
-
client
|
|
141
|
+
client?.gauge(name, value, tag)
|
|
115
142
|
},
|
|
116
143
|
|
|
117
144
|
increment (name, tag, monotonic) {
|
|
@@ -123,30 +150,20 @@ const runtimeMetrics = module.exports = {
|
|
|
123
150
|
}
|
|
124
151
|
}
|
|
125
152
|
|
|
126
|
-
function reset () {
|
|
127
|
-
interval = null
|
|
128
|
-
client = null
|
|
129
|
-
time = null
|
|
130
|
-
cpuUsage = null
|
|
131
|
-
nativeMetrics = null
|
|
132
|
-
gcObserver && gcObserver.disconnect()
|
|
133
|
-
gcObserver = null
|
|
134
|
-
}
|
|
135
|
-
|
|
136
153
|
function captureCpuUsage () {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const elapsedMs = elapsedTime[0] * 1000 + elapsedTime[1] / 1_000_000
|
|
146
|
-
const userPercent = 100 * elapsedUsage.user / 1000 / elapsedMs
|
|
147
|
-
const systemPercent = 100 * elapsedUsage.system / 1000 / elapsedMs
|
|
154
|
+
const currentCpuUsage = process.cpuUsage()
|
|
155
|
+
const elapsedUsageUser = currentCpuUsage.user - lastCpuUsage.user
|
|
156
|
+
const elapsedUsageSystem = currentCpuUsage.system - lastCpuUsage.system
|
|
157
|
+
|
|
158
|
+
const currentTime = performance.now() // Milliseconds with decimal places
|
|
159
|
+
const elapsedUsDividedBy100 = (currentTime - lastTime) * 10
|
|
160
|
+
const userPercent = elapsedUsageUser / elapsedUsDividedBy100
|
|
161
|
+
const systemPercent = elapsedUsageSystem / elapsedUsDividedBy100
|
|
148
162
|
const totalPercent = userPercent + systemPercent
|
|
149
163
|
|
|
164
|
+
lastTime = currentTime
|
|
165
|
+
lastCpuUsage = currentCpuUsage
|
|
166
|
+
|
|
150
167
|
client.gauge('runtime.node.cpu.system', systemPercent.toFixed(2))
|
|
151
168
|
client.gauge('runtime.node.cpu.user', userPercent.toFixed(2))
|
|
152
169
|
client.gauge('runtime.node.cpu.total', totalPercent.toFixed(2))
|
|
@@ -160,12 +177,44 @@ function captureMemoryUsage () {
|
|
|
160
177
|
client.gauge('runtime.node.mem.rss', stats.rss)
|
|
161
178
|
client.gauge('runtime.node.mem.total', os.totalmem())
|
|
162
179
|
client.gauge('runtime.node.mem.free', os.freemem())
|
|
180
|
+
client.gauge('runtime.node.mem.external', stats.external)
|
|
181
|
+
// TODO: Add arrayBuffers to the metrics. That also requires the
|
|
182
|
+
// node_metadata.csv to be updated for the website.
|
|
183
|
+
//
|
|
184
|
+
// client.gauge('runtime.node.mem.arrayBuffers', stats.arrayBuffers)
|
|
185
|
+
}
|
|
163
186
|
|
|
164
|
-
|
|
187
|
+
function captureUptime () {
|
|
188
|
+
// WARNING: lastTime must be updated in the same interval before this function is called!
|
|
189
|
+
// This is a faster `process.uptime()`.
|
|
190
|
+
client.gauge('runtime.node.process.uptime', Math.round((lastTime + 499) / 1000))
|
|
165
191
|
}
|
|
166
192
|
|
|
167
|
-
function
|
|
168
|
-
|
|
193
|
+
function captureEventLoopDelay () {
|
|
194
|
+
eventLoopDelayObserver.disable()
|
|
195
|
+
|
|
196
|
+
if (eventLoopDelayObserver.count !== 0) {
|
|
197
|
+
const minimum = eventLoopDelayResolution * 1e6
|
|
198
|
+
const avg = Math.max(eventLoopDelayObserver.mean - minimum, 0)
|
|
199
|
+
const sum = Math.round(avg * eventLoopDelayObserver.count)
|
|
200
|
+
|
|
201
|
+
if (sum !== 0) {
|
|
202
|
+
// Normalize the metrics to the same format as the native metrics.
|
|
203
|
+
const stats = {
|
|
204
|
+
min: Math.max(eventLoopDelayObserver.min - minimum, 0),
|
|
205
|
+
max: Math.max(eventLoopDelayObserver.max - minimum, 0),
|
|
206
|
+
sum,
|
|
207
|
+
total: sum,
|
|
208
|
+
avg,
|
|
209
|
+
count: eventLoopDelayObserver.count,
|
|
210
|
+
p95: Math.max(eventLoopDelayObserver.percentile(95) - minimum, 0)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
histogram('runtime.node.event_loop.delay', stats)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
eventLoopDelayObserver = monitorEventLoopDelay({ resolution: eventLoopDelayResolution })
|
|
217
|
+
eventLoopDelayObserver.enable()
|
|
169
218
|
}
|
|
170
219
|
|
|
171
220
|
function captureHeapStats () {
|
|
@@ -176,14 +225,17 @@ function captureHeapStats () {
|
|
|
176
225
|
client.gauge('runtime.node.heap.total_physical_size', stats.total_physical_size)
|
|
177
226
|
client.gauge('runtime.node.heap.total_available_size', stats.total_available_size)
|
|
178
227
|
client.gauge('runtime.node.heap.heap_size_limit', stats.heap_size_limit)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
228
|
+
client.gauge('runtime.node.heap.malloced_memory', stats.malloced_memory)
|
|
229
|
+
client.gauge('runtime.node.heap.peak_malloced_memory', stats.peak_malloced_memory)
|
|
230
|
+
// TODO: Add number_of_native_contexts and number_of_detached_contexts to the
|
|
231
|
+
// metrics. Those metrics allow to identify memory leaks. Adding them also
|
|
232
|
+
// requires the node_metadata.csv to be updated for the website.
|
|
233
|
+
//
|
|
234
|
+
// client.gauge('runtime.node.heap.number_of_native_contexts', stats.number_of_native_contexts)
|
|
235
|
+
// client.gauge('runtime.node.heap.number_of_detached_contexts', stats.number_of_detached_contexts)
|
|
182
236
|
}
|
|
183
237
|
|
|
184
238
|
function captureHeapSpace () {
|
|
185
|
-
if (!v8.getHeapSpaceStatistics) return
|
|
186
|
-
|
|
187
239
|
const stats = v8.getHeapSpaceStatistics()
|
|
188
240
|
|
|
189
241
|
for (let i = 0, l = stats.length; i < l; i++) {
|
|
@@ -197,55 +249,63 @@ function captureHeapSpace () {
|
|
|
197
249
|
}
|
|
198
250
|
|
|
199
251
|
/**
|
|
200
|
-
* Gathers and reports Event Loop Utilization (ELU) since last run
|
|
252
|
+
* Gathers and reports Event Loop Utilization (ELU) since last run, or from the
|
|
253
|
+
* start of the process on first run.
|
|
201
254
|
*
|
|
202
255
|
* ELU is a measure of how busy the event loop is, like running JavaScript or
|
|
203
256
|
* waiting on *Sync functions. The value is between 0 (idle) and 1 (exhausted).
|
|
204
|
-
*
|
|
205
|
-
* performance.eventLoopUtilization available in Node.js >= v14.10, >= v12.19, >= v16
|
|
206
257
|
*/
|
|
207
|
-
let
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
// if elu is undefined (first run) the measurement is from start of process
|
|
211
|
-
elu = performance.eventLoopUtilization(elu)
|
|
258
|
+
let lastElu = { idle: 0, active: 0 }
|
|
259
|
+
function captureELU () {
|
|
260
|
+
const elu = performance.eventLoopUtilization()
|
|
212
261
|
|
|
213
|
-
|
|
214
|
-
|
|
262
|
+
const idle = elu.idle - lastElu.idle
|
|
263
|
+
const active = elu.active - lastElu.active
|
|
264
|
+
const utilization = active / (idle + active)
|
|
265
|
+
|
|
266
|
+
lastElu = elu
|
|
267
|
+
|
|
268
|
+
client.gauge('runtime.node.event_loop.utilization', utilization)
|
|
215
269
|
}
|
|
216
270
|
|
|
217
|
-
function captureCommonMetrics () {
|
|
271
|
+
function captureCommonMetrics (trackEventLoop) {
|
|
218
272
|
captureMemoryUsage()
|
|
219
|
-
|
|
273
|
+
captureUptime()
|
|
220
274
|
captureHeapStats()
|
|
221
|
-
|
|
275
|
+
if (trackEventLoop) {
|
|
276
|
+
captureELU()
|
|
277
|
+
}
|
|
222
278
|
}
|
|
223
279
|
|
|
224
|
-
function captureNativeMetrics () {
|
|
280
|
+
function captureNativeMetrics (trackEventLoop, trackGc) {
|
|
225
281
|
const stats = nativeMetrics.stats()
|
|
226
282
|
const spaces = stats.heap.spaces
|
|
227
|
-
const elapsedTime = process.hrtime(time)
|
|
228
283
|
|
|
229
|
-
|
|
284
|
+
const currentTime = performance.now() // Milliseconds with decimal places
|
|
285
|
+
const elapsedUsDividedBy100 = (currentTime - lastTime) * 10
|
|
286
|
+
lastTime = currentTime
|
|
230
287
|
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
const systemPercent = 100 * stats.cpu.system / elapsedUs
|
|
288
|
+
const userPercent = stats.cpu.user / elapsedUsDividedBy100
|
|
289
|
+
const systemPercent = stats.cpu.system / elapsedUsDividedBy100
|
|
234
290
|
const totalPercent = userPercent + systemPercent
|
|
235
291
|
|
|
236
292
|
client.gauge('runtime.node.cpu.system', systemPercent.toFixed(2))
|
|
237
293
|
client.gauge('runtime.node.cpu.user', userPercent.toFixed(2))
|
|
238
294
|
client.gauge('runtime.node.cpu.total', totalPercent.toFixed(2))
|
|
239
295
|
|
|
240
|
-
|
|
296
|
+
if (trackEventLoop) {
|
|
297
|
+
histogram('runtime.node.event_loop.delay', stats.eventLoop)
|
|
298
|
+
}
|
|
241
299
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
300
|
+
if (trackGc) {
|
|
301
|
+
for (const [type, value] of Object.entries(stats.gc)) {
|
|
302
|
+
if (type === 'all') {
|
|
303
|
+
histogram('runtime.node.gc.pause', value)
|
|
304
|
+
} else {
|
|
305
|
+
histogram('runtime.node.gc.pause.by.type', value, `gc_type:${type}`)
|
|
306
|
+
}
|
|
247
307
|
}
|
|
248
|
-
}
|
|
308
|
+
}
|
|
249
309
|
|
|
250
310
|
for (let i = 0, l = spaces.length; i < l; i++) {
|
|
251
311
|
const tag = `heap_space:${spaces[i].space_name}`
|
|
@@ -258,13 +318,19 @@ function captureNativeMetrics () {
|
|
|
258
318
|
}
|
|
259
319
|
|
|
260
320
|
function histogram (name, stats, tag) {
|
|
321
|
+
if (stats.count === 0) {
|
|
322
|
+
return
|
|
323
|
+
}
|
|
261
324
|
client.gauge(`${name}.min`, stats.min, tag)
|
|
262
325
|
client.gauge(`${name}.max`, stats.max, tag)
|
|
263
326
|
client.increment(`${name}.sum`, stats.sum, tag)
|
|
264
327
|
client.increment(`${name}.total`, stats.sum, tag)
|
|
265
328
|
client.gauge(`${name}.avg`, stats.avg, tag)
|
|
266
329
|
client.increment(`${name}.count`, stats.count, tag)
|
|
267
|
-
|
|
330
|
+
if (stats.median !== undefined) {
|
|
331
|
+
// TODO: Consider adding the median to the Node.js histogram/adding stddev to native metrics.
|
|
332
|
+
client.gauge(`${name}.median`, stats.median, tag)
|
|
333
|
+
}
|
|
268
334
|
client.gauge(`${name}.95percentile`, stats.p95, tag)
|
|
269
335
|
}
|
|
270
336
|
|
|
@@ -276,42 +342,25 @@ function startGCObserver () {
|
|
|
276
342
|
const type = gcType(entry.detail?.kind || entry.kind)
|
|
277
343
|
const duration = entry.duration * 1_000_000
|
|
278
344
|
|
|
279
|
-
|
|
280
|
-
|
|
345
|
+
// These are individual metrics for each type of GC.
|
|
346
|
+
client.histogram('runtime.node.gc.pause.by.type', duration, `gc_type:${type}`)
|
|
347
|
+
client.histogram('runtime.node.gc.pause', duration)
|
|
281
348
|
}
|
|
282
349
|
})
|
|
283
350
|
|
|
284
351
|
gcObserver.observe({ type: 'gc' })
|
|
285
352
|
}
|
|
286
353
|
|
|
354
|
+
const minorGCType = NODE_MAJOR >= 22 ? 'minor_mark_sweep' : 'minor_mark_compact'
|
|
355
|
+
|
|
287
356
|
function gcType (kind) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
} else if (NODE_MAJOR >= 18) {
|
|
298
|
-
switch (kind) {
|
|
299
|
-
case 1: return 'scavenge'
|
|
300
|
-
case 2: return 'minor_mark_compact'
|
|
301
|
-
case 4: return 'mark_sweep_compact'
|
|
302
|
-
case 8: return 'incremental_marking'
|
|
303
|
-
case 16: return 'process_weak_callbacks'
|
|
304
|
-
case 31: return 'all'
|
|
305
|
-
}
|
|
306
|
-
} else {
|
|
307
|
-
switch (kind) {
|
|
308
|
-
case 1: return 'scavenge'
|
|
309
|
-
case 2: return 'mark_sweep_compact'
|
|
310
|
-
case 4: return 'incremental_marking'
|
|
311
|
-
case 8: return 'process_weak_callbacks'
|
|
312
|
-
case 15: return 'all'
|
|
313
|
-
}
|
|
357
|
+
switch (kind) {
|
|
358
|
+
case 1: return 'scavenge'
|
|
359
|
+
case 2: return minorGCType
|
|
360
|
+
case 4: return 'mark_sweep_compact' // Deprecated, might be removed soon.
|
|
361
|
+
case 8: return 'incremental_marking'
|
|
362
|
+
case 16: return 'process_weak_callbacks'
|
|
363
|
+
case 31: return 'all'
|
|
364
|
+
default: return 'unknown'
|
|
314
365
|
}
|
|
315
|
-
|
|
316
|
-
return 'unknown'
|
|
317
366
|
}
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"DD_APPSEC_STACK_TRACE_ENABLED": ["A"],
|
|
32
32
|
"DD_APPSEC_TRACE_RATE_LIMIT": ["A"],
|
|
33
33
|
"DD_APPSEC_WAF_TIMEOUT": ["A"],
|
|
34
|
-
"DD_AZURE_APP_SERVICES": ["A"],
|
|
35
34
|
"DD_CIVISIBILITY_AGENTLESS_ENABLED": ["A"],
|
|
36
35
|
"DD_CIVISIBILITY_AGENTLESS_URL": ["A"],
|
|
37
36
|
"DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER": ["A"],
|