dd-trace 6.12.0 → 6.13.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/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- package/index.d.ts +127 -26
- package/package.json +10 -10
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +28 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/jest.js +140 -18
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +7 -0
- package/packages/datadog-instrumentations/src/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright.js +34 -20
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -4
- package/packages/datadog-instrumentations/src/vitest-worker.js +11 -0
- package/packages/datadog-instrumentations/src/webdriverio.js +26 -0
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +9 -1
- package/packages/datadog-plugin-undici/src/index.js +2 -15
- package/packages/dd-trace/src/aiguard/evaluation.js +94 -20
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +21 -9
- package/packages/dd-trace/src/aiguard/noop.js +9 -1
- package/packages/dd-trace/src/aiguard/redaction.js +183 -0
- package/packages/dd-trace/src/aiguard/sdk.js +7 -2
- package/packages/dd-trace/src/aiguard/tags.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +51 -26
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -11
- package/packages/dd-trace/src/ci-visibility/final-flush.js +20 -0
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +196 -29
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -0
- package/packages/dd-trace/src/config/index.js +2 -1
- package/packages/dd-trace/src/config/supported-configurations.json +27 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -3
- package/packages/dd-trace/src/dogstatsd.js +45 -18
- package/packages/dd-trace/src/encode/agentless-json.js +5 -4
- package/packages/dd-trace/src/exporters/agent/index.js +22 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +4 -4
- package/packages/dd-trace/src/exporters/common/writer.js +24 -2
- package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -3
- package/packages/dd-trace/src/flush.js +102 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +51 -2
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
- package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
- package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
- package/packages/dd-trace/src/llmobs/index.js +80 -17
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
- package/packages/dd-trace/src/llmobs/writers/base.js +60 -24
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +4 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +3 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
- package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +410 -342
- package/packages/dd-trace/src/opentracing/span.js +28 -0
- package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +74 -21
- package/packages/dd-trace/src/plugins/util/web.js +2 -65
- package/packages/dd-trace/src/profiling/oom.js +4 -3
- package/packages/dd-trace/src/profiling/profiler.js +7 -0
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/proxy.js +36 -8
- package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +17 -6
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +55 -0
- package/packages/dd-trace/src/serverless/vercel.js +122 -0
- package/packages/dd-trace/src/serverless.js +37 -20
- package/packages/dd-trace/src/span_stats.js +40 -3
- package/packages/dd-trace/src/tracer.js +22 -0
- package/packages/dd-trace/src/web-tags-cache.js +67 -13
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
|
@@ -7,12 +7,12 @@ const { getEnvironmentVariable } = require('./config/helper')
|
|
|
7
7
|
const runtimeMetrics = require('./runtime_metrics')
|
|
8
8
|
const log = require('./log')
|
|
9
9
|
const { setStartupLogPluginManager, startupLog } = require('./startup-log')
|
|
10
|
-
const DynamicInstrumentation = require('./debugger')
|
|
11
10
|
const telemetry = require('./telemetry')
|
|
12
11
|
const nomenclature = require('./service-naming')
|
|
13
12
|
const PluginManager = require('./plugin_manager')
|
|
14
13
|
const NoopDogStatsDClient = require('./noop/dogstatsd')
|
|
15
|
-
const { IS_SERVERLESS } = require('./serverless')
|
|
14
|
+
const { IS_SERVERLESS, initializeServerlessTelemetry, supportsServerlessTelemetryRetention } = require('./serverless')
|
|
15
|
+
const { flushServerlessTelemetry, registerTelemetryFlusher } = require('./flush')
|
|
16
16
|
const processTags = require('./process-tags')
|
|
17
17
|
const { isTrue } = require('./util')
|
|
18
18
|
const {
|
|
@@ -41,6 +41,13 @@ const OPENFEATURE_STATE_NOOP = 0
|
|
|
41
41
|
const OPENFEATURE_STATE_LAZY = 1
|
|
42
42
|
const OPENFEATURE_STATE_ACTIVE = 2
|
|
43
43
|
|
|
44
|
+
let dynamicInstrumentation
|
|
45
|
+
|
|
46
|
+
function getDynamicInstrumentation () {
|
|
47
|
+
dynamicInstrumentation ??= require('./debugger')
|
|
48
|
+
return dynamicInstrumentation
|
|
49
|
+
}
|
|
50
|
+
|
|
44
51
|
class LazyModule {
|
|
45
52
|
constructor (provider) {
|
|
46
53
|
this.provider = provider
|
|
@@ -100,6 +107,15 @@ class Tracer extends NoopProxy {
|
|
|
100
107
|
this._pluginManager = new PluginManager(this)
|
|
101
108
|
this.dogstatsd = new NoopDogStatsDClient()
|
|
102
109
|
this._tracingInitialized = false
|
|
110
|
+
// Logs and metrics can need retention even when tracing is disabled.
|
|
111
|
+
if (supportsServerlessTelemetryRetention()) {
|
|
112
|
+
this._serverlessTelemetry = {
|
|
113
|
+
flushAll: (done, options) => {
|
|
114
|
+
if (typeof this._tracer?.flushAll === 'function') this._tracer.flushAll(done, options)
|
|
115
|
+
else flushServerlessTelemetry(done, options)
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
}
|
|
103
119
|
this._flare = new LazyModule(() => require('./flare'))
|
|
104
120
|
this.setBaggageItem = setBaggageItem
|
|
105
121
|
this.getBaggageItem = getBaggageItem
|
|
@@ -205,7 +221,7 @@ class Tracer extends NoopProxy {
|
|
|
205
221
|
}
|
|
206
222
|
|
|
207
223
|
if (config.dynamicInstrumentation.enabled) {
|
|
208
|
-
|
|
224
|
+
getDynamicInstrumentation().start(config, rc)
|
|
209
225
|
}
|
|
210
226
|
|
|
211
227
|
const openfeatureRemoteConfig = require('./openfeature/remote_config')
|
|
@@ -255,13 +271,20 @@ class Tracer extends NoopProxy {
|
|
|
255
271
|
|
|
256
272
|
if (config.runtimeMetrics.enabled) {
|
|
257
273
|
runtimeMetrics.start(config)
|
|
274
|
+
// Agent trace response metrics are recorded asynchronously, so drain
|
|
275
|
+
// runtime metrics after the trace export has completed.
|
|
276
|
+
registerTelemetryFlusher(done => runtimeMetrics.flush(done), { afterTrace: true })
|
|
258
277
|
}
|
|
259
278
|
|
|
260
279
|
this.#updateTracing(config)
|
|
261
280
|
|
|
262
|
-
|
|
281
|
+
if (config.iast.enabled) {
|
|
282
|
+
this._modules.rewriter.enable(config)
|
|
283
|
+
}
|
|
263
284
|
|
|
264
|
-
if (config.
|
|
285
|
+
if (config.isCiVisibility &&
|
|
286
|
+
config.DD_TRACE_ENABLED &&
|
|
287
|
+
config.testOptimization.DD_CIVISIBILITY_MANUAL_API_ENABLED) {
|
|
265
288
|
const TestApiManualPlugin = require('./ci-visibility/test-api-manual/test-api-manual-plugin')
|
|
266
289
|
this._testApiManualPlugin = new TestApiManualPlugin(this)
|
|
267
290
|
// `shouldGetEnvironmentData` is passed as false so that we only lazily calculate it
|
|
@@ -269,7 +292,7 @@ class Tracer extends NoopProxy {
|
|
|
269
292
|
// are lazily configured when the library is imported.
|
|
270
293
|
this._testApiManualPlugin.configure({ ...config, enabled: true }, false)
|
|
271
294
|
}
|
|
272
|
-
if (config.DD_AGENTLESS_LOG_SUBMISSION_ENABLED) {
|
|
295
|
+
if (config.isCiVisibility && config.DD_AGENTLESS_LOG_SUBMISSION_ENABLED) {
|
|
273
296
|
if (config.DD_API_KEY) {
|
|
274
297
|
const LogSubmissionPlugin = require('./ci-visibility/log-submission/log-submission-plugin')
|
|
275
298
|
const automaticLogPlugin = new LogSubmissionPlugin(this)
|
|
@@ -282,7 +305,7 @@ class Tracer extends NoopProxy {
|
|
|
282
305
|
}
|
|
283
306
|
}
|
|
284
307
|
|
|
285
|
-
if (config.testOptimization.DD_TEST_FAILED_TEST_REPLAY_ENABLED) {
|
|
308
|
+
if (config.isCiVisibility && config.testOptimization.DD_TEST_FAILED_TEST_REPLAY_ENABLED) {
|
|
286
309
|
const getDynamicInstrumentationClient = require('./ci-visibility/dynamic-instrumentation')
|
|
287
310
|
// We instantiate the client but do not start the Worker here. The worker is started lazily
|
|
288
311
|
getDynamicInstrumentationClient(config)
|
|
@@ -389,10 +412,12 @@ class Tracer extends NoopProxy {
|
|
|
389
412
|
if (this._tracingInitialized) {
|
|
390
413
|
this._tracer.configure(config)
|
|
391
414
|
this._pluginManager.configure(config)
|
|
392
|
-
|
|
415
|
+
dynamicInstrumentation?.configure(config)
|
|
393
416
|
setStartupLogPluginManager(this._pluginManager)
|
|
394
417
|
startupLog()
|
|
395
418
|
}
|
|
419
|
+
|
|
420
|
+
initializeServerlessTelemetry(this._serverlessTelemetry)
|
|
396
421
|
}
|
|
397
422
|
|
|
398
423
|
/**
|
|
@@ -404,6 +429,9 @@ class Tracer extends NoopProxy {
|
|
|
404
429
|
*/
|
|
405
430
|
#updateDebugger (config, rc) {
|
|
406
431
|
const shouldBeEnabled = config.dynamicInstrumentation.enabled
|
|
432
|
+
if (!shouldBeEnabled && dynamicInstrumentation === undefined) return
|
|
433
|
+
|
|
434
|
+
const DynamicInstrumentation = getDynamicInstrumentation()
|
|
407
435
|
const isCurrentlyStarted = DynamicInstrumentation.isStarted()
|
|
408
436
|
|
|
409
437
|
if (shouldBeEnabled) {
|
|
@@ -13,6 +13,7 @@ const noop = runtimeMetrics = {
|
|
|
13
13
|
gauge () {},
|
|
14
14
|
increment () {},
|
|
15
15
|
decrement () {},
|
|
16
|
+
flush (done) { done?.() },
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
module.exports = {
|
|
@@ -42,6 +43,10 @@ module.exports = {
|
|
|
42
43
|
runtimeMetrics = noop
|
|
43
44
|
Object.setPrototypeOf(module.exports, noop)
|
|
44
45
|
},
|
|
46
|
+
|
|
47
|
+
flush (done) {
|
|
48
|
+
runtimeMetrics.flush(done)
|
|
49
|
+
},
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
Object.setPrototypeOf(module.exports, noop)
|
|
@@ -27,6 +27,7 @@ let client = null
|
|
|
27
27
|
let lastTime = 0
|
|
28
28
|
let lastCpuUsage = null
|
|
29
29
|
let eventLoopDelayObserver = null
|
|
30
|
+
let capture = null
|
|
30
31
|
|
|
31
32
|
// !!!!!!!!!!!
|
|
32
33
|
// IMPORTANT
|
|
@@ -76,11 +77,10 @@ module.exports = {
|
|
|
76
77
|
lastTime = performance.now()
|
|
77
78
|
|
|
78
79
|
if (nativeMetrics) {
|
|
79
|
-
|
|
80
|
+
capture = () => {
|
|
80
81
|
captureNativeMetrics(trackEventLoop, trackGc)
|
|
81
82
|
captureCommonMetrics(trackEventLoop)
|
|
82
|
-
|
|
83
|
-
}, flushIntervalMs)
|
|
83
|
+
}
|
|
84
84
|
} else {
|
|
85
85
|
lastCpuUsage = process.cpuUsage()
|
|
86
86
|
|
|
@@ -92,17 +92,21 @@ module.exports = {
|
|
|
92
92
|
eventLoopDelayObserver.enable()
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
capture = () => {
|
|
96
96
|
captureCpuUsage()
|
|
97
97
|
captureCommonMetrics(trackEventLoop)
|
|
98
98
|
captureHeapSpace()
|
|
99
99
|
if (trackEventLoop) {
|
|
100
100
|
captureEventLoopDelay()
|
|
101
101
|
}
|
|
102
|
-
|
|
103
|
-
}, flushIntervalMs)
|
|
102
|
+
}
|
|
104
103
|
}
|
|
105
104
|
|
|
105
|
+
interval = setInterval(() => {
|
|
106
|
+
capture()
|
|
107
|
+
client.flush()
|
|
108
|
+
}, flushIntervalMs)
|
|
109
|
+
|
|
106
110
|
interval.unref?.()
|
|
107
111
|
},
|
|
108
112
|
|
|
@@ -114,6 +118,7 @@ module.exports = {
|
|
|
114
118
|
interval = null
|
|
115
119
|
|
|
116
120
|
client = null
|
|
121
|
+
capture = null
|
|
117
122
|
lastCpuUsage = null
|
|
118
123
|
|
|
119
124
|
gcObserver?.disconnect()
|
|
@@ -158,6 +163,12 @@ module.exports = {
|
|
|
158
163
|
decrement (name, tag) {
|
|
159
164
|
this.count(name, -1, tag)
|
|
160
165
|
},
|
|
166
|
+
|
|
167
|
+
flush (done) {
|
|
168
|
+
if (!client) return done?.()
|
|
169
|
+
capture?.()
|
|
170
|
+
client.flush(done)
|
|
171
|
+
},
|
|
161
172
|
}
|
|
162
173
|
|
|
163
174
|
function captureCpuUsage () {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tracks transport deliveries that must outlive a serverless request.
|
|
5
|
+
*
|
|
6
|
+
* The tracker is created only for platforms with an invocation-retention
|
|
7
|
+
* boundary. Exporters keep their normal callback path when it is absent.
|
|
8
|
+
*/
|
|
9
|
+
class TelemetryDeliveryTracker {
|
|
10
|
+
#deliveries = new Set()
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Tracks one asynchronous transport delivery until its callback runs.
|
|
14
|
+
* @param {(done: () => void) => void} deliver
|
|
15
|
+
* @param {(() => void)|undefined} done
|
|
16
|
+
*/
|
|
17
|
+
track (deliver, done) {
|
|
18
|
+
const delivery = { callbacks: done ? [done] : [] }
|
|
19
|
+
this.#deliveries.add(delivery)
|
|
20
|
+
|
|
21
|
+
let completed = false
|
|
22
|
+
const complete = () => {
|
|
23
|
+
if (completed) return
|
|
24
|
+
completed = true
|
|
25
|
+
this.#deliveries.delete(delivery)
|
|
26
|
+
for (const callback of delivery.callbacks) callback()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
deliver(complete)
|
|
31
|
+
} catch (error) {
|
|
32
|
+
complete()
|
|
33
|
+
throw error
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Calls back after every delivery active at this boundary has completed.
|
|
39
|
+
* @param {(() => void)|undefined} done
|
|
40
|
+
*/
|
|
41
|
+
waitForIdle (done) {
|
|
42
|
+
if (!done) return
|
|
43
|
+
|
|
44
|
+
const deliveries = [...this.#deliveries]
|
|
45
|
+
if (deliveries.length === 0) return done()
|
|
46
|
+
|
|
47
|
+
let pending = deliveries.length
|
|
48
|
+
const complete = () => {
|
|
49
|
+
if (--pending === 0) done()
|
|
50
|
+
}
|
|
51
|
+
for (const delivery of deliveries) delivery.callbacks.push(complete)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = TelemetryDeliveryTracker
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { channel } = require('dc-polyfill')
|
|
4
|
+
|
|
5
|
+
const { getEnvironmentVariable } = require('../config/helper')
|
|
6
|
+
const log = require('../log')
|
|
7
|
+
|
|
8
|
+
const httpRequestFinishChannel = channel('apm:http:server:request:finish')
|
|
9
|
+
const http2RequestStartChannel = channel('apm:http2:server:request:start')
|
|
10
|
+
const http2ResponseEmitChannel = channel('apm:http2:server:response:emit')
|
|
11
|
+
const VERCEL_REQUEST_CONTEXT = Symbol.for('@vercel/request-context')
|
|
12
|
+
const VERCEL_FLUSH_TIMEOUT = 2000
|
|
13
|
+
const vercelRetentionHandlers = new WeakMap()
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {{ flushAll?: (done: () => void, options?: { timeout?: number }) => void }} TelemetryFlusher
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {TelemetryFlusher} tracer
|
|
21
|
+
* @param {() => void} done
|
|
22
|
+
* @returns {void}
|
|
23
|
+
*/
|
|
24
|
+
function flushVercelTelemetry (tracer, done) {
|
|
25
|
+
setImmediate(() => {
|
|
26
|
+
try {
|
|
27
|
+
tracer.flushAll(done, { timeout: VERCEL_FLUSH_TIMEOUT })
|
|
28
|
+
} catch (error) {
|
|
29
|
+
log.warn('Unable to flush Vercel telemetry:', error)
|
|
30
|
+
done()
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function registerVercelRequestFlush (tracer) {
|
|
36
|
+
const requestContext = getVercelRequestContext()
|
|
37
|
+
if (!requestContext) return
|
|
38
|
+
|
|
39
|
+
const { waitUntil } = requestContext
|
|
40
|
+
if (typeof waitUntil !== 'function') return
|
|
41
|
+
|
|
42
|
+
// Retain the invocation synchronously, then flush after the response completes.
|
|
43
|
+
let done
|
|
44
|
+
const pending = new Promise(resolve => { done = resolve })
|
|
45
|
+
try {
|
|
46
|
+
waitUntil(pending)
|
|
47
|
+
flushVercelTelemetry(tracer, done)
|
|
48
|
+
} catch (error) {
|
|
49
|
+
log.warn('Unable to retain Vercel telemetry:', error)
|
|
50
|
+
done()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getVercelRequestContext () {
|
|
55
|
+
return globalThis[VERCEL_REQUEST_CONTEXT]?.get?.()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// HTTP/2 binds response lifecycle events while handling its request-start channel.
|
|
59
|
+
function activateHttp2Lifecycle () {}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Retains a Vercel Node Function until configured telemetry exporters complete.
|
|
63
|
+
*
|
|
64
|
+
* @param {TelemetryFlusher} tracer
|
|
65
|
+
* @returns {(() => void)|undefined}
|
|
66
|
+
*/
|
|
67
|
+
function registerVercelTelemetryRetention (tracer) {
|
|
68
|
+
const existing = vercelRetentionHandlers.get(tracer)
|
|
69
|
+
if (existing) return existing
|
|
70
|
+
|
|
71
|
+
if (typeof tracer?.flushAll !== 'function') return
|
|
72
|
+
// The HTTP finish channel activates its response wrapper directly. HTTP/2 needs
|
|
73
|
+
// a passive request-start subscriber before it can bind response emit events.
|
|
74
|
+
const flushRequest = () => registerVercelRequestFlush(tracer)
|
|
75
|
+
const flushHttp2Response = ({ eventName }) => {
|
|
76
|
+
if (eventName === 'close') flushRequest()
|
|
77
|
+
}
|
|
78
|
+
httpRequestFinishChannel.subscribe(flushRequest)
|
|
79
|
+
http2RequestStartChannel.subscribe(activateHttp2Lifecycle)
|
|
80
|
+
http2ResponseEmitChannel.subscribe(flushHttp2Response)
|
|
81
|
+
|
|
82
|
+
const unregister = () => {
|
|
83
|
+
httpRequestFinishChannel.unsubscribe(flushRequest)
|
|
84
|
+
http2RequestStartChannel.unsubscribe(activateHttp2Lifecycle)
|
|
85
|
+
http2ResponseEmitChannel.unsubscribe(flushHttp2Response)
|
|
86
|
+
vercelRetentionHandlers.delete(tracer)
|
|
87
|
+
}
|
|
88
|
+
vercelRetentionHandlers.set(tracer, unregister)
|
|
89
|
+
return unregister
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Gets Vercel deployment tags to attach to spans.
|
|
94
|
+
*
|
|
95
|
+
* @returns {string[]|undefined}
|
|
96
|
+
*/
|
|
97
|
+
function getVercelPlatformTags () {
|
|
98
|
+
let tags
|
|
99
|
+
const projectId = getEnvironmentVariable('VERCEL_PROJECT_ID')
|
|
100
|
+
if (projectId) {
|
|
101
|
+
tags = ['vercel.project_id', projectId]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const environment = getEnvironmentVariable('VERCEL_ENV')
|
|
105
|
+
if (environment) {
|
|
106
|
+
tags ??= []
|
|
107
|
+
tags.push('vercel.environment', environment)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const region = getEnvironmentVariable('VERCEL_REGION')
|
|
111
|
+
if (region) {
|
|
112
|
+
tags ??= []
|
|
113
|
+
tags.push('vercel.region', region)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return tags
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = {
|
|
120
|
+
getVercelPlatformTags,
|
|
121
|
+
registerVercelTelemetryRetention,
|
|
122
|
+
}
|
|
@@ -45,44 +45,61 @@ function isInServerlessEnvironment () {
|
|
|
45
45
|
/**
|
|
46
46
|
* Gets tags describing the serverless platform where the tracer is running.
|
|
47
47
|
*
|
|
48
|
+
* @param {{ isVercel: boolean }} [platform] Detected serverless platform.
|
|
48
49
|
* @returns {string[]|undefined}
|
|
49
50
|
*/
|
|
50
|
-
function getServerlessPlatformTags () {
|
|
51
|
-
if (
|
|
52
|
-
return getVercelPlatformTags()
|
|
51
|
+
function getServerlessPlatformTags (platform = getServerlessPlatform()) {
|
|
52
|
+
if (platform.isVercel) {
|
|
53
|
+
return require('./serverless/vercel').getVercelPlatformTags()
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/**
|
|
57
|
-
*
|
|
58
|
+
* Detects the serverless platform once while configuration is built.
|
|
59
|
+
* @returns {{ isVercel: boolean }}
|
|
58
60
|
*/
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (projectId) {
|
|
63
|
-
tags = ['vercel.project_id', projectId]
|
|
64
|
-
}
|
|
61
|
+
function getServerlessPlatform () {
|
|
62
|
+
return { isVercel: getEnvironmentVariable('VERCEL') === '1' }
|
|
63
|
+
}
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Whether the current platform can retain an invocation for telemetry delivery.
|
|
67
|
+
*
|
|
68
|
+
* Add future serverless platforms here as they gain an equivalent retention hook.
|
|
69
|
+
* @returns {boolean}
|
|
70
|
+
*/
|
|
71
|
+
function supportsServerlessTelemetryRetention () {
|
|
72
|
+
return getServerlessPlatform().isVercel
|
|
73
|
+
}
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Creates delivery tracking for platforms with an invocation retention hook.
|
|
77
|
+
*/
|
|
78
|
+
function createServerlessDeliveryTracker () {
|
|
79
|
+
if (supportsServerlessTelemetryRetention()) {
|
|
80
|
+
return new (require('./serverless/telemetry-delivery-tracker'))()
|
|
76
81
|
}
|
|
82
|
+
}
|
|
77
83
|
|
|
78
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Registers the lifecycle adapter selected by the detected serverless platform.
|
|
86
|
+
* @param {{ flushAll?: (done: () => void) => void }} tracer
|
|
87
|
+
*/
|
|
88
|
+
function initializeServerlessTelemetry (tracer) {
|
|
89
|
+
if (supportsServerlessTelemetryRetention()) {
|
|
90
|
+
return require('./serverless/vercel').registerVercelTelemetryRetention(tracer)
|
|
91
|
+
}
|
|
79
92
|
}
|
|
80
93
|
|
|
81
94
|
module.exports = {
|
|
82
95
|
getServerlessPlatformTags,
|
|
96
|
+
getServerlessPlatform,
|
|
97
|
+
supportsServerlessTelemetryRetention,
|
|
98
|
+
createServerlessDeliveryTracker,
|
|
83
99
|
getIsGCPFunction,
|
|
84
100
|
getIsAzureFunction,
|
|
85
101
|
enableGCPPubSubPushSubscription,
|
|
86
102
|
getIsFlexConsumptionAzureFunction,
|
|
103
|
+
initializeServerlessTelemetry,
|
|
87
104
|
IS_SERVERLESS: isInServerlessEnvironment(),
|
|
88
105
|
}
|
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
} = require('../../../ext/tags')
|
|
16
16
|
const { ORIGIN_KEY, TOP_LEVEL_KEY, SVC_SRC_KEY, GRPC_STATUS_NAMES } = require('./constants')
|
|
17
17
|
const id = require('./id')
|
|
18
|
+
const log = require('./log')
|
|
18
19
|
|
|
19
20
|
const GRPC_STATUS_CODE_MAP = Object.fromEntries(GRPC_STATUS_NAMES.map((name, i) => [name, String(i)]))
|
|
20
21
|
const ZERO_ID = id('0')
|
|
@@ -235,6 +236,18 @@ class SpanStatsProcessor {
|
|
|
235
236
|
}
|
|
236
237
|
|
|
237
238
|
onInterval () {
|
|
239
|
+
this.#flush()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Drains pending span statistics and waits for their export.
|
|
244
|
+
* @param {Function} [done]
|
|
245
|
+
*/
|
|
246
|
+
forceFlush (done) {
|
|
247
|
+
this.#flush(done)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
#flush (done) {
|
|
238
251
|
const drained = this.#drainBuckets()
|
|
239
252
|
|
|
240
253
|
if (this.enabled && !this.otlpExporter) {
|
|
@@ -248,10 +261,34 @@ class SpanStatsProcessor {
|
|
|
248
261
|
RuntimeID: this.tags['runtime-id'],
|
|
249
262
|
Sequence: ++this.sequence,
|
|
250
263
|
ProcessTags: processTags.serialized,
|
|
251
|
-
})
|
|
264
|
+
}, done)
|
|
252
265
|
} else if (this.otlpExporter && drained.length > 0) {
|
|
253
|
-
this.otlpExporter.
|
|
254
|
-
|
|
266
|
+
if (typeof this.otlpExporter.flush === 'function' && done) {
|
|
267
|
+
// Snapshot requests already in flight before starting this boundary
|
|
268
|
+
// export, so a later invocation cannot extend this lifecycle barrier.
|
|
269
|
+
let pending = 2
|
|
270
|
+
const complete = () => {
|
|
271
|
+
if (--pending === 0) done()
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
this.otlpExporter.flush(complete)
|
|
275
|
+
} catch (error) {
|
|
276
|
+
log.error('Failed to flush OTLP span stats:', error)
|
|
277
|
+
complete()
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
this.otlpExporter.export(drained, this.bucketSizeNs, complete)
|
|
281
|
+
} catch (error) {
|
|
282
|
+
log.error('Failed to export OTLP span stats:', error)
|
|
283
|
+
complete()
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
this.otlpExporter.export(drained, this.bucketSizeNs, done)
|
|
287
|
+
}
|
|
288
|
+
} else if (this.otlpExporter) {
|
|
289
|
+
if (typeof this.otlpExporter.flush === 'function') this.otlpExporter.flush(done)
|
|
290
|
+
else done?.()
|
|
291
|
+
} else done?.()
|
|
255
292
|
}
|
|
256
293
|
|
|
257
294
|
onSpanFinished (span) {
|
|
@@ -13,6 +13,7 @@ const { isError } = require('./util')
|
|
|
13
13
|
const { setStartupLogConfig } = require('./startup-log')
|
|
14
14
|
const { DataStreamsCheckpointer, DataStreamsManager, DataStreamsProcessor } = require('./datastreams')
|
|
15
15
|
const { IS_SERVERLESS } = require('./serverless')
|
|
16
|
+
const { flushServerlessTelemetry } = require('./flush')
|
|
16
17
|
const log = require('./log')
|
|
17
18
|
// Always-on writer (console.warn), not the channel-gated `log`: these surface regardless of
|
|
18
19
|
// DD_TRACE_DEBUG.
|
|
@@ -150,6 +151,27 @@ class DatadogTracer extends Tracer {
|
|
|
150
151
|
this._dataStreamsProcessor.setUrl(url)
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Flushes every configured telemetry pipeline for a serverless lifecycle.
|
|
156
|
+
* @param {Function} [done] Called after every configured export completes
|
|
157
|
+
* @param {{ timeout?: number }} [options] Bounds this flush operation.
|
|
158
|
+
*/
|
|
159
|
+
flushAll (done, options) {
|
|
160
|
+
const traceExporter = this._exporter
|
|
161
|
+
const spanStats = this._processor?._stats
|
|
162
|
+
const traceFlusher = typeof traceExporter?.flush === 'function'
|
|
163
|
+
? callback => traceExporter.flush(callback)
|
|
164
|
+
: undefined
|
|
165
|
+
const spanStatsFlusher = typeof spanStats?.forceFlush === 'function'
|
|
166
|
+
? callback => spanStats.forceFlush(callback)
|
|
167
|
+
: undefined
|
|
168
|
+
|
|
169
|
+
flushServerlessTelemetry(done, options, {
|
|
170
|
+
trace: traceFlusher,
|
|
171
|
+
spanStats: spanStatsFlusher,
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
|
|
153
175
|
scope () {
|
|
154
176
|
return this._scope
|
|
155
177
|
}
|