dd-trace 5.82.0 → 5.84.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 +77 -79
- package/ci/init.js +6 -6
- package/index.d.ts +213 -4
- package/loader-hook.mjs +1 -1
- package/package.json +59 -56
- package/packages/datadog-core/src/storage.js +7 -7
- package/packages/datadog-esbuild/index.js +6 -0
- package/packages/datadog-instrumentations/src/ai.js +7 -3
- package/packages/datadog-instrumentations/src/child_process.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +1 -1
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/instrumentations.js +4 -3
- package/packages/datadog-instrumentations/src/helpers/register.js +3 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +6 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +73 -16
- package/packages/datadog-instrumentations/src/http/client.js +2 -2
- package/packages/datadog-instrumentations/src/jest.js +124 -64
- package/packages/datadog-instrumentations/src/koa.js +2 -1
- package/packages/datadog-instrumentations/src/light-my-request.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/worker.js +1 -1
- package/packages/datadog-instrumentations/src/mocha.js +1 -1
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/mysql2.js +2 -2
- package/packages/datadog-instrumentations/src/net.js +13 -5
- package/packages/datadog-instrumentations/src/nyc.js +1 -1
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +4 -4
- package/packages/datadog-instrumentations/src/pg.js +4 -2
- package/packages/datadog-instrumentations/src/playwright.js +15 -11
- package/packages/datadog-instrumentations/src/selenium.js +2 -2
- package/packages/datadog-instrumentations/src/undici.js +12 -1
- package/packages/datadog-plugin-aws-sdk/src/base.js +4 -4
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +2 -2
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +2 -2
- package/packages/datadog-plugin-cucumber/src/index.js +35 -34
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
- package/packages/datadog-plugin-dd-trace-api/src/index.js +2 -2
- package/packages/datadog-plugin-express/src/code_origin.js +21 -15
- package/packages/datadog-plugin-fastify/src/code_origin.js +17 -4
- package/packages/datadog-plugin-jest/src/index.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +2 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +26 -26
- package/packages/datadog-plugin-undici/src/index.js +305 -2
- package/packages/datadog-plugin-vitest/src/index.js +5 -5
- package/packages/datadog-shimmer/src/shimmer.js +2 -5
- package/packages/dd-trace/index.js +19 -0
- package/packages/dd-trace/src/agent/info.js +57 -0
- package/packages/dd-trace/src/agent/url.js +28 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +47 -7
- package/packages/dd-trace/src/appsec/rasp/index.js +2 -4
- package/packages/dd-trace/src/azure_metadata.js +8 -3
- package/packages/dd-trace/src/baggage.js +36 -11
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +5 -1
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +3 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +5 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -11
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +3 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +4 -4
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -2
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +4 -4
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -4
- package/packages/dd-trace/src/ci-visibility/telemetry.js +6 -2
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +2 -2
- package/packages/dd-trace/src/{config_defaults.js → config/defaults.js} +3 -3
- package/packages/dd-trace/src/{config-helper.js → config/helper.js} +88 -15
- package/packages/dd-trace/src/{config.js → config/index.js} +107 -46
- package/packages/dd-trace/src/config/remote_config.js +188 -19
- package/packages/dd-trace/src/{config_stable.js → config/stable.js} +20 -32
- package/packages/dd-trace/src/{supported-configurations.json → config/supported-configurations.json} +3 -1
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -5
- package/packages/dd-trace/src/datastreams/processor.js +1 -1
- package/packages/dd-trace/src/datastreams/writer.js +2 -8
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -7
- package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +10 -11
- package/packages/dd-trace/src/debugger/devtools_client/send.js +3 -3
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/constants.js +1 -1
- package/packages/dd-trace/src/debugger/index.js +83 -15
- package/packages/dd-trace/src/dogstatsd.js +5 -11
- package/packages/dd-trace/src/encode/0.4.js +2 -2
- package/packages/dd-trace/src/exporter.js +1 -1
- package/packages/dd-trace/src/exporters/agent/index.js +5 -11
- package/packages/dd-trace/src/exporters/agent/writer.js +12 -16
- package/packages/dd-trace/src/exporters/common/{agent-info-exporter.js → buffering-exporter.js} +10 -37
- package/packages/dd-trace/src/exporters/common/docker.js +2 -2
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- package/packages/dd-trace/src/exporters/common/util.js +2 -2
- package/packages/dd-trace/src/exporters/span-stats/index.js +3 -10
- package/packages/dd-trace/src/flare/index.js +1 -1
- package/packages/dd-trace/src/guardrails/telemetry.js +1 -1
- package/packages/dd-trace/src/index.js +4 -4
- package/packages/dd-trace/src/lambda/handler.js +2 -2
- package/packages/dd-trace/src/lambda/index.js +2 -2
- package/packages/dd-trace/src/lambda/runtime/patch.js +2 -2
- package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +8 -1
- package/packages/dd-trace/src/llmobs/index.js +2 -2
- package/packages/dd-trace/src/llmobs/noop.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +3 -4
- package/packages/dd-trace/src/llmobs/sdk.js +33 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +17 -7
- package/packages/dd-trace/src/llmobs/tagger.js +175 -1
- package/packages/dd-trace/src/llmobs/writers/base.js +118 -45
- package/packages/dd-trace/src/llmobs/writers/spans.js +4 -3
- package/packages/dd-trace/src/llmobs/writers/util.js +3 -9
- package/packages/dd-trace/src/log/index.js +50 -35
- package/packages/dd-trace/src/log/writer.js +13 -78
- package/packages/dd-trace/src/noop/proxy.js +3 -3
- package/packages/dd-trace/src/openfeature/writers/base.js +9 -16
- package/packages/dd-trace/src/openfeature/writers/util.js +3 -8
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +2 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +48 -6
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +45 -21
- package/packages/dd-trace/src/opentracing/span.js +4 -4
- package/packages/dd-trace/src/plugin_manager.js +8 -6
- package/packages/dd-trace/src/plugins/util/ci.js +5 -8
- package/packages/dd-trace/src/plugins/util/git-cache.js +3 -3
- package/packages/dd-trace/src/plugins/util/test.js +1 -1
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +41 -43
- package/packages/dd-trace/src/profiler.js +4 -39
- package/packages/dd-trace/src/profiling/config.js +74 -34
- package/packages/dd-trace/src/profiling/exporter_cli.js +5 -5
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +9 -2
- package/packages/dd-trace/src/profiling/index.js +1 -1
- package/packages/dd-trace/src/profiling/libuv-size.js +1 -1
- package/packages/dd-trace/src/profiling/profiler.js +57 -2
- package/packages/dd-trace/src/proxy.js +34 -5
- package/packages/dd-trace/src/remote_config/capabilities.js +4 -0
- package/packages/dd-trace/src/remote_config/index.js +2 -7
- package/packages/dd-trace/src/ritm.js +8 -4
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -2
- package/packages/dd-trace/src/serverless.js +2 -2
- package/packages/dd-trace/src/span_processor.js +2 -2
- package/packages/dd-trace/src/startup-log.js +7 -16
- package/packages/dd-trace/src/telemetry/endpoints.js +67 -5
- package/packages/dd-trace/src/telemetry/send-data.js +103 -4
- package/packages/dd-trace/src/telemetry/telemetry.js +229 -110
- package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
- package/vendor/dist/esquery/index.js +1 -1
- package/vendor/dist/meriyah/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- /package/packages/dd-trace/src/{git_properties.js → config/git_properties.js} +0 -0
|
@@ -1,10 +1,313 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const HttpClientPlugin = require('../../datadog-plugin-http/src/client')
|
|
4
|
+
const { storage } = require('../../datadog-core')
|
|
5
|
+
const tags = require('../../../ext/tags')
|
|
6
|
+
const formats = require('../../../ext/formats')
|
|
7
|
+
const HTTP_HEADERS = formats.HTTP_HEADERS
|
|
8
|
+
const log = require('../../dd-trace/src/log')
|
|
9
|
+
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
const {
|
|
12
|
+
HTTP_STATUS_CODE,
|
|
13
|
+
HTTP_REQUEST_HEADERS,
|
|
14
|
+
HTTP_RESPONSE_HEADERS,
|
|
15
|
+
} = tags
|
|
16
|
+
|
|
17
|
+
// WeakMap to store span context for native undici request objects
|
|
18
|
+
const requestContexts = new WeakMap()
|
|
19
|
+
|
|
20
|
+
class UndiciPlugin extends HttpClientPlugin {
|
|
6
21
|
static id = 'undici'
|
|
7
22
|
static prefix = 'tracing:apm:undici:fetch'
|
|
23
|
+
|
|
24
|
+
constructor (...args) {
|
|
25
|
+
super(...args)
|
|
26
|
+
|
|
27
|
+
// Subscribe to native undici diagnostic channels for undici >= 4.7.0
|
|
28
|
+
// These channels fire for ALL undici requests (fetch, request, stream, etc.)
|
|
29
|
+
this.addSub('undici:request:create', this.#onNativeRequestCreate.bind(this))
|
|
30
|
+
this.addSub('undici:request:headers', this.#onNativeRequestHeaders.bind(this))
|
|
31
|
+
this.addSub('undici:request:trailers', this.#onNativeRequestTrailers.bind(this))
|
|
32
|
+
this.addSub('undici:request:error', this.#onNativeRequestError.bind(this))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ===========================================
|
|
36
|
+
// Native undici diagnostic channel handlers
|
|
37
|
+
// These fire for undici >= 4.7.0 for ALL request types (fetch, request, stream, etc.)
|
|
38
|
+
// ===========================================
|
|
39
|
+
|
|
40
|
+
#onNativeRequestCreate ({ request }) {
|
|
41
|
+
if (!request) return
|
|
42
|
+
|
|
43
|
+
const store = storage('legacy').getStore()
|
|
44
|
+
const { origin = '', path = '/' } = request
|
|
45
|
+
const method = request.method?.toUpperCase() ?? 'GET'
|
|
46
|
+
|
|
47
|
+
// Parse origin to extract protocol, hostname, port
|
|
48
|
+
let protocol = 'http:'
|
|
49
|
+
let hostname = 'localhost'
|
|
50
|
+
let port = ''
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const url = new URL(origin)
|
|
54
|
+
protocol = url.protocol
|
|
55
|
+
hostname = url.hostname
|
|
56
|
+
port = url.port
|
|
57
|
+
} catch {
|
|
58
|
+
// If origin is not a valid URL, use defaults
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const host = port ? `${hostname}:${port}` : hostname
|
|
62
|
+
const pathname = path.split(/[?#]/)[0]
|
|
63
|
+
const uri = `${protocol}//${host}${pathname}`
|
|
64
|
+
|
|
65
|
+
const allowed = this.config.filter(uri)
|
|
66
|
+
const childOf = store && allowed ? store.span : null
|
|
67
|
+
|
|
68
|
+
const span = this.startSpan(this.operationName(), {
|
|
69
|
+
childOf,
|
|
70
|
+
meta: {
|
|
71
|
+
'span.kind': 'client',
|
|
72
|
+
'http.method': method,
|
|
73
|
+
'http.url': uri,
|
|
74
|
+
'out.host': hostname
|
|
75
|
+
},
|
|
76
|
+
metrics: {
|
|
77
|
+
[CLIENT_PORT_KEY]: port ? Number.parseInt(port, 10) : undefined
|
|
78
|
+
},
|
|
79
|
+
service: this.serviceName({ pluginConfig: this.config, sessionDetails: { host: hostname, port } }),
|
|
80
|
+
resource: method,
|
|
81
|
+
type: 'http'
|
|
82
|
+
}, false)
|
|
83
|
+
|
|
84
|
+
// Disable recording if not allowed
|
|
85
|
+
if (!allowed) {
|
|
86
|
+
span._spanContext._trace.record = false
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Capture request headers if configured
|
|
90
|
+
if (request.headers && this.config.headers) {
|
|
91
|
+
addConfiguredHeaders(span, request.headers, this.config.headers, HTTP_REQUEST_HEADERS)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Inject trace headers if propagation is allowed
|
|
95
|
+
if (this.config.propagationFilter(uri)) {
|
|
96
|
+
const headers = {}
|
|
97
|
+
this.tracer.inject(span, HTTP_HEADERS, headers)
|
|
98
|
+
|
|
99
|
+
// Use addHeader if available (undici provides this on the request object)
|
|
100
|
+
if (typeof request.addHeader === 'function') {
|
|
101
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
102
|
+
request.addHeader(name, value)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Store span context for request for later retrieval
|
|
108
|
+
requestContexts.set(request, {
|
|
109
|
+
span,
|
|
110
|
+
store,
|
|
111
|
+
uri
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// Enter the span context
|
|
115
|
+
storage('legacy').enterWith({ ...store, span })
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#onNativeRequestHeaders ({ request, response }) {
|
|
119
|
+
const ctx = requestContexts.get(request)
|
|
120
|
+
if (!ctx) return
|
|
121
|
+
|
|
122
|
+
const { span } = ctx
|
|
123
|
+
const statusCode = response?.statusCode
|
|
124
|
+
|
|
125
|
+
if (statusCode) {
|
|
126
|
+
span.setTag(HTTP_STATUS_CODE, statusCode)
|
|
127
|
+
|
|
128
|
+
if (!this.config.validateStatus(statusCode)) {
|
|
129
|
+
span.setTag('error', 1)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Add response headers if configured
|
|
134
|
+
if (response?.headers && this.config.headers) {
|
|
135
|
+
addConfiguredHeaders(span, response.headers, this.config.headers, HTTP_RESPONSE_HEADERS)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#onNativeRequestTrailers ({ request }) {
|
|
140
|
+
const ctx = requestContexts.get(request)
|
|
141
|
+
if (!ctx) return
|
|
142
|
+
|
|
143
|
+
const { span, store } = ctx
|
|
144
|
+
|
|
145
|
+
// Call the request hook if configured
|
|
146
|
+
this.config.hooks.request(span, null, null)
|
|
147
|
+
|
|
148
|
+
// Finish the span
|
|
149
|
+
span.finish()
|
|
150
|
+
|
|
151
|
+
// Clean up
|
|
152
|
+
requestContexts.delete(request)
|
|
153
|
+
|
|
154
|
+
// Restore parent store
|
|
155
|
+
if (store) {
|
|
156
|
+
storage('legacy').enterWith(store)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
#onNativeRequestError ({ request, error }) {
|
|
161
|
+
const ctx = requestContexts.get(request)
|
|
162
|
+
if (!ctx) return
|
|
163
|
+
|
|
164
|
+
const { span, store } = ctx
|
|
165
|
+
|
|
166
|
+
// Don't record AbortError as an error - it's user-initiated cancellation
|
|
167
|
+
if (error && error.name !== 'AbortError') {
|
|
168
|
+
span.setTag('error', error)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Call the request hook if configured
|
|
172
|
+
this.config.hooks.request(span, null, null)
|
|
173
|
+
|
|
174
|
+
// Finish the span
|
|
175
|
+
span.finish()
|
|
176
|
+
|
|
177
|
+
// Clean up
|
|
178
|
+
requestContexts.delete(request)
|
|
179
|
+
|
|
180
|
+
// Restore parent store
|
|
181
|
+
if (store) {
|
|
182
|
+
storage('legacy').enterWith(store)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ===========================================
|
|
187
|
+
// Fetch-based tracing channel handlers
|
|
188
|
+
// These handle fetch() for undici < 4.7.0 (before native DC was added)
|
|
189
|
+
// ===========================================
|
|
190
|
+
|
|
191
|
+
bindStart (ctx) {
|
|
192
|
+
const req = ctx.req
|
|
193
|
+
const options = new URL(req.url)
|
|
194
|
+
options.headers = Object.fromEntries(req.headers.entries())
|
|
195
|
+
options.method = req.method
|
|
196
|
+
|
|
197
|
+
ctx.args = { options }
|
|
198
|
+
|
|
199
|
+
const store = super.bindStart(ctx)
|
|
200
|
+
|
|
201
|
+
// Inject trace headers back into the request
|
|
202
|
+
for (const name of Object.keys(options.headers)) {
|
|
203
|
+
if (!req.headers.has(name)) {
|
|
204
|
+
req.headers.set(name, options.headers[name])
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return store
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
error (ctx) {
|
|
212
|
+
// Don't record AbortError as an error - it's user-initiated cancellation
|
|
213
|
+
if (!ctx.error || ctx.error.name !== 'AbortError') {
|
|
214
|
+
return super.error(ctx)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
asyncEnd (ctx) {
|
|
219
|
+
ctx.res = ctx.result
|
|
220
|
+
return this.finish(ctx)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
configure (config) {
|
|
224
|
+
return super.configure(normalizeConfig(config))
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Add configured headers to span with appropriate tags
|
|
229
|
+
function addConfiguredHeaders (span, rawHeaders, configuredHeaders, headerType) {
|
|
230
|
+
const headers = normalizeHeaders(rawHeaders)
|
|
231
|
+
|
|
232
|
+
for (const [key, tag] of configuredHeaders) {
|
|
233
|
+
const value = headers[key]
|
|
234
|
+
if (value) {
|
|
235
|
+
span.setTag(tag || `${headerType}.${key}`, value)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
8
238
|
}
|
|
9
239
|
|
|
240
|
+
// Normalize headers to an object, handling different undici formats:
|
|
241
|
+
// - Array format: alternating key-value pairs (undici >= 6.0.0)
|
|
242
|
+
// - String format: HTTP header lines like "key: value\r\n" (undici 5.x)
|
|
243
|
+
// - Object format: already a headers object
|
|
244
|
+
function normalizeHeaders (headers) {
|
|
245
|
+
if (!headers) return {}
|
|
246
|
+
|
|
247
|
+
// String format (undici 5.x): "key: value\r\nkey2: value2\r\n"
|
|
248
|
+
if (typeof headers === 'string') {
|
|
249
|
+
const result = {}
|
|
250
|
+
const lines = headers.split('\r\n')
|
|
251
|
+
for (const line of lines) {
|
|
252
|
+
if (!line) continue
|
|
253
|
+
const colonIndex = line.indexOf(':')
|
|
254
|
+
if (colonIndex > 0) {
|
|
255
|
+
const key = line.slice(0, colonIndex).toLowerCase().trim()
|
|
256
|
+
const value = line.slice(colonIndex + 1).trim()
|
|
257
|
+
result[key] = value
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return result
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Array format (undici >= 6.0.0): alternating key-value pairs
|
|
264
|
+
if (Array.isArray(headers)) {
|
|
265
|
+
const result = {}
|
|
266
|
+
for (let i = 0; i < headers.length; i += 2) {
|
|
267
|
+
const key = headers[i]
|
|
268
|
+
if (typeof key === 'string') {
|
|
269
|
+
result[key.toLowerCase()] = headers[i + 1]
|
|
270
|
+
} else if (Buffer.isBuffer(key)) {
|
|
271
|
+
result[key.toString().toLowerCase()] = headers[i + 1]?.toString?.() || headers[i + 1]
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return result
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Object format: use as-is
|
|
278
|
+
return headers
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function normalizeConfig (config) {
|
|
282
|
+
const validateStatus = getStatusValidator(config)
|
|
283
|
+
const hooks = getHooks(config)
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
...config,
|
|
287
|
+
validateStatus,
|
|
288
|
+
hooks
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function getStatusValidator (config) {
|
|
293
|
+
if (typeof config.validateStatus === 'function') {
|
|
294
|
+
return config.validateStatus
|
|
295
|
+
} else if (Object.hasOwn(config, 'validateStatus')) {
|
|
296
|
+
log.error('Expected `validateStatus` to be a function.')
|
|
297
|
+
}
|
|
298
|
+
return defaultValidateStatus
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function defaultValidateStatus (code) {
|
|
302
|
+
return code < 400 || code >= 500
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function getHooks (config) {
|
|
306
|
+
const request = config.hooks?.request ?? noop
|
|
307
|
+
|
|
308
|
+
return { request }
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function noop () {}
|
|
312
|
+
|
|
10
313
|
module.exports = UndiciPlugin
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
|
-
const {
|
|
5
|
+
const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
|
|
6
6
|
|
|
7
7
|
const {
|
|
8
8
|
TEST_STATUS,
|
|
@@ -281,11 +281,11 @@ class VitestPlugin extends CiPlugin {
|
|
|
281
281
|
this.addBind('ci:vitest:test-suite:start', (ctx) => {
|
|
282
282
|
const { testSuiteAbsolutePath, frameworkVersion } = ctx
|
|
283
283
|
|
|
284
|
-
this.command =
|
|
284
|
+
this.command = getValueFromEnvSources('DD_CIVISIBILITY_TEST_COMMAND')
|
|
285
285
|
this.frameworkVersion = frameworkVersion
|
|
286
286
|
const testSessionSpanContext = this.tracer.extract('text_map', {
|
|
287
|
-
'x-datadog-trace-id':
|
|
288
|
-
'x-datadog-parent-id':
|
|
287
|
+
'x-datadog-trace-id': getValueFromEnvSources('DD_CIVISIBILITY_TEST_SESSION_ID'),
|
|
288
|
+
'x-datadog-parent-id': getValueFromEnvSources('DD_CIVISIBILITY_TEST_MODULE_ID')
|
|
289
289
|
})
|
|
290
290
|
|
|
291
291
|
const trimmedCommand = DD_MAJOR < 6 ? this.command : 'vitest run'
|
|
@@ -405,7 +405,7 @@ class VitestPlugin extends CiPlugin {
|
|
|
405
405
|
finishAllTraceSpans(this.testSessionSpan)
|
|
406
406
|
this.telemetry.count(TELEMETRY_TEST_SESSION, {
|
|
407
407
|
provider: this.ciProviderName,
|
|
408
|
-
autoInjected: !!
|
|
408
|
+
autoInjected: !!getValueFromEnvSources('DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER')
|
|
409
409
|
})
|
|
410
410
|
this.tracer._exporter.flush(onFinish)
|
|
411
411
|
})
|
|
@@ -76,11 +76,8 @@ function wrapFunction (original, wrapper) {
|
|
|
76
76
|
if (typeof original !== 'function') return original
|
|
77
77
|
|
|
78
78
|
const wrapped = wrapper(original)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
assertNotClass(original)
|
|
82
|
-
copyProperties(original, wrapped)
|
|
83
|
-
}
|
|
79
|
+
assertNotClass(original)
|
|
80
|
+
copyProperties(original, wrapped)
|
|
84
81
|
|
|
85
82
|
return wrapped
|
|
86
83
|
}
|
|
@@ -10,6 +10,25 @@ if (!global._ddtrace) {
|
|
|
10
10
|
writable: true
|
|
11
11
|
})
|
|
12
12
|
|
|
13
|
+
const ddTraceSymbol = Symbol.for('dd-trace')
|
|
14
|
+
|
|
15
|
+
Object.defineProperty(globalThis, ddTraceSymbol, {
|
|
16
|
+
value: {
|
|
17
|
+
beforeExitHandlers: new Set(),
|
|
18
|
+
},
|
|
19
|
+
enumerable: false,
|
|
20
|
+
configurable: true, // Allow this to be overridden by loading the tracer
|
|
21
|
+
writable: false
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
process.once('beforeExit', function mainBeforeExit () {
|
|
25
|
+
if (globalThis[ddTraceSymbol]?.beforeExitHandlers) {
|
|
26
|
+
for (const handler of globalThis[ddTraceSymbol].beforeExitHandlers) {
|
|
27
|
+
handler()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
13
32
|
global._ddtrace.default = global._ddtrace
|
|
14
33
|
global._ddtrace.tracer = global._ddtrace
|
|
15
34
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const request = require('../exporters/common/request')
|
|
4
|
+
|
|
5
|
+
const CACHE_TTL_MS = 60_000 // 1 minute
|
|
6
|
+
|
|
7
|
+
let cachedUrl = null
|
|
8
|
+
let cachedData = null
|
|
9
|
+
let cachedTimestamp = 0
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
fetchAgentInfo,
|
|
13
|
+
clearCache // For testing purposes only
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Fetches agent information from the /info endpoint
|
|
18
|
+
* @param {URL} url - The agent URL
|
|
19
|
+
* @param {Function} callback - Callback function with signature (err, agentInfo)
|
|
20
|
+
*/
|
|
21
|
+
function fetchAgentInfo (url, callback) {
|
|
22
|
+
const urlKey = url.href
|
|
23
|
+
|
|
24
|
+
if (cachedUrl !== null && cachedUrl !== urlKey) {
|
|
25
|
+
// Clear cache if URL changes
|
|
26
|
+
clearCache()
|
|
27
|
+
} else if (cachedData !== null && (Date.now() - cachedTimestamp) < CACHE_TTL_MS) {
|
|
28
|
+
// Return cached result if still valid
|
|
29
|
+
return process.nextTick(callback, null, cachedData)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
request('', {
|
|
33
|
+
path: '/info',
|
|
34
|
+
url
|
|
35
|
+
}, (err, res) => {
|
|
36
|
+
if (err) {
|
|
37
|
+
return callback(err)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
cachedData = JSON.parse(res)
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return callback(e)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
cachedUrl = urlKey
|
|
47
|
+
cachedTimestamp = Date.now()
|
|
48
|
+
|
|
49
|
+
callback(null, cachedData)
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function clearCache () {
|
|
54
|
+
cachedUrl = null
|
|
55
|
+
cachedData = null
|
|
56
|
+
cachedTimestamp = 0
|
|
57
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { URL, format } = require('url')
|
|
4
|
+
const defaults = require('../config/defaults')
|
|
5
|
+
|
|
6
|
+
module.exports = { getAgentUrl }
|
|
7
|
+
|
|
8
|
+
// TODO: Investigate merging with the getAgentUrl function in config/index.js which has
|
|
9
|
+
// additional logic for unix socket auto-detection on Linux. The config version is only used
|
|
10
|
+
// during config initialization, while this one is used throughout the codebase. Consider if
|
|
11
|
+
// the unix socket detection should be part of this general helper or remain config-specific.
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Gets the agent URL from config, constructing it from hostname/port if needed
|
|
15
|
+
* @param {ReturnType<import('../config')>} config - Tracer configuration object
|
|
16
|
+
* @returns {URL} The agent URL
|
|
17
|
+
*/
|
|
18
|
+
function getAgentUrl (config) {
|
|
19
|
+
const { url, hostname, port } = config
|
|
20
|
+
if (url) {
|
|
21
|
+
return url instanceof URL ? url : new URL(url)
|
|
22
|
+
}
|
|
23
|
+
return new URL(format({
|
|
24
|
+
protocol: 'http:',
|
|
25
|
+
hostname: hostname || defaults.hostname,
|
|
26
|
+
port: port || defaults.port
|
|
27
|
+
}))
|
|
28
|
+
}
|
|
@@ -12,7 +12,7 @@ const { getName } = require('../telemetry/verbosity')
|
|
|
12
12
|
const telemetry = require('../telemetry')
|
|
13
13
|
const log = require('../../../log')
|
|
14
14
|
const orchestrionConfig = require('../../../../../datadog-instrumentations/src/orchestrion-config')
|
|
15
|
-
const { getEnvironmentVariable } = require('../../../config
|
|
15
|
+
const { getEnvironmentVariable } = require('../../../config/helper')
|
|
16
16
|
const { LOG_MESSAGE, REWRITTEN_MESSAGE } = require('./constants')
|
|
17
17
|
const { incrementTelemetryIfNeeded } = require('./rewriter-telemetry')
|
|
18
18
|
const { csiMethods } = require('./csi-methods')
|
|
@@ -104,6 +104,8 @@ function enable (_config) {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
const analyzedBodies = new WeakSet()
|
|
108
|
+
|
|
107
109
|
function onRequestBodyParsed ({ req, res, body, abortController }) {
|
|
108
110
|
if (body === undefined || body === null) return
|
|
109
111
|
|
|
@@ -120,6 +122,12 @@ function onRequestBodyParsed ({ req, res, body, abortController }) {
|
|
|
120
122
|
storedBodies.set(req, body)
|
|
121
123
|
}
|
|
122
124
|
|
|
125
|
+
// eslint-disable-next-line eslint-rules/eslint-safe-typeof-object
|
|
126
|
+
if (typeof body === 'object') {
|
|
127
|
+
if (isEmptyObject(body)) return
|
|
128
|
+
analyzedBodies.add(body)
|
|
129
|
+
}
|
|
130
|
+
|
|
123
131
|
const results = waf.run({
|
|
124
132
|
persistent: {
|
|
125
133
|
[addresses.HTTP_INCOMING_BODY]: body
|
|
@@ -129,12 +137,17 @@ function onRequestBodyParsed ({ req, res, body, abortController }) {
|
|
|
129
137
|
handleResults(results?.actions, req, res, rootSpan, abortController)
|
|
130
138
|
}
|
|
131
139
|
|
|
140
|
+
const analyzedCookies = new WeakSet()
|
|
141
|
+
|
|
132
142
|
function onRequestCookieParser ({ req, res, abortController, cookies }) {
|
|
133
143
|
if (!cookies || typeof cookies !== 'object') return
|
|
134
144
|
|
|
135
145
|
const rootSpan = web.root(req)
|
|
136
146
|
if (!rootSpan) return
|
|
137
147
|
|
|
148
|
+
if (isEmptyObject(cookies)) return
|
|
149
|
+
analyzedCookies.add(cookies)
|
|
150
|
+
|
|
138
151
|
const results = waf.run({
|
|
139
152
|
persistent: {
|
|
140
153
|
[addresses.HTTP_INCOMING_COOKIES]: cookies
|
|
@@ -178,19 +191,34 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
178
191
|
const persistent = {}
|
|
179
192
|
|
|
180
193
|
// we need to keep this to support other body parsers
|
|
181
|
-
// TODO: no need to analyze it if it was already done by the body-parser hook
|
|
182
194
|
if (req.body !== undefined && req.body !== null) {
|
|
183
|
-
|
|
195
|
+
// eslint-disable-next-line eslint-rules/eslint-safe-typeof-object
|
|
196
|
+
if (typeof req.body === 'object') {
|
|
197
|
+
if (!isEmptyObject(req.body) && !analyzedBodies.has(req.body)) {
|
|
198
|
+
persistent[addresses.HTTP_INCOMING_BODY] = req.body
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
persistent[addresses.HTTP_INCOMING_BODY] = req.body
|
|
202
|
+
}
|
|
184
203
|
}
|
|
185
204
|
|
|
186
205
|
// we need to keep this to support other cookie parsers
|
|
187
|
-
if (
|
|
206
|
+
if (
|
|
207
|
+
req.cookies !== null &&
|
|
208
|
+
typeof req.cookies === 'object' &&
|
|
209
|
+
!isEmptyObject(req.cookies) &&
|
|
210
|
+
!analyzedCookies.has(req.cookies)
|
|
211
|
+
) {
|
|
188
212
|
persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies
|
|
189
213
|
}
|
|
190
214
|
|
|
191
215
|
// we need to keep this to support nextjs
|
|
192
216
|
const query = req.query
|
|
193
|
-
if (
|
|
217
|
+
if (
|
|
218
|
+
query !== null &&
|
|
219
|
+
typeof query === 'object' &&
|
|
220
|
+
!isEmptyObject(query)
|
|
221
|
+
) {
|
|
194
222
|
persistent[addresses.HTTP_INCOMING_QUERY] = query
|
|
195
223
|
}
|
|
196
224
|
|
|
@@ -198,7 +226,7 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
198
226
|
persistent[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
|
|
199
227
|
}
|
|
200
228
|
|
|
201
|
-
if (
|
|
229
|
+
if (!isEmptyObject(persistent)) {
|
|
202
230
|
waf.run({ persistent }, req)
|
|
203
231
|
}
|
|
204
232
|
|
|
@@ -273,6 +301,8 @@ function onRequestQueryParsed ({ req, res, query, abortController }) {
|
|
|
273
301
|
const rootSpan = web.root(req)
|
|
274
302
|
if (!rootSpan) return
|
|
275
303
|
|
|
304
|
+
if (isEmptyObject(query)) return
|
|
305
|
+
|
|
276
306
|
const results = waf.run({
|
|
277
307
|
persistent: {
|
|
278
308
|
[addresses.HTTP_INCOMING_QUERY]: query
|
|
@@ -286,7 +316,7 @@ function onRequestProcessParams ({ req, res, abortController, params }) {
|
|
|
286
316
|
const rootSpan = web.root(req)
|
|
287
317
|
if (!rootSpan) return
|
|
288
318
|
|
|
289
|
-
if (!params || typeof params !== 'object' ||
|
|
319
|
+
if (!params || typeof params !== 'object' || isEmptyObject(params)) return
|
|
290
320
|
|
|
291
321
|
const results = waf.run({
|
|
292
322
|
persistent: {
|
|
@@ -310,7 +340,7 @@ function onResponseBody ({ req, res, body }) {
|
|
|
310
340
|
}
|
|
311
341
|
|
|
312
342
|
function onResponseWriteHead ({ req, res, abortController, statusCode, responseHeaders }) {
|
|
313
|
-
if (
|
|
343
|
+
if (!isEmptyObject(responseHeaders)) {
|
|
314
344
|
storedResponseHeaders.set(req, responseHeaders)
|
|
315
345
|
}
|
|
316
346
|
|
|
@@ -401,6 +431,16 @@ function disable () {
|
|
|
401
431
|
if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseSetHeader)
|
|
402
432
|
}
|
|
403
433
|
|
|
434
|
+
// this is faster than Object.keys().length === 0
|
|
435
|
+
function isEmptyObject (obj) {
|
|
436
|
+
// eslint-disable-next-line no-unreachable-loop
|
|
437
|
+
for (const _ in obj) {
|
|
438
|
+
return false
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return true
|
|
442
|
+
}
|
|
443
|
+
|
|
404
444
|
module.exports = {
|
|
405
445
|
enable,
|
|
406
446
|
disable,
|
|
@@ -73,16 +73,14 @@ function handleUncaughtExceptionMonitor (error) {
|
|
|
73
73
|
}
|
|
74
74
|
} else {
|
|
75
75
|
const cleanUp = removeAllListeners(process, 'uncaughtException')
|
|
76
|
-
const handler = () => {
|
|
77
|
-
process.removeListener('uncaughtException', handler)
|
|
78
|
-
}
|
|
76
|
+
const handler = () => {}
|
|
79
77
|
|
|
80
78
|
setTimeout(() => {
|
|
81
79
|
process.removeListener('uncaughtException', handler)
|
|
82
80
|
cleanUp()
|
|
83
81
|
})
|
|
84
82
|
|
|
85
|
-
process.
|
|
83
|
+
process.once('uncaughtException', handler)
|
|
86
84
|
}
|
|
87
85
|
}
|
|
88
86
|
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
// Modeled after https://github.com/DataDog/libdatadog/blob/f3994857a59bb5679a65967138c5a3aec418a65f/ddcommon/src/azure_app_services.rs
|
|
4
4
|
|
|
5
5
|
const os = require('os')
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
getEnvironmentVariable,
|
|
8
|
+
getEnvironmentVariables,
|
|
9
|
+
getValueFromEnvSources
|
|
10
|
+
} = require('./config/helper')
|
|
7
11
|
const { getIsAzureFunction, getIsFlexConsumptionAzureFunction } = require('./serverless')
|
|
8
12
|
|
|
9
13
|
function extractSubscriptionID (ownerName) {
|
|
@@ -37,8 +41,6 @@ function trimObject (obj) {
|
|
|
37
41
|
function buildMetadata () {
|
|
38
42
|
const {
|
|
39
43
|
COMPUTERNAME,
|
|
40
|
-
DD_AAS_DOTNET_EXTENSION_VERSION,
|
|
41
|
-
DD_AZURE_RESOURCE_GROUP,
|
|
42
44
|
FUNCTIONS_EXTENSION_VERSION,
|
|
43
45
|
FUNCTIONS_WORKER_RUNTIME,
|
|
44
46
|
FUNCTIONS_WORKER_RUNTIME_VERSION,
|
|
@@ -49,6 +51,9 @@ function buildMetadata () {
|
|
|
49
51
|
WEBSITE_SITE_NAME
|
|
50
52
|
} = getEnvironmentVariables()
|
|
51
53
|
|
|
54
|
+
const DD_AAS_DOTNET_EXTENSION_VERSION = getValueFromEnvSources('DD_AAS_DOTNET_EXTENSION_VERSION')
|
|
55
|
+
const DD_AZURE_RESOURCE_GROUP = getValueFromEnvSources('DD_AZURE_RESOURCE_GROUP')
|
|
56
|
+
|
|
52
57
|
const subscriptionID = extractSubscriptionID(WEBSITE_OWNER_NAME)
|
|
53
58
|
|
|
54
59
|
const siteName = WEBSITE_SITE_NAME
|