dd-trace 5.106.0 → 5.107.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/index.d.ts +20 -1
- package/package.json +5 -7
- package/packages/datadog-core/src/storage.js +47 -48
- package/packages/datadog-esbuild/index.js +6 -1
- package/packages/datadog-instrumentations/src/ai.js +12 -3
- package/packages/datadog-instrumentations/src/body-parser.js +5 -2
- package/packages/datadog-instrumentations/src/connect.js +3 -2
- package/packages/datadog-instrumentations/src/cookie-parser.js +3 -2
- package/packages/datadog-instrumentations/src/cucumber.js +7 -0
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +7 -5
- package/packages/datadog-instrumentations/src/express-session.js +12 -11
- package/packages/datadog-instrumentations/src/express.js +24 -20
- package/packages/datadog-instrumentations/src/fastify.js +18 -6
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +27 -12
- package/packages/datadog-instrumentations/src/http/client.js +9 -12
- package/packages/datadog-instrumentations/src/http/server.js +30 -16
- package/packages/datadog-instrumentations/src/http2/client.js +15 -12
- package/packages/datadog-instrumentations/src/http2/server.js +15 -8
- package/packages/datadog-instrumentations/src/jest/bail-reporter.js +42 -0
- package/packages/datadog-instrumentations/src/jest.js +143 -73
- package/packages/datadog-instrumentations/src/mocha/main.js +43 -8
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -17
- package/packages/datadog-instrumentations/src/multer.js +3 -2
- package/packages/datadog-instrumentations/src/mysql2.js +34 -0
- package/packages/datadog-instrumentations/src/net.js +8 -6
- package/packages/datadog-instrumentations/src/openai.js +19 -7
- package/packages/datadog-instrumentations/src/pg.js +19 -0
- package/packages/datadog-instrumentations/src/router.js +12 -10
- package/packages/datadog-instrumentations/src/vitest.js +29 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +0 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +62 -11
- package/packages/datadog-plugin-cucumber/src/index.js +2 -0
- package/packages/datadog-plugin-cypress/src/support.js +31 -1
- package/packages/datadog-plugin-http/src/client.js +0 -3
- package/packages/datadog-plugin-http/src/server.js +11 -1
- package/packages/datadog-plugin-mocha/src/index.js +2 -0
- package/packages/datadog-plugin-pg/src/index.js +10 -0
- package/packages/dd-trace/src/aiguard/index.js +34 -15
- package/packages/dd-trace/src/aiguard/sdk.js +34 -3
- package/packages/dd-trace/src/aiguard/tags.js +6 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +14 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +1 -1
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +5 -9
- package/packages/dd-trace/src/config/parsers.js +8 -0
- package/packages/dd-trace/src/config/supported-configurations.json +13 -6
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/writer.js +1 -2
- package/packages/dd-trace/src/debugger/config.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -2
- package/packages/dd-trace/src/debugger/index.js +1 -2
- package/packages/dd-trace/src/dogstatsd.js +2 -3
- package/packages/dd-trace/src/encode/0.4.js +49 -41
- package/packages/dd-trace/src/encode/agentless-json.js +5 -1
- package/packages/dd-trace/src/encode/tags-processors.js +14 -0
- package/packages/dd-trace/src/exporters/agent/index.js +1 -2
- package/packages/dd-trace/src/exporters/agentless/index.js +6 -10
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +1 -2
- package/packages/dd-trace/src/exporters/common/request.js +26 -0
- package/packages/dd-trace/src/exporters/span-stats/index.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +4 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +45 -0
- package/packages/dd-trace/src/llmobs/sdk.js +4 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +17 -1
- package/packages/dd-trace/src/llmobs/tagger.js +5 -3
- package/packages/dd-trace/src/llmobs/util.js +54 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -2
- package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
- package/packages/dd-trace/src/openfeature/writers/base.js +1 -10
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -2
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +26 -13
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +7 -10
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +92 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +3 -2
- package/packages/dd-trace/src/opentracing/span.js +23 -18
- package/packages/dd-trace/src/opentracing/tracer.js +16 -12
- package/packages/dd-trace/src/plugins/ci_plugin.js +131 -46
- package/packages/dd-trace/src/priority_sampler.js +6 -5
- package/packages/dd-trace/src/profiling/config.js +1 -2
- package/packages/dd-trace/src/proxy.js +13 -10
- package/packages/dd-trace/src/remote_config/index.js +1 -2
- package/packages/dd-trace/src/runtime_metrics/client.js +30 -0
- package/packages/dd-trace/src/runtime_metrics/index.js +12 -2
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +284 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -11
- package/packages/dd-trace/src/service-naming/source-resolver.js +5 -1
- package/packages/dd-trace/src/span_format.js +33 -25
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/startup-log.js +1 -2
- package/packages/dd-trace/src/telemetry/send-data.js +1 -1
- package/packages/dd-trace/src/tracer.js +1 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +2 -2
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/dd-trace/src/agent/url.js +0 -28
- package/scripts/preinstall.js +0 -34
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const v8 = require('node:v8')
|
|
4
|
+
const process = require('node:process')
|
|
5
|
+
const { performance, monitorEventLoopDelay, PerformanceObserver, constants } = require('node:perf_hooks')
|
|
6
|
+
const { metrics } = require('@opentelemetry/api')
|
|
7
|
+
const log = require('../log')
|
|
8
|
+
const { createMetricsClient } = require('./client')
|
|
9
|
+
|
|
10
|
+
const METER_NAME = 'datadog.runtime_metrics'
|
|
11
|
+
|
|
12
|
+
const ATTR_ELU_STATE_IDLE = { 'nodejs.eventloop.state': 'idle' }
|
|
13
|
+
const ATTR_ELU_STATE_ACTIVE = { 'nodejs.eventloop.state': 'active' }
|
|
14
|
+
|
|
15
|
+
// Pre-allocated `{ 'v8js.gc.type': <type> }` attribute objects so the observer
|
|
16
|
+
// doesn't allocate a new one per entry under GC pressure.
|
|
17
|
+
const ATTR_GC_TYPE_MINOR = { 'v8js.gc.type': 'minor' }
|
|
18
|
+
const ATTR_GC_TYPE_MAJOR = { 'v8js.gc.type': 'major' }
|
|
19
|
+
const ATTR_GC_TYPE_INCREMENTAL = { 'v8js.gc.type': 'incremental' }
|
|
20
|
+
const ATTR_GC_TYPE_WEAKCB = { 'v8js.gc.type': 'weakcb' }
|
|
21
|
+
|
|
22
|
+
// Kind 2 is V8's MinorMarkSweep (Node 20+) and not exposed via perf_hooks.constants.
|
|
23
|
+
const GC_ATTR_BY_KIND = new Map([
|
|
24
|
+
[constants.NODE_PERFORMANCE_GC_MINOR, ATTR_GC_TYPE_MINOR],
|
|
25
|
+
[2, ATTR_GC_TYPE_MINOR],
|
|
26
|
+
[constants.NODE_PERFORMANCE_GC_MAJOR, ATTR_GC_TYPE_MAJOR],
|
|
27
|
+
[constants.NODE_PERFORMANCE_GC_INCREMENTAL, ATTR_GC_TYPE_INCREMENTAL],
|
|
28
|
+
[constants.NODE_PERFORMANCE_GC_WEAKCB, ATTR_GC_TYPE_WEAKCB],
|
|
29
|
+
])
|
|
30
|
+
|
|
31
|
+
let meter = null
|
|
32
|
+
let eventLoopHistogram = null
|
|
33
|
+
let gcObserver = null
|
|
34
|
+
let lastElu = null
|
|
35
|
+
|
|
36
|
+
// Cache `{ 'v8js.heap.space.name': <name> }` per V8 space name to avoid per-scrape allocations.
|
|
37
|
+
const HEAP_SPACE_ATTR_CACHE = new Map()
|
|
38
|
+
function getHeapSpaceAttr (name) {
|
|
39
|
+
let attr = HEAP_SPACE_ATTR_CACHE.get(name)
|
|
40
|
+
if (!attr) {
|
|
41
|
+
attr = { 'v8js.heap.space.name': name }
|
|
42
|
+
HEAP_SPACE_ATTR_CACHE.set(name, attr)
|
|
43
|
+
}
|
|
44
|
+
return attr
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// getMeter() returns a cached meter, so without tracking what we registered we'd
|
|
48
|
+
// stack callbacks every time start() runs.
|
|
49
|
+
const registeredCallbacks = []
|
|
50
|
+
const registeredBatchCallbacks = []
|
|
51
|
+
|
|
52
|
+
// DD-proprietary tracer metrics (runtime.node.spans.*, datadog.tracer.*) have no OTel
|
|
53
|
+
// equivalent; keep a DogStatsD client so OTLP-path customers don't lose them.
|
|
54
|
+
let client = null
|
|
55
|
+
let flushInterval = null
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
/**
|
|
59
|
+
* @param {import('../config/config-base')} config - Tracer configuration
|
|
60
|
+
*/
|
|
61
|
+
start (config) {
|
|
62
|
+
this.stop()
|
|
63
|
+
|
|
64
|
+
client = createMetricsClient(config)
|
|
65
|
+
flushInterval = setInterval(() => {
|
|
66
|
+
client.flush()
|
|
67
|
+
}, config.DD_RUNTIME_METRICS_FLUSH_INTERVAL ?? 10_000)
|
|
68
|
+
flushInterval.unref?.()
|
|
69
|
+
|
|
70
|
+
meter = metrics.getMeterProvider().getMeter(METER_NAME)
|
|
71
|
+
|
|
72
|
+
const trackEventLoop = config.runtimeMetrics.eventLoop !== false
|
|
73
|
+
const trackGc = config.runtimeMetrics.gc !== false
|
|
74
|
+
if (trackEventLoop) {
|
|
75
|
+
eventLoopHistogram = monitorEventLoopDelay({ resolution: 4 })
|
|
76
|
+
eventLoopHistogram.enable()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const heapUsed = createHeapInstrument('v8js.memory.heap.used', 'V8 heap memory used.')
|
|
80
|
+
const heapLimit = createHeapInstrument('v8js.memory.heap.limit', 'V8 heap memory total available size.')
|
|
81
|
+
const heapSpaceAvailable = createHeapInstrument(
|
|
82
|
+
'v8js.memory.heap.space.available_size', 'V8 heap space available size.')
|
|
83
|
+
const heapSpacePhysical = createHeapInstrument(
|
|
84
|
+
'v8js.memory.heap.space.physical_size', 'V8 heap space physical size.')
|
|
85
|
+
const heapSpaceSize = createHeapInstrument(
|
|
86
|
+
'v8js.memory.heap.space.size', 'Total heap memory size pre-allocated for a heap space.')
|
|
87
|
+
|
|
88
|
+
registerBatchCallback(
|
|
89
|
+
(result) => {
|
|
90
|
+
const stats = v8.getHeapStatistics()
|
|
91
|
+
result.observe(heapLimit, stats.heap_size_limit)
|
|
92
|
+
|
|
93
|
+
const spaces = v8.getHeapSpaceStatistics()
|
|
94
|
+
for (let i = 0; i < spaces.length; i++) {
|
|
95
|
+
const space = spaces[i]
|
|
96
|
+
const attr = getHeapSpaceAttr(space.space_name)
|
|
97
|
+
result.observe(heapUsed, space.space_used_size, attr)
|
|
98
|
+
result.observe(heapSpaceAvailable, space.space_available_size, attr)
|
|
99
|
+
result.observe(heapSpacePhysical, space.physical_space_size, attr)
|
|
100
|
+
result.observe(heapSpaceSize, space.space_size, attr)
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
[heapUsed, heapLimit, heapSpaceAvailable, heapSpacePhysical, heapSpaceSize]
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
const activeResource = meter.createObservableGauge('v8js.resource.active', {
|
|
107
|
+
unit: '{resource}',
|
|
108
|
+
description: 'Gauge of the active resources that are currently keeping the event loop alive.',
|
|
109
|
+
})
|
|
110
|
+
registerCallback((result) => {
|
|
111
|
+
const counts = new Map()
|
|
112
|
+
// Stable since Node 22.16; available on 18+ as experimental.
|
|
113
|
+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
114
|
+
for (const resource of process.getActiveResourcesInfo()) {
|
|
115
|
+
counts.set(resource, (counts.get(resource) ?? 0) + 1)
|
|
116
|
+
}
|
|
117
|
+
for (const [type, count] of counts) {
|
|
118
|
+
result.observe(count, { 'v8js.resource.type': type })
|
|
119
|
+
}
|
|
120
|
+
}, activeResource)
|
|
121
|
+
|
|
122
|
+
// Spec wants nodejs.eventloop.delay.* in seconds; perf_hooks gives nanoseconds.
|
|
123
|
+
// Match @opentelemetry/instrumentation-runtime-node EventLoopDelayCollector: one batch
|
|
124
|
+
// callback, guard on sample count, emit, then reset so each interval is independent.
|
|
125
|
+
if (trackEventLoop) {
|
|
126
|
+
const delayMin = createDelayGauge('nodejs.eventloop.delay.min', 'Event loop minimum delay.')
|
|
127
|
+
const delayMax = createDelayGauge('nodejs.eventloop.delay.max', 'Event loop maximum delay.')
|
|
128
|
+
const delayMean = createDelayGauge('nodejs.eventloop.delay.mean', 'Event loop mean delay.')
|
|
129
|
+
const delayStddev = createDelayGauge('nodejs.eventloop.delay.stddev', 'Event loop standard deviation delay.')
|
|
130
|
+
const delayP50 = createDelayGauge('nodejs.eventloop.delay.p50', 'Event loop 50th percentile delay.')
|
|
131
|
+
const delayP90 = createDelayGauge('nodejs.eventloop.delay.p90', 'Event loop 90th percentile delay.')
|
|
132
|
+
const delayP99 = createDelayGauge('nodejs.eventloop.delay.p99', 'Event loop 99th percentile delay.')
|
|
133
|
+
|
|
134
|
+
registerBatchCallback((result) => {
|
|
135
|
+
const h = eventLoopHistogram
|
|
136
|
+
if (!h || h.count < 5) return
|
|
137
|
+
result.observe(delayMin, h.min / 1e9)
|
|
138
|
+
result.observe(delayMax, h.max / 1e9)
|
|
139
|
+
result.observe(delayMean, h.mean / 1e9)
|
|
140
|
+
result.observe(delayStddev, h.stddev / 1e9)
|
|
141
|
+
result.observe(delayP50, h.percentile(50) / 1e9)
|
|
142
|
+
result.observe(delayP90, h.percentile(90) / 1e9)
|
|
143
|
+
result.observe(delayP99, h.percentile(99) / 1e9)
|
|
144
|
+
h.reset()
|
|
145
|
+
}, [delayMin, delayMax, delayMean, delayStddev, delayP50, delayP90, delayP99])
|
|
146
|
+
|
|
147
|
+
if (performance.eventLoopUtilization) {
|
|
148
|
+
// Baseline so the first observation isn't 1.0.
|
|
149
|
+
lastElu = performance.eventLoopUtilization()
|
|
150
|
+
|
|
151
|
+
const eluTime = meter.createObservableCounter('nodejs.eventloop.time', {
|
|
152
|
+
unit: 's',
|
|
153
|
+
description: 'Cumulative duration of time the event loop has been in each state.',
|
|
154
|
+
})
|
|
155
|
+
registerCallback((result) => {
|
|
156
|
+
const elu = performance.eventLoopUtilization()
|
|
157
|
+
result.observe(elu.idle / 1000, ATTR_ELU_STATE_IDLE)
|
|
158
|
+
result.observe(elu.active / 1000, ATTR_ELU_STATE_ACTIVE)
|
|
159
|
+
}, eluTime)
|
|
160
|
+
|
|
161
|
+
const eluGauge = meter.createObservableGauge('nodejs.eventloop.utilization', {
|
|
162
|
+
unit: '1',
|
|
163
|
+
description: 'Event loop utilization.',
|
|
164
|
+
})
|
|
165
|
+
registerCallback((result) => {
|
|
166
|
+
const current = performance.eventLoopUtilization()
|
|
167
|
+
const idle = current.idle - lastElu.idle
|
|
168
|
+
const active = current.active - lastElu.active
|
|
169
|
+
lastElu = current
|
|
170
|
+
const total = idle + active
|
|
171
|
+
result.observe(total > 0 ? active / total : 0)
|
|
172
|
+
}, eluGauge)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (trackGc) {
|
|
177
|
+
const gcHistogram = meter.createHistogram('v8js.gc.duration', {
|
|
178
|
+
unit: 's',
|
|
179
|
+
description: 'Garbage collection duration.',
|
|
180
|
+
})
|
|
181
|
+
gcObserver = new PerformanceObserver(list => {
|
|
182
|
+
const entries = list.getEntries()
|
|
183
|
+
for (let i = 0; i < entries.length; i++) {
|
|
184
|
+
const entry = entries[i]
|
|
185
|
+
const attr = GC_ATTR_BY_KIND.get(entry.detail?.kind ?? entry.kind)
|
|
186
|
+
if (attr === undefined) continue
|
|
187
|
+
gcHistogram.record(entry.duration / 1000, attr)
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
gcObserver.observe({ type: 'gc' })
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
log.debug('Started OTLP runtime metrics with OTel-native naming (v8js.*, nodejs.*)')
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @returns {void}
|
|
198
|
+
*/
|
|
199
|
+
stop () {
|
|
200
|
+
if (eventLoopHistogram) {
|
|
201
|
+
eventLoopHistogram.disable()
|
|
202
|
+
eventLoopHistogram = null
|
|
203
|
+
}
|
|
204
|
+
gcObserver?.disconnect()
|
|
205
|
+
gcObserver = null
|
|
206
|
+
for (let i = 0; i < registeredCallbacks.length; i++) {
|
|
207
|
+
const [callback, instrument] = registeredCallbacks[i]
|
|
208
|
+
instrument.removeCallback(callback)
|
|
209
|
+
}
|
|
210
|
+
registeredCallbacks.length = 0
|
|
211
|
+
if (meter) {
|
|
212
|
+
for (let i = 0; i < registeredBatchCallbacks.length; i++) {
|
|
213
|
+
const [callback, instruments] = registeredBatchCallbacks[i]
|
|
214
|
+
meter.removeBatchObservableCallback(callback, instruments)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
registeredBatchCallbacks.length = 0
|
|
218
|
+
meter = null
|
|
219
|
+
lastElu = null
|
|
220
|
+
if (flushInterval) {
|
|
221
|
+
clearInterval(flushInterval)
|
|
222
|
+
flushInterval = null
|
|
223
|
+
}
|
|
224
|
+
client = null
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
// Tied to @datadog/native-metrics which the OTLP path doesn't enable; noop with expected shape.
|
|
228
|
+
track () { return { finish () {} } },
|
|
229
|
+
|
|
230
|
+
boolean (name, value, tag) {
|
|
231
|
+
client?.boolean(name, value, tag)
|
|
232
|
+
},
|
|
233
|
+
histogram (name, value, tag) {
|
|
234
|
+
client?.histogram(name, value, tag)
|
|
235
|
+
},
|
|
236
|
+
count (name, count, tag, monotonic = false) {
|
|
237
|
+
client?.count(name, count, tag, monotonic)
|
|
238
|
+
},
|
|
239
|
+
gauge (name, value, tag) {
|
|
240
|
+
client?.gauge(name, value, tag)
|
|
241
|
+
},
|
|
242
|
+
increment (name, tag, monotonic) {
|
|
243
|
+
this.count(name, 1, tag, monotonic)
|
|
244
|
+
},
|
|
245
|
+
decrement (name, tag) {
|
|
246
|
+
this.count(name, -1, tag)
|
|
247
|
+
},
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* @param {Function} callback
|
|
252
|
+
* @param {object} instrument
|
|
253
|
+
*/
|
|
254
|
+
function registerCallback (callback, instrument) {
|
|
255
|
+
instrument.addCallback(callback)
|
|
256
|
+
registeredCallbacks.push([callback, instrument])
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @param {Function} callback
|
|
261
|
+
* @param {Array} instruments
|
|
262
|
+
*/
|
|
263
|
+
function registerBatchCallback (callback, instruments) {
|
|
264
|
+
meter.addBatchObservableCallback(callback, instruments)
|
|
265
|
+
registeredBatchCallbacks.push([callback, instruments])
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @param {string} name
|
|
270
|
+
* @param {string} description
|
|
271
|
+
* @returns {object}
|
|
272
|
+
*/
|
|
273
|
+
function createHeapInstrument (name, description) {
|
|
274
|
+
return meter.createObservableUpDownCounter(name, { unit: 'By', description })
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @param {string} name
|
|
279
|
+
* @param {string} description
|
|
280
|
+
* @returns {object}
|
|
281
|
+
*/
|
|
282
|
+
function createDelayGauge (name, description) {
|
|
283
|
+
return meter.createObservableGauge(name, { unit: 's', description })
|
|
284
|
+
}
|
|
@@ -6,11 +6,9 @@ const v8 = require('v8')
|
|
|
6
6
|
const os = require('os')
|
|
7
7
|
const process = require('process')
|
|
8
8
|
const { performance, PerformanceObserver, monitorEventLoopDelay } = require('perf_hooks')
|
|
9
|
-
const { DogStatsDClient, MetricsAggregationClient } = require('../dogstatsd')
|
|
10
9
|
const log = require('../log')
|
|
11
|
-
|
|
12
10
|
const { NODE_MAJOR } = require('../../../../version')
|
|
13
|
-
const
|
|
11
|
+
const { createMetricsClient } = require('./client')
|
|
14
12
|
|
|
15
13
|
const eventLoopDelayResolution = 4
|
|
16
14
|
|
|
@@ -37,18 +35,11 @@ module.exports = {
|
|
|
37
35
|
this.stop()
|
|
38
36
|
// The agent expects a flush every ten seconds, so this is for tests only.
|
|
39
37
|
const flushIntervalMs = config.DD_RUNTIME_METRICS_FLUSH_INTERVAL
|
|
40
|
-
const clientConfig = DogStatsDClient.generateClientConfig(config)
|
|
41
|
-
|
|
42
|
-
if (config.DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED) {
|
|
43
|
-
for (const tag of processTags.tagsArray) {
|
|
44
|
-
clientConfig.tags.push(tag)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
38
|
|
|
48
39
|
const trackEventLoop = config.runtimeMetrics.eventLoop !== false
|
|
49
40
|
const trackGc = config.runtimeMetrics.gc !== false
|
|
50
41
|
|
|
51
|
-
client =
|
|
42
|
+
client = createMetricsClient(config)
|
|
52
43
|
|
|
53
44
|
if (trackGc) {
|
|
54
45
|
startGCObserver()
|
|
@@ -26,7 +26,11 @@ function resolveServiceSource (span, tracerService) {
|
|
|
26
26
|
|
|
27
27
|
if (currentService === tracerService) {
|
|
28
28
|
if (existingSource === undefined) return
|
|
29
|
-
|
|
29
|
+
// Clear by assigning undefined rather than deleting: `delete` on the plain
|
|
30
|
+
// `_tags` object drops it into dictionary (slow) mode, so the per-span
|
|
31
|
+
// extractTags scan that follows pays the slow path. The encode loop skips
|
|
32
|
+
// undefined values, so the emitted meta is unchanged.
|
|
33
|
+
spanContext.setTag(SVC_SRC_KEY, undefined)
|
|
30
34
|
return
|
|
31
35
|
}
|
|
32
36
|
|
|
@@ -48,9 +48,10 @@ const { IGNORE_OTEL_ERROR } = constants
|
|
|
48
48
|
* @property {Array} links
|
|
49
49
|
* @property {Array<SpanEvent> | undefined} span_events
|
|
50
50
|
*
|
|
51
|
-
* @typedef {object} SpanEvent
|
|
51
|
+
* @typedef {object} SpanEvent Raw span event as stored on the span; the encoder
|
|
52
|
+
* layer derives `time_unix_nano` from `startTime` via `eventTimeNano`.
|
|
52
53
|
* @property {string} name
|
|
53
|
-
* @property {number}
|
|
54
|
+
* @property {number} startTime Milliseconds with sub-millisecond precision.
|
|
54
55
|
* @property {Record<string, string>} [attributes]
|
|
55
56
|
*/
|
|
56
57
|
|
|
@@ -88,7 +89,6 @@ function formatSpan (span) {
|
|
|
88
89
|
metrics: {},
|
|
89
90
|
start: Math.round(span._startTime * 1e6),
|
|
90
91
|
duration: Math.round(span._duration * 1e6),
|
|
91
|
-
links: [],
|
|
92
92
|
span_events: undefined,
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -112,24 +112,32 @@ function setSingleSpanIngestionTags (formattedSpan, options) {
|
|
|
112
112
|
* @param {import('./opentracing/span')} span
|
|
113
113
|
*/
|
|
114
114
|
function extractSpanLinks (formattedSpan, span) {
|
|
115
|
-
|
|
115
|
+
const links = span._links
|
|
116
|
+
if (!links?.length) {
|
|
116
117
|
return
|
|
117
118
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
// Build the `_dd.span_links` JSON directly. The trace / span ids are decimal
|
|
120
|
+
// strings (no escaping); attributes are pre-sanitized to a string map and
|
|
121
|
+
// `undefined` when empty, so they only need a presence check. Avoids the
|
|
122
|
+
// throwaway array of formatted-link objects the previous `map` allocated and
|
|
123
|
+
// the second walk `JSON.stringify` does over them.
|
|
124
|
+
let serialized = '['
|
|
125
|
+
for (let i = 0; i < links.length; i++) {
|
|
126
|
+
if (i > 0) serialized += ','
|
|
127
|
+
const { context, attributes } = links[i]
|
|
128
|
+
serialized += `{"trace_id":"${context.toTraceId(true)}","span_id":"${context.toSpanId(true)}"`
|
|
129
|
+
if (attributes !== undefined) {
|
|
130
|
+
serialized += `,"attributes":${JSON.stringify(attributes)}`
|
|
122
131
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
formattedLink.attributes = attributes
|
|
132
|
+
if (context?._sampling?.priority >= 0) {
|
|
133
|
+
serialized += `,"flags":${context._sampling.priority > 0 ? 1 : 0}`
|
|
126
134
|
}
|
|
127
|
-
if (context?.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
135
|
+
if (context?._tracestate) {
|
|
136
|
+
serialized += `,"tracestate":${JSON.stringify(context._tracestate.toString())}`
|
|
137
|
+
}
|
|
138
|
+
serialized += '}'
|
|
139
|
+
}
|
|
140
|
+
serialized += ']'
|
|
133
141
|
if (serialized.length > MAX_META_VALUE_LENGTH) {
|
|
134
142
|
serialized = `${serialized.slice(0, MAX_META_VALUE_LENGTH)}...`
|
|
135
143
|
}
|
|
@@ -137,6 +145,12 @@ function extractSpanLinks (formattedSpan, span) {
|
|
|
137
145
|
}
|
|
138
146
|
|
|
139
147
|
/**
|
|
148
|
+
* Hand the raw `_events` array to the encoder layer instead of copying it into
|
|
149
|
+
* reshaped `{ name, time_unix_nano, attributes }` objects. Each encoder derives
|
|
150
|
+
* `time_unix_nano` from `event.startTime` via `eventTimeNano` and drops empty
|
|
151
|
+
* attribute objects itself, so the per-event allocation here is pure waste on
|
|
152
|
+
* every event-bearing span.
|
|
153
|
+
*
|
|
140
154
|
* @param {FormattedSpan} formattedSpan
|
|
141
155
|
* @param {import('./opentracing/span')} span
|
|
142
156
|
*/
|
|
@@ -144,13 +158,7 @@ function extractSpanEvents (formattedSpan, span) {
|
|
|
144
158
|
if (!span._events?.length) {
|
|
145
159
|
return
|
|
146
160
|
}
|
|
147
|
-
formattedSpan.span_events = span._events
|
|
148
|
-
return {
|
|
149
|
-
name: event.name,
|
|
150
|
-
time_unix_nano: Math.round(event.startTime * 1e6),
|
|
151
|
-
attributes: event.attributes && Object.keys(event.attributes).length > 0 ? event.attributes : undefined,
|
|
152
|
-
}
|
|
153
|
-
})
|
|
161
|
+
formattedSpan.span_events = span._events
|
|
154
162
|
}
|
|
155
163
|
|
|
156
164
|
function extractTags (formattedSpan, span) {
|
|
@@ -168,7 +176,7 @@ function extractTags (formattedSpan, span) {
|
|
|
168
176
|
metrics[MEASURED] = 1
|
|
169
177
|
}
|
|
170
178
|
|
|
171
|
-
const tracerService = span.tracer().
|
|
179
|
+
const tracerService = span.tracer().serviceLower
|
|
172
180
|
if (tags['service.name']?.toLowerCase() !== tracerService) {
|
|
173
181
|
span.setTag(BASE_SERVICE, tracerService)
|
|
174
182
|
|
|
@@ -190,7 +190,7 @@ class SpanStatsProcessor {
|
|
|
190
190
|
if (!this.enabled) return
|
|
191
191
|
if (!span.metrics[TOP_LEVEL_KEY] && !span.metrics[MEASURED]) return
|
|
192
192
|
|
|
193
|
-
const spanEndNs = span.
|
|
193
|
+
const spanEndNs = span.start + span.duration
|
|
194
194
|
const bucketTime = spanEndNs - (spanEndNs % this.bucketSizeNs)
|
|
195
195
|
|
|
196
196
|
this.buckets.forTime(bucketTime)
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
const os = require('os')
|
|
4
4
|
const { inspect } = require('util')
|
|
5
5
|
const tracerVersion = require('../../../package.json').version
|
|
6
|
-
const { getAgentUrl } = require('./agent/url')
|
|
7
6
|
const { warn } = require('./log/writer')
|
|
8
7
|
|
|
9
8
|
const errors = {}
|
|
@@ -76,7 +75,7 @@ function logGenericError (message) {
|
|
|
76
75
|
* @returns {Record<string, unknown>}
|
|
77
76
|
*/
|
|
78
77
|
function configInfo () {
|
|
79
|
-
const url =
|
|
78
|
+
const url = config.url
|
|
80
79
|
|
|
81
80
|
return {
|
|
82
81
|
[inspect.custom] () {
|
|
@@ -146,7 +146,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
|
|
|
146
146
|
|
|
147
147
|
if (isCiVisibilityAgentlessMode) {
|
|
148
148
|
try {
|
|
149
|
-
url
|
|
149
|
+
url = config.DD_CIVISIBILITY_AGENTLESS_URL ?? new URL(getAgentlessTelemetryEndpoint(config.site))
|
|
150
150
|
} catch (err) {
|
|
151
151
|
log.error('Telemetry endpoint url is invalid', err)
|
|
152
152
|
// No point to do the request if the URL is invalid
|
|
@@ -9,7 +9,7 @@ const { isError } = require('./util')
|
|
|
9
9
|
const { setStartupLogConfig } = require('./startup-log')
|
|
10
10
|
const { DataStreamsCheckpointer, DataStreamsManager, DataStreamsProcessor } = require('./datastreams')
|
|
11
11
|
const { IS_SERVERLESS } = require('./serverless')
|
|
12
|
-
const log = require('./log
|
|
12
|
+
const log = require('./log')
|
|
13
13
|
|
|
14
14
|
const SPAN_TYPE = tags.SPAN_TYPE
|
|
15
15
|
const RESOURCE_NAME = tags.RESOURCE_NAME
|