dd-trace 5.109.0 → 5.110.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/init.js +4 -3
- package/index.d.ts +66 -24
- package/loader-hook.mjs +88 -2
- package/package.json +18 -14
- package/packages/datadog-esbuild/index.js +26 -0
- package/packages/datadog-esbuild/src/utils.js +46 -3
- package/packages/datadog-instrumentations/src/connect.js +4 -3
- package/packages/datadog-instrumentations/src/fastify.js +4 -12
- package/packages/datadog-instrumentations/src/graphql.js +17 -6
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
- package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
- package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
- package/packages/datadog-instrumentations/src/koa.js +3 -2
- package/packages/datadog-instrumentations/src/mariadb.js +4 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
- package/packages/datadog-instrumentations/src/next.js +10 -10
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/restify.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +18 -5
- package/packages/datadog-instrumentations/src/selenium.js +1 -2
- package/packages/datadog-instrumentations/src/tedious.js +28 -0
- package/packages/datadog-instrumentations/src/vitest.js +264 -7
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -1
- package/packages/datadog-plugin-http/src/client.js +8 -3
- package/packages/datadog-plugin-http2/src/client.js +5 -2
- package/packages/datadog-plugin-mysql/src/index.js +7 -2
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-undici/src/index.js +5 -2
- package/packages/datadog-plugin-vitest/src/index.js +7 -1
- package/packages/datadog-webpack/index.js +17 -1
- package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
- package/packages/dd-trace/src/aiguard/sdk.js +13 -1
- package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
- package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
- package/packages/dd-trace/src/appsec/index.js +10 -6
- package/packages/dd-trace/src/appsec/reporter.js +1 -0
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +26 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
- package/packages/dd-trace/src/config/defaults.js +22 -3
- package/packages/dd-trace/src/config/generated-config-types.d.ts +535 -1
- package/packages/dd-trace/src/config/helper.js +133 -33
- package/packages/dd-trace/src/config/index.js +4 -1
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/supported-configurations.json +21 -3
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
- package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
- package/packages/dd-trace/src/debugger/index.js +5 -0
- package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
- package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
- package/packages/dd-trace/src/exporters/common/request.js +1 -20
- package/packages/dd-trace/src/exporters/common/url.js +34 -0
- package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
- package/packages/dd-trace/src/guardrails/index.js +8 -5
- package/packages/dd-trace/src/index.js +6 -4
- package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
- package/packages/dd-trace/src/llmobs/index.js +16 -5
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
- package/packages/dd-trace/src/llmobs/sdk.js +5 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +4 -0
- package/packages/dd-trace/src/llmobs/tagger.js +55 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
- package/packages/dd-trace/src/log/index.js +18 -20
- package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
- package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +11 -4
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
- package/packages/dd-trace/src/opentelemetry/span.js +14 -3
- package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +2 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
- package/packages/dd-trace/src/opentracing/span_context.js +17 -0
- package/packages/dd-trace/src/plugin_manager.js +7 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +9 -1
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
- package/packages/dd-trace/src/plugins/util/test.js +80 -0
- package/packages/dd-trace/src/plugins/util/url.js +68 -1
- package/packages/dd-trace/src/plugins/util/web.js +19 -36
- package/packages/dd-trace/src/priority_sampler.js +1 -13
- package/packages/dd-trace/src/profiling/config.js +129 -198
- package/packages/dd-trace/src/profiling/constants.js +4 -7
- package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
- package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
- package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
- package/packages/dd-trace/src/profiling/oom.js +71 -0
- package/packages/dd-trace/src/profiling/profiler.js +35 -23
- package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
- package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
- package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +1 -2
- package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
- package/packages/dd-trace/src/span_format.js +2 -1
- package/packages/dd-trace/src/span_processor.js +6 -0
- package/packages/dd-trace/src/telemetry/send-data.js +12 -10
- package/packages/dd-trace/src/tracer.js +15 -1
- package/packages/dd-trace/src/util.js +14 -0
- package/register.js +58 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
|
@@ -13,7 +13,8 @@ const { storage } = require('../../../../datadog-core')
|
|
|
13
13
|
const legacyStorage = storage('legacy')
|
|
14
14
|
const urlFilter = require('./urlfilter')
|
|
15
15
|
const { createInferredProxySpan, finishInferredProxySpan } = require('./inferred_proxy')
|
|
16
|
-
const { extractURL, obfuscateQs, calculateHttpEndpoint } = require('./url')
|
|
16
|
+
const { extractURL, obfuscateQs, getQsObfuscator, calculateHttpEndpoint } = require('./url')
|
|
17
|
+
const { NETWORK_PEER_ADDRESS } = require('./http-otel-semantics')
|
|
17
18
|
|
|
18
19
|
const WEB = types.WEB
|
|
19
20
|
const SERVER = kinds.SERVER
|
|
@@ -389,6 +390,15 @@ function addRequestTags (context, spanType) {
|
|
|
389
390
|
[HTTP_USERAGENT]: req.headers['user-agent'],
|
|
390
391
|
})
|
|
391
392
|
|
|
393
|
+
// OTel `network.peer.address` is the immediate socket peer. It has no Datadog
|
|
394
|
+
// equivalent and the socket isn't available at serialization, so (unlike the
|
|
395
|
+
// other HTTP attributes, which are renamed centrally in span_format) it is set
|
|
396
|
+
// here directly when OTel semantics are enabled.
|
|
397
|
+
if (config.DD_TRACE_OTEL_SEMANTICS_ENABLED) {
|
|
398
|
+
const peerAddress = req.socket?.remoteAddress
|
|
399
|
+
if (peerAddress) span.setTag(NETWORK_PEER_ADDRESS, peerAddress)
|
|
400
|
+
}
|
|
401
|
+
|
|
392
402
|
// if client ip has already been set by appsec, no need to run it again
|
|
393
403
|
if (config.extractIp && !spanContext.hasTag(HTTP_CLIENT_IP)) {
|
|
394
404
|
const clientIp = config.extractIp(config, req)
|
|
@@ -433,26 +443,25 @@ function applyRouteOrEndpointTag (context) {
|
|
|
433
443
|
if (!span) return
|
|
434
444
|
const spanContext = span.context()
|
|
435
445
|
|
|
436
|
-
// AppSec
|
|
437
|
-
// route
|
|
438
|
-
|
|
439
|
-
// means the work has already been done; paths are stable between the
|
|
440
|
-
// two calls, so the second pass has nothing to add.
|
|
441
|
-
if (spanContext.hasTag(HTTP_ROUTE) || spanContext.hasTag(HTTP_ENDPOINT)) return
|
|
446
|
+
// AppSec runs this from a pre-finish hook and the finish path runs it
|
|
447
|
+
// again. http.route is terminal once set; nothing supersedes it.
|
|
448
|
+
if (spanContext.hasTag(HTTP_ROUTE)) return
|
|
442
449
|
|
|
443
450
|
// Skip the `Array.prototype.join` builtin in the empty / single-segment
|
|
444
451
|
// cases; `paths[0]` covers both (`undefined` is falsy for the empty case).
|
|
445
452
|
const route = paths.length > 1 ? paths.join('') : paths[0]
|
|
446
453
|
|
|
447
454
|
if (route) {
|
|
448
|
-
//
|
|
455
|
+
// A framework route supersedes any http.endpoint fallback a pre-finish
|
|
456
|
+
// call stamped before the route resolved; it must not be gated on
|
|
457
|
+
// HTTP_ENDPOINT.
|
|
449
458
|
span.setTag(HTTP_ROUTE, route)
|
|
450
459
|
return
|
|
451
460
|
}
|
|
452
461
|
|
|
453
|
-
|
|
462
|
+
// Route unavailable. Compute the http.endpoint fallback once across both calls.
|
|
463
|
+
if (!config.resourceRenamingEnabled || spanContext.hasTag(HTTP_ENDPOINT)) return
|
|
454
464
|
|
|
455
|
-
// Route is unavailable, compute http.endpoint once.
|
|
456
465
|
const url = spanContext.getTag(HTTP_URL)
|
|
457
466
|
const endpoint = url ? calculateHttpEndpoint(url) : '/'
|
|
458
467
|
span.setTag(HTTP_ENDPOINT, endpoint)
|
|
@@ -543,30 +552,4 @@ function getMiddlewareSetting (config) {
|
|
|
543
552
|
return true
|
|
544
553
|
}
|
|
545
554
|
|
|
546
|
-
function getQsObfuscator (config) {
|
|
547
|
-
const obfuscator = config.queryStringObfuscation
|
|
548
|
-
|
|
549
|
-
if (typeof obfuscator === 'boolean') {
|
|
550
|
-
return obfuscator
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
if (typeof obfuscator === 'string') {
|
|
554
|
-
if (obfuscator === '') return false // disable obfuscator
|
|
555
|
-
|
|
556
|
-
if (obfuscator === '.*') return true // optimize full redact
|
|
557
|
-
|
|
558
|
-
try {
|
|
559
|
-
return new RegExp(obfuscator, 'gi')
|
|
560
|
-
} catch (err) {
|
|
561
|
-
log.error('Web plugin error getting qs obfuscator', err)
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
if (config.hasOwnProperty('queryStringObfuscation')) {
|
|
566
|
-
log.error('Expected `queryStringObfuscation` to be a regex string or boolean.')
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
return true
|
|
570
|
-
}
|
|
571
|
-
|
|
572
555
|
module.exports = web
|
|
@@ -19,6 +19,7 @@ const RateLimiter = require('./rate_limiter')
|
|
|
19
19
|
const Sampler = require('./sampler')
|
|
20
20
|
const { setSamplingRules } = require('./startup-log')
|
|
21
21
|
const SamplingRule = require('./sampling_rule')
|
|
22
|
+
const { formatKnuthRate } = require('./util')
|
|
22
23
|
|
|
23
24
|
const {
|
|
24
25
|
SAMPLING_MECHANISM_DEFAULT,
|
|
@@ -36,19 +37,6 @@ const {
|
|
|
36
37
|
|
|
37
38
|
const DEFAULT_KEY = 'service:,env:'
|
|
38
39
|
|
|
39
|
-
/**
|
|
40
|
-
* Formats a sampling rate as a string with up to 6 decimal digits and no trailing zeros.
|
|
41
|
-
*
|
|
42
|
-
* @param {number} rate
|
|
43
|
-
*/
|
|
44
|
-
function formatKnuthRate (rate) {
|
|
45
|
-
const string = Number(rate).toFixed(6)
|
|
46
|
-
for (let i = string.length - 1; i > 0; i--) {
|
|
47
|
-
if (string[i] === '0') continue
|
|
48
|
-
return string.slice(0, i + (string[i] === '.' ? 0 : 1))
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
40
|
const defaultSampler = new Sampler(AUTO_KEEP)
|
|
53
41
|
|
|
54
42
|
/**
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const path = require('path')
|
|
4
|
-
const { pathToFileURL } = require('url')
|
|
5
|
-
|
|
6
3
|
const satisfies = require('../../../../vendor/dist/semifies')
|
|
7
4
|
const getGitMetadata = require('../git_metadata')
|
|
8
5
|
const log = require('../log')
|
|
9
6
|
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
|
|
10
7
|
const { getIsAzureFunction } = require('../serverless')
|
|
11
8
|
const { getAzureTagsFromMetadata, getAzureAppMetadata, getAzureFunctionMetadata } = require('../azure_metadata')
|
|
12
|
-
const { getEnvironmentVariable } = require('../config/helper')
|
|
9
|
+
const { getEnvironmentVariable, getValueFromEnvSources } = require('../config/helper')
|
|
13
10
|
const { isACFActive } = require('../../../datadog-core/src/storage')
|
|
14
11
|
|
|
15
12
|
const { AgentExporter } = require('./exporters/agent')
|
|
@@ -17,152 +14,93 @@ const { FileExporter } = require('./exporters/file')
|
|
|
17
14
|
const WallProfiler = require('./profilers/wall')
|
|
18
15
|
const SpaceProfiler = require('./profilers/space')
|
|
19
16
|
const EventsProfiler = require('./profilers/events')
|
|
20
|
-
const {
|
|
17
|
+
const { ensureOOMExportStrategies } = require('./oom')
|
|
21
18
|
const { tagger } = require('./tagger')
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const { commitSHA, repositoryUrl } = getGitMetadata(options)
|
|
42
|
-
if (repositoryUrl && commitSHA) {
|
|
43
|
-
this.tags[GIT_REPOSITORY_URL] = repositoryUrl
|
|
44
|
-
this.tags[GIT_COMMIT_SHA] = commitSHA
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Normalize from seconds to milliseconds. Default must be longer than a minute.
|
|
48
|
-
this.flushInterval = options.DD_PROFILING_UPLOAD_PERIOD * 1000
|
|
49
|
-
this.uploadTimeout = options.DD_PROFILING_UPLOAD_TIMEOUT
|
|
50
|
-
this.sourceMap = options.DD_PROFILING_SOURCE_MAP
|
|
51
|
-
this.debugSourceMaps = options.DD_PROFILING_DEBUG_SOURCE_MAPS
|
|
52
|
-
this.endpointCollectionEnabled = options.DD_PROFILING_ENDPOINT_COLLECTION_ENABLED
|
|
53
|
-
this.pprofPrefix = options.DD_PROFILING_PPROF_PREFIX
|
|
54
|
-
this.v8ProfilerBugWorkaroundEnabled = options.DD_PROFILING_V8_PROFILER_BUG_WORKAROUND
|
|
55
|
-
|
|
56
|
-
this.url = options.url
|
|
57
|
-
|
|
58
|
-
this.libraryInjected = !!options.DD_INJECTION_ENABLED
|
|
59
|
-
|
|
60
|
-
let activation
|
|
61
|
-
if (options.profiling.enabled === 'auto') {
|
|
62
|
-
activation = 'auto'
|
|
63
|
-
} else if (options.profiling.enabled === 'true') {
|
|
64
|
-
activation = 'manual'
|
|
65
|
-
} // else activation = undefined
|
|
66
|
-
|
|
67
|
-
this.activation = activation
|
|
68
|
-
this.exporters = ensureExporters(options.DD_PROFILING_EXPORTERS, this)
|
|
69
|
-
|
|
70
|
-
const oomMonitoringEnabled = options.DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED
|
|
71
|
-
const heapLimitExtensionSize = options.DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE
|
|
72
|
-
const maxHeapExtensionCount = options.DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT
|
|
73
|
-
const exportStrategies = oomMonitoringEnabled
|
|
74
|
-
? ensureOOMExportStrategies(options.DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES)
|
|
75
|
-
: []
|
|
76
|
-
const exportCommand = oomMonitoringEnabled ? buildExportCommand(this) : undefined
|
|
77
|
-
this.oomMonitoring = {
|
|
78
|
-
enabled: oomMonitoringEnabled,
|
|
79
|
-
heapLimitExtensionSize,
|
|
80
|
-
maxHeapExtensionCount,
|
|
81
|
-
exportStrategies,
|
|
82
|
-
exportCommand,
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const profilers = getProfilers(options)
|
|
86
|
-
|
|
87
|
-
this.timelineEnabled = options.DD_PROFILING_TIMELINE_ENABLED
|
|
88
|
-
this.timelineSamplingEnabled = options.DD_INTERNAL_PROFILING_TIMELINE_SAMPLING_ENABLED
|
|
89
|
-
this.allocationProfilingEnabled = options.DD_PROFILING_ALLOCATION_ENABLED
|
|
90
|
-
this.codeHotspotsEnabled = options.DD_PROFILING_CODEHOTSPOTS_ENABLED
|
|
91
|
-
this.cpuProfilingEnabled = options.DD_PROFILING_CPU_ENABLED
|
|
92
|
-
this.heapSamplingInterval = options.DD_PROFILING_HEAP_SAMPLING_INTERVAL
|
|
93
|
-
|
|
94
|
-
this.samplingInterval = 1e3 / 99 // 99hz in milliseconds
|
|
95
|
-
|
|
96
|
-
const isAtLeast24 = satisfies(process.versions.node, '>=24.0.0')
|
|
97
|
-
|
|
98
|
-
const uploadCompression0 = options.DD_PROFILING_DEBUG_UPLOAD_COMPRESSION
|
|
99
|
-
let [uploadCompression, level0] = uploadCompression0.split('-')
|
|
100
|
-
let level = level0 ? Number.parseInt(level0, 10) : undefined
|
|
101
|
-
if (level !== undefined) {
|
|
102
|
-
const maxLevel = { gzip: 9, zstd: 22 }[uploadCompression]
|
|
103
|
-
if (level > maxLevel) {
|
|
104
|
-
log.warn('Invalid compression level %d. Will use %d.', level, maxLevel)
|
|
105
|
-
level = maxLevel
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Default to either zstd (on Node.js 24+) or gzip (earlier Node.js). We could default to ztsd
|
|
110
|
-
// everywhere as we ship a Rust zstd compressor for older Node.js versions, but on 24+ we use
|
|
111
|
-
// the built-in one that runs asynchronously on libuv worker threads, just as gzip does. This is
|
|
112
|
-
// the least disruptive choice.
|
|
113
|
-
if (uploadCompression === 'on') {
|
|
114
|
-
uploadCompression = isAtLeast24 ? 'zstd' : 'gzip'
|
|
115
|
-
}
|
|
20
|
+
/** @typedef {import('../config/config-base')} TracerConfig */
|
|
21
|
+
/** @typedef {AgentExporter | FileExporter} ProfilingExporter */
|
|
22
|
+
/** @typedef {WallProfiler | SpaceProfiler | EventsProfiler} ProfilingProfiler */
|
|
23
|
+
|
|
24
|
+
/** @param {TracerConfig} config */
|
|
25
|
+
function getProfilingTags (config) {
|
|
26
|
+
const functionName = getEnvironmentVariable('AWS_LAMBDA_FUNCTION_NAME')
|
|
27
|
+
|
|
28
|
+
const tags = {
|
|
29
|
+
...config.tags,
|
|
30
|
+
...tagger.parse({
|
|
31
|
+
host: config.reportHostname ? require('os').hostname() : undefined,
|
|
32
|
+
functionname: functionName,
|
|
33
|
+
}),
|
|
34
|
+
...getAzureTagsFromMetadata(getIsAzureFunction() ? getAzureFunctionMetadata() : getAzureAppMetadata()),
|
|
35
|
+
}
|
|
116
36
|
|
|
117
|
-
|
|
37
|
+
const { commitSHA, repositoryUrl } = getGitMetadata(config)
|
|
38
|
+
if (repositoryUrl && commitSHA) {
|
|
39
|
+
tags[GIT_REPOSITORY_URL] = repositoryUrl
|
|
40
|
+
tags[GIT_COMMIT_SHA] = commitSHA
|
|
41
|
+
}
|
|
118
42
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
log.warn('DD_PROFILING_ASYNC_CONTEXT_FRAME_ENABLED was set %s, it will have no effect.', msg)
|
|
122
|
-
that.asyncContextFrameEnabled = false
|
|
123
|
-
}
|
|
43
|
+
return tags
|
|
44
|
+
}
|
|
124
45
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
46
|
+
/** @param {TracerConfig} config */
|
|
47
|
+
function getAsyncContextFrameEnabled (config) {
|
|
48
|
+
const enabled = config.DD_PROFILING_ASYNC_CONTEXT_FRAME_ENABLED
|
|
49
|
+
if (enabled && !isACFActive) {
|
|
50
|
+
// The default value already tracks runtime support, so an unset config landing
|
|
51
|
+
// here is expected; only an explicit opt-in the runtime can't honor is worth a warning.
|
|
52
|
+
if (getValueFromEnvSources('DD_PROFILING_ASYNC_CONTEXT_FRAME_ENABLED', true)) {
|
|
53
|
+
let reason
|
|
54
|
+
if (satisfies(process.versions.node, '>=24.0.0')) {
|
|
55
|
+
reason = 'with --no-async-context-frame'
|
|
129
56
|
} else if (satisfies(process.versions.node, '>=22.9.0')) {
|
|
130
|
-
|
|
57
|
+
reason = 'without --experimental-async-context-frame'
|
|
131
58
|
} else {
|
|
132
|
-
|
|
59
|
+
reason = 'but it requires at least Node.js 22.9.0'
|
|
133
60
|
}
|
|
61
|
+
log.warn('DD_PROFILING_ASYNC_CONTEXT_FRAME_ENABLED was set %s, it will have no effect.', reason)
|
|
134
62
|
}
|
|
135
|
-
|
|
136
|
-
this.heartbeatInterval = options.telemetry.heartbeatInterval
|
|
137
|
-
|
|
138
|
-
this.profilers = ensureProfilers(profilers, this)
|
|
63
|
+
return false
|
|
139
64
|
}
|
|
65
|
+
return enabled
|
|
66
|
+
}
|
|
140
67
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
68
|
+
/**
|
|
69
|
+
* @param {TracerConfig} config
|
|
70
|
+
* @param {{
|
|
71
|
+
* asyncContextFrameEnabled: boolean,
|
|
72
|
+
* flushInterval: number,
|
|
73
|
+
* tags: Record<string, string>,
|
|
74
|
+
* exporters: ProfilingExporter[],
|
|
75
|
+
* }} runtime
|
|
76
|
+
*/
|
|
77
|
+
function createProfilers (config, { asyncContextFrameEnabled, flushInterval, tags, exporters }) {
|
|
78
|
+
const profilers = []
|
|
79
|
+
for (const name of selectProfilerTypes(config)) {
|
|
80
|
+
switch (name) {
|
|
81
|
+
case 'cpu':
|
|
82
|
+
case 'wall':
|
|
83
|
+
profilers.push(new WallProfiler(config, { asyncContextFrameEnabled, flushInterval }))
|
|
84
|
+
break
|
|
85
|
+
case 'space':
|
|
86
|
+
profilers.push(new SpaceProfiler(config, { tags, exporters }))
|
|
87
|
+
break
|
|
88
|
+
default:
|
|
89
|
+
log.error('Unknown profiler "%s"', name)
|
|
157
90
|
}
|
|
158
|
-
delete report.oomMonitoring.exportCommand
|
|
159
|
-
return report
|
|
160
91
|
}
|
|
161
|
-
}
|
|
162
92
|
|
|
163
|
-
|
|
93
|
+
// The events profiler produces timeline events. It is only added if timeline
|
|
94
|
+
// is enabled and there's a wall profiler.
|
|
95
|
+
if (config.DD_PROFILING_TIMELINE_ENABLED && profilers.some(profiler => profiler instanceof WallProfiler)) {
|
|
96
|
+
profilers.push(new EventsProfiler(config, { flushInterval }))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return profilers
|
|
100
|
+
}
|
|
164
101
|
|
|
165
|
-
|
|
102
|
+
/** @param {TracerConfig} config */
|
|
103
|
+
function selectProfilerTypes ({
|
|
166
104
|
DD_PROFILING_HEAP_ENABLED,
|
|
167
105
|
DD_PROFILING_WALLTIME_ENABLED,
|
|
168
106
|
DD_PROFILING_PROFILERS,
|
|
@@ -214,81 +152,74 @@ function getProfilers ({
|
|
|
214
152
|
return profilersArray
|
|
215
153
|
}
|
|
216
154
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return strategy
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function ensureOOMExportStrategies (strategies) {
|
|
226
|
-
const set = new Set()
|
|
227
|
-
for (const strategy of strategies) {
|
|
228
|
-
set.add(getExportStrategy(strategy))
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return [...set]
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function getExporter (name, options) {
|
|
155
|
+
/**
|
|
156
|
+
* @param {string} name
|
|
157
|
+
* @param {TracerConfig} config
|
|
158
|
+
*/
|
|
159
|
+
function getExporter (name, config) {
|
|
235
160
|
switch (name) {
|
|
236
161
|
case 'agent':
|
|
237
|
-
return new AgentExporter(
|
|
162
|
+
return new AgentExporter(config)
|
|
238
163
|
case 'file':
|
|
239
|
-
return new FileExporter(
|
|
164
|
+
return new FileExporter(config)
|
|
240
165
|
default:
|
|
241
166
|
log.error('Unknown exporter "%s"', name)
|
|
242
167
|
}
|
|
243
168
|
}
|
|
244
169
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
for (let i = 0; i < profilers.length; i++) {
|
|
265
|
-
const profiler = getProfiler(profilers[i], options)
|
|
266
|
-
if (profiler !== undefined) {
|
|
267
|
-
filteredProfilers.push(profiler)
|
|
170
|
+
/**
|
|
171
|
+
* Assembles everything the profiler needs from the tracer config: the runtime objects (tags,
|
|
172
|
+
* exporters, profilers) the {@link import('./profiler').Profiler#start} consumes and the system
|
|
173
|
+
* info report sent with each profile. The leaves read the canonical DD_PROFILING_* fields straight
|
|
174
|
+
* off the config; only the genuinely runtime values (tags, exporters, the resolved async context
|
|
175
|
+
* frame flag, the flush interval) are derived here.
|
|
176
|
+
*
|
|
177
|
+
* @param {TracerConfig} config
|
|
178
|
+
*/
|
|
179
|
+
function buildProfilingRuntime (config) {
|
|
180
|
+
const tags = getProfilingTags(config)
|
|
181
|
+
const exporters = []
|
|
182
|
+
for (const name of config.DD_PROFILING_EXPORTERS) {
|
|
183
|
+
const exporter = getExporter(name, config)
|
|
184
|
+
// getExporter logs and returns undefined for an unknown exporter name; drop it so a misconfigured
|
|
185
|
+
// DD_PROFILING_EXPORTERS entry can't crash the export path later.
|
|
186
|
+
if (exporter !== undefined) {
|
|
187
|
+
exporters.push(exporter)
|
|
268
188
|
}
|
|
269
189
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
190
|
+
const asyncContextFrameEnabled = getAsyncContextFrameEnabled(config)
|
|
191
|
+
const flushInterval = config.DD_PROFILING_UPLOAD_PERIOD * 1000
|
|
192
|
+
const profilers = createProfilers(config, { asyncContextFrameEnabled, flushInterval, tags, exporters })
|
|
193
|
+
const uploadCompression = config.DD_PROFILING_DEBUG_UPLOAD_COMPRESSION
|
|
194
|
+
|
|
195
|
+
const oomMonitoringEnabled = config.DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED
|
|
196
|
+
const systemInfoReport = {
|
|
197
|
+
allocationProfilingEnabled: config.DD_PROFILING_ALLOCATION_ENABLED,
|
|
198
|
+
asyncContextFrameEnabled,
|
|
199
|
+
codeHotspotsEnabled: config.DD_PROFILING_CODEHOTSPOTS_ENABLED,
|
|
200
|
+
cpuProfilingEnabled: config.DD_PROFILING_CPU_ENABLED,
|
|
201
|
+
debugSourceMaps: config.DD_PROFILING_DEBUG_SOURCE_MAPS,
|
|
202
|
+
endpointCollectionEnabled: config.DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
|
|
203
|
+
heapSamplingInterval: config.DD_PROFILING_HEAP_SAMPLING_INTERVAL,
|
|
204
|
+
oomMonitoring: {
|
|
205
|
+
enabled: oomMonitoringEnabled,
|
|
206
|
+
heapLimitExtensionSize: config.DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
|
|
207
|
+
maxHeapExtensionCount: config.DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
|
|
208
|
+
exportStrategies: oomMonitoringEnabled
|
|
209
|
+
? ensureOOMExportStrategies(config.DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES)
|
|
210
|
+
: [],
|
|
211
|
+
},
|
|
212
|
+
profilerTypes: profilers.map(profiler => profiler.type),
|
|
213
|
+
sourceMap: config.DD_PROFILING_SOURCE_MAP,
|
|
214
|
+
timelineEnabled: config.DD_PROFILING_TIMELINE_ENABLED,
|
|
215
|
+
timelineSamplingEnabled: config.DD_INTERNAL_PROFILING_TIMELINE_SAMPLING_ENABLED,
|
|
216
|
+
uploadCompression: { ...uploadCompression },
|
|
217
|
+
v8ProfilerBugWorkaroundEnabled: config.DD_PROFILING_V8_PROFILER_BUG_WORKAROUND,
|
|
275
218
|
}
|
|
276
219
|
|
|
277
|
-
return
|
|
220
|
+
return { tags, exporters, flushInterval, profilers, uploadCompression, systemInfoReport }
|
|
278
221
|
}
|
|
279
222
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
['snapshot', snapshotKinds.ON_OUT_OF_MEMORY]].map(([key, value]) => `${key}:${value}`).join(',')
|
|
283
|
-
const urls = []
|
|
284
|
-
for (const exporter of options.exporters) {
|
|
285
|
-
if (exporter instanceof AgentExporter) {
|
|
286
|
-
urls.push(options.url.toString())
|
|
287
|
-
} else if (exporter instanceof FileExporter) {
|
|
288
|
-
urls.push(pathToFileURL(options.pprofPrefix).toString())
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
return [process.execPath,
|
|
292
|
-
path.join(__dirname, 'exporter_cli.js'),
|
|
293
|
-
urls.join(','), tags, 'space']
|
|
223
|
+
module.exports = {
|
|
224
|
+
buildProfilingRuntime,
|
|
294
225
|
}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
// 99hz in milliseconds.
|
|
4
|
+
const SAMPLING_INTERVAL = 1e3 / 99
|
|
5
|
+
|
|
3
6
|
const snapshotKinds = Object.freeze({
|
|
4
7
|
PERIODIC: 'periodic',
|
|
5
8
|
ON_SHUTDOWN: 'on_shutdown',
|
|
6
9
|
ON_OUT_OF_MEMORY: 'on_oom',
|
|
7
10
|
})
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
PROCESS: 'process',
|
|
11
|
-
ASYNC_CALLBACK: 'async',
|
|
12
|
-
LOGS: 'logs',
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
module.exports = { snapshotKinds, oomExportStrategies }
|
|
12
|
+
module.exports = { SAMPLING_INTERVAL, snapshotKinds }
|
|
@@ -15,20 +15,20 @@ const timeoutMs = 15 * 1000
|
|
|
15
15
|
|
|
16
16
|
function exporterFromURL (url) {
|
|
17
17
|
if (url.protocol === 'file:') {
|
|
18
|
-
return new FileExporter({
|
|
18
|
+
return new FileExporter({}, fileURLToPath(url))
|
|
19
19
|
}
|
|
20
|
+
// The subprocess has no tracer config, so mirror the canonical config property
|
|
21
|
+
// names the exporters read. Normalize the raw env to the parsed profiling.enabled
|
|
22
|
+
// values ('true' | 'false' | 'auto') the main process would have produced.
|
|
20
23
|
const profilingEnabled = (getValueFromEnvSources('DD_PROFILING_ENABLED') ?? '').toLowerCase()
|
|
21
|
-
const
|
|
22
|
-
? '
|
|
23
|
-
: profilingEnabled
|
|
24
|
-
? 'auto'
|
|
25
|
-
: 'unknown'
|
|
24
|
+
const enabled = profilingEnabled === 'auto'
|
|
25
|
+
? 'auto'
|
|
26
|
+
: ['true', '1'].includes(profilingEnabled) ? 'true' : 'false'
|
|
26
27
|
return new AgentExporter({
|
|
27
28
|
url,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
activation,
|
|
29
|
+
DD_PROFILING_UPLOAD_TIMEOUT: timeoutMs,
|
|
30
|
+
DD_INJECTION_ENABLED: getValueFromEnvSources('DD_INJECTION_ENABLED'),
|
|
31
|
+
profiling: { enabled },
|
|
32
32
|
})
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
const { request: httpRequest } = require('http')
|
|
4
4
|
const { request: httpsRequest } = require('https')
|
|
5
5
|
const perf = require('perf_hooks').performance
|
|
6
|
-
const { urlToHttpOptions } = require('url')
|
|
7
6
|
|
|
8
7
|
const retry = require('../../../../../vendor/dist/retry')
|
|
9
8
|
// TODO: avoid using dd-trace internals. Make this a separate module?
|
|
10
9
|
const docker = require('../../exporters/common/docker')
|
|
11
10
|
const FormData = require('../../exporters/common/form-data')
|
|
11
|
+
const { parseUrl } = require('../../exporters/common/url')
|
|
12
12
|
const log = require('../../log')
|
|
13
13
|
const { storage } = require('../../../../datadog-core')
|
|
14
14
|
const version = require('../../../../../package.json').version
|
|
@@ -88,15 +88,22 @@ function computeRetries (uploadTimeout) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
class AgentExporter extends EventSerializer {
|
|
91
|
-
|
|
91
|
+
#backoffTime
|
|
92
|
+
#backoffTries
|
|
93
|
+
|
|
94
|
+
/** @param {import('./event_serializer').TracerConfig} config */
|
|
95
|
+
constructor (config) {
|
|
92
96
|
super(config)
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
this._url = config.url
|
|
98
|
+
|
|
99
|
+
const [backoffTries, backoffTime] = computeRetries(config.DD_PROFILING_UPLOAD_TIMEOUT)
|
|
95
100
|
|
|
96
|
-
|
|
101
|
+
this.#backoffTime = backoffTime
|
|
102
|
+
this.#backoffTries = backoffTries
|
|
103
|
+
}
|
|
97
104
|
|
|
98
|
-
|
|
99
|
-
this.
|
|
105
|
+
getExportUrl () {
|
|
106
|
+
return this._url
|
|
100
107
|
}
|
|
101
108
|
|
|
102
109
|
export (exportSpec) {
|
|
@@ -128,8 +135,8 @@ class AgentExporter extends EventSerializer {
|
|
|
128
135
|
return new Promise((resolve, reject) => {
|
|
129
136
|
const operation = retry.operation({
|
|
130
137
|
randomize: true,
|
|
131
|
-
minTimeout: this
|
|
132
|
-
retries: this
|
|
138
|
+
minTimeout: this.#backoffTime,
|
|
139
|
+
retries: this.#backoffTries,
|
|
133
140
|
unref: true,
|
|
134
141
|
})
|
|
135
142
|
|
|
@@ -148,18 +155,18 @@ class AgentExporter extends EventSerializer {
|
|
|
148
155
|
'DD-EVP-ORIGIN-VERSION': version,
|
|
149
156
|
...form.getHeaders(),
|
|
150
157
|
},
|
|
151
|
-
timeout: this
|
|
158
|
+
timeout: this.#backoffTime * 2 ** attempt,
|
|
152
159
|
}
|
|
153
160
|
|
|
154
161
|
docker.inject(options.headers)
|
|
155
162
|
|
|
156
|
-
|
|
157
|
-
|
|
163
|
+
const url = parseUrl(this._url)
|
|
164
|
+
if (url.protocol === 'unix:') {
|
|
165
|
+
options.socketPath = url.pathname
|
|
158
166
|
} else {
|
|
159
|
-
|
|
160
|
-
options.
|
|
161
|
-
options.
|
|
162
|
-
options.port = httpOptions.port
|
|
167
|
+
options.protocol = url.protocol
|
|
168
|
+
options.hostname = url.hostname
|
|
169
|
+
options.port = url.port
|
|
163
170
|
}
|
|
164
171
|
|
|
165
172
|
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|