dd-trace 5.108.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.
Files changed (174) hide show
  1. package/ci/init.js +4 -3
  2. package/index.d.ts +88 -25
  3. package/loader-hook.mjs +88 -2
  4. package/package.json +19 -14
  5. package/packages/datadog-esbuild/index.js +26 -0
  6. package/packages/datadog-esbuild/src/utils.js +46 -3
  7. package/packages/datadog-instrumentations/src/ai.js +43 -48
  8. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js-context-methods.js +18 -0
  9. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +111 -0
  10. package/packages/datadog-instrumentations/src/aws-sdk.js +3 -1
  11. package/packages/datadog-instrumentations/src/connect.js +4 -3
  12. package/packages/datadog-instrumentations/src/electron.js +1 -1
  13. package/packages/datadog-instrumentations/src/fastify.js +4 -12
  14. package/packages/datadog-instrumentations/src/graphql.js +17 -6
  15. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
  16. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  17. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
  18. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
  19. package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
  20. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/aws-durable-execution-sdk-js.js +31 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
  24. package/packages/datadog-instrumentations/src/http/client.js +12 -2
  25. package/packages/datadog-instrumentations/src/ioredis.js +0 -1
  26. package/packages/datadog-instrumentations/src/iovalkey.js +1 -2
  27. package/packages/datadog-instrumentations/src/koa.js +3 -2
  28. package/packages/datadog-instrumentations/src/mariadb.js +4 -2
  29. package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
  30. package/packages/datadog-instrumentations/src/next.js +44 -10
  31. package/packages/datadog-instrumentations/src/openai.js +77 -18
  32. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
  33. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  34. package/packages/datadog-instrumentations/src/redis.js +0 -1
  35. package/packages/datadog-instrumentations/src/restify.js +4 -3
  36. package/packages/datadog-instrumentations/src/router.js +18 -5
  37. package/packages/datadog-instrumentations/src/selenium.js +1 -2
  38. package/packages/datadog-instrumentations/src/tedious.js +28 -0
  39. package/packages/datadog-instrumentations/src/vitest.js +323 -7
  40. package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
  41. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/checkpoint.js +31 -0
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +55 -0
  43. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +114 -0
  44. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +128 -0
  45. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/index.js +19 -0
  46. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +224 -0
  47. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +43 -0
  48. package/packages/datadog-plugin-aws-sdk/src/base.js +14 -16
  49. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
  50. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +100 -37
  51. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +108 -99
  52. package/packages/datadog-plugin-bullmq/src/filter.js +35 -0
  53. package/packages/datadog-plugin-bullmq/src/producer.js +84 -4
  54. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -1
  55. package/packages/datadog-plugin-fs/src/index.js +1 -0
  56. package/packages/datadog-plugin-http/src/client.js +8 -3
  57. package/packages/datadog-plugin-http2/src/client.js +5 -2
  58. package/packages/datadog-plugin-mysql/src/index.js +7 -2
  59. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
  60. package/packages/datadog-plugin-redis/src/index.js +1 -2
  61. package/packages/datadog-plugin-undici/src/index.js +5 -2
  62. package/packages/datadog-plugin-vitest/src/index.js +11 -2
  63. package/packages/datadog-webpack/index.js +17 -1
  64. package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
  65. package/packages/dd-trace/src/aiguard/channels.js +0 -1
  66. package/packages/dd-trace/src/aiguard/index.js +11 -49
  67. package/packages/dd-trace/src/aiguard/integrations/evaluate.js +46 -0
  68. package/packages/dd-trace/src/aiguard/integrations/openai.js +66 -0
  69. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +78 -0
  70. package/packages/{datadog-instrumentations/src/helpers/ai-messages.js → dd-trace/src/aiguard/messages/openai.js} +85 -193
  71. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +185 -0
  72. package/packages/dd-trace/src/aiguard/sdk.js +13 -1
  73. package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
  74. package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
  75. package/packages/dd-trace/src/appsec/channels.js +1 -0
  76. package/packages/dd-trace/src/appsec/downstream_requests.js +111 -58
  77. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +54 -12
  78. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +5 -1
  79. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +29 -4
  80. package/packages/dd-trace/src/appsec/index.js +10 -6
  81. package/packages/dd-trace/src/appsec/rasp/ssrf.js +19 -11
  82. package/packages/dd-trace/src/appsec/reporter.js +1 -0
  83. package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
  84. package/packages/dd-trace/src/appsec/telemetry/index.js +26 -0
  85. package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
  86. package/packages/dd-trace/src/config/defaults.js +22 -3
  87. package/packages/dd-trace/src/config/generated-config-types.d.ts +538 -1
  88. package/packages/dd-trace/src/config/helper.js +133 -33
  89. package/packages/dd-trace/src/config/index.js +4 -1
  90. package/packages/dd-trace/src/config/parsers.js +19 -1
  91. package/packages/dd-trace/src/config/supported-configurations.json +45 -5
  92. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +49 -35
  93. package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
  94. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
  95. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
  96. package/packages/dd-trace/src/debugger/index.js +5 -0
  97. package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
  98. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
  99. package/packages/dd-trace/src/dogstatsd.js +15 -8
  100. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
  101. package/packages/dd-trace/src/exporters/agentless/index.js +7 -5
  102. package/packages/dd-trace/src/exporters/agentless/intake.js +43 -0
  103. package/packages/dd-trace/src/exporters/agentless/writer.js +5 -4
  104. package/packages/dd-trace/src/exporters/common/request.js +1 -20
  105. package/packages/dd-trace/src/exporters/common/url.js +34 -0
  106. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
  107. package/packages/dd-trace/src/guardrails/index.js +8 -5
  108. package/packages/dd-trace/src/index.js +6 -4
  109. package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
  110. package/packages/dd-trace/src/llmobs/index.js +16 -5
  111. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
  112. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
  113. package/packages/dd-trace/src/llmobs/sdk.js +5 -3
  114. package/packages/dd-trace/src/llmobs/span_processor.js +4 -0
  115. package/packages/dd-trace/src/llmobs/tagger.js +55 -0
  116. package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
  117. package/packages/dd-trace/src/log/index.js +18 -20
  118. package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -1
  119. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
  120. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
  121. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
  122. package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
  123. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
  124. package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
  125. package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
  126. package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
  127. package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
  128. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +11 -4
  129. package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
  130. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  131. package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
  132. package/packages/dd-trace/src/opentelemetry/trace/index.js +2 -1
  133. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
  134. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
  135. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
  136. package/packages/dd-trace/src/opentracing/span_context.js +17 -0
  137. package/packages/dd-trace/src/plugin_manager.js +7 -1
  138. package/packages/dd-trace/src/plugins/ci_plugin.js +36 -3
  139. package/packages/dd-trace/src/plugins/index.js +3 -0
  140. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
  141. package/packages/dd-trace/src/plugins/util/test.js +80 -0
  142. package/packages/dd-trace/src/plugins/util/url.js +68 -1
  143. package/packages/dd-trace/src/plugins/util/web.js +19 -36
  144. package/packages/dd-trace/src/priority_sampler.js +1 -13
  145. package/packages/dd-trace/src/profiling/config.js +129 -198
  146. package/packages/dd-trace/src/profiling/constants.js +4 -7
  147. package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
  148. package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
  149. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
  150. package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
  151. package/packages/dd-trace/src/profiling/oom.js +71 -0
  152. package/packages/dd-trace/src/profiling/profiler.js +35 -23
  153. package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
  154. package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
  155. package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
  156. package/packages/dd-trace/src/ritm.js +4 -2
  157. package/packages/dd-trace/src/serverless.js +1 -2
  158. package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
  159. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +12 -0
  160. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +12 -0
  161. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
  162. package/packages/dd-trace/src/span_format.js +2 -1
  163. package/packages/dd-trace/src/span_processor.js +6 -0
  164. package/packages/dd-trace/src/telemetry/send-data.js +12 -10
  165. package/packages/dd-trace/src/tracer.js +15 -1
  166. package/packages/dd-trace/src/util.js +14 -0
  167. package/register.js +58 -1
  168. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  169. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  170. package/vendor/dist/pprof-format/index.js +1 -1
  171. package/vendor/dist/protobufjs/index.js +1 -1
  172. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  173. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +0 -284
  174. package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
@@ -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 { oomExportStrategies, snapshotKinds } = require('./constants')
17
+ const { ensureOOMExportStrategies } = require('./oom')
21
18
  const { tagger } = require('./tagger')
22
19
 
23
- class Config {
24
- constructor (options) {
25
- const AWS_LAMBDA_FUNCTION_NAME = getEnvironmentVariable('AWS_LAMBDA_FUNCTION_NAME')
26
-
27
- this.version = options.version
28
- this.service = options.service
29
- this.env = options.env
30
- this.functionname = AWS_LAMBDA_FUNCTION_NAME
31
-
32
- this.tags = {
33
- ...options.tags,
34
- ...tagger.parse({
35
- host: options.reportHostname ? require('os').hostname() : undefined,
36
- functionname: AWS_LAMBDA_FUNCTION_NAME,
37
- }),
38
- ...getAzureTagsFromMetadata(getIsAzureFunction() ? getAzureFunctionMetadata() : getAzureAppMetadata()),
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
- this.uploadCompression = { method: uploadCompression, level }
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
- const that = this
120
- function turnOffAsyncContextFrame (msg) {
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
- this.asyncContextFrameEnabled = options.DD_PROFILING_ASYNC_CONTEXT_FRAME_ENABLED ?? isACFActive
126
- if (this.asyncContextFrameEnabled && !isACFActive) {
127
- if (isAtLeast24) {
128
- turnOffAsyncContextFrame('with --no-async-context-frame')
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
- turnOffAsyncContextFrame('without --experimental-async-context-frame')
57
+ reason = 'without --experimental-async-context-frame'
131
58
  } else {
132
- turnOffAsyncContextFrame('but it requires at least Node.js 22.9.0')
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
- get systemInfoReport () {
142
- const report = {
143
- allocationProfilingEnabled: this.allocationProfilingEnabled,
144
- asyncContextFrameEnabled: this.asyncContextFrameEnabled,
145
- codeHotspotsEnabled: this.codeHotspotsEnabled,
146
- cpuProfilingEnabled: this.cpuProfilingEnabled,
147
- debugSourceMaps: this.debugSourceMaps,
148
- endpointCollectionEnabled: this.endpointCollectionEnabled,
149
- heapSamplingInterval: this.heapSamplingInterval,
150
- oomMonitoring: { ...this.oomMonitoring },
151
- profilerTypes: this.profilers.map(profiler => profiler.type),
152
- sourceMap: this.sourceMap,
153
- timelineEnabled: this.timelineEnabled,
154
- timelineSamplingEnabled: this.timelineSamplingEnabled,
155
- uploadCompression: { ...this.uploadCompression },
156
- v8ProfilerBugWorkaroundEnabled: this.v8ProfilerBugWorkaroundEnabled,
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
- module.exports = { Config }
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
- function getProfilers ({
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
- function getExportStrategy (name) {
218
- const strategy = Object.values(oomExportStrategies).find(value => value === name)
219
- if (strategy === undefined) {
220
- log.error('Unknown oom export strategy "%s"', name)
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(options)
162
+ return new AgentExporter(config)
238
163
  case 'file':
239
- return new FileExporter(options)
164
+ return new FileExporter(config)
240
165
  default:
241
166
  log.error('Unknown exporter "%s"', name)
242
167
  }
243
168
  }
244
169
 
245
- function ensureExporters (exporters, options) {
246
- return exporters.map((exporter) => getExporter(exporter, options))
247
- }
248
-
249
- function getProfiler (name, options) {
250
- switch (name) {
251
- case 'cpu':
252
- case 'wall':
253
- return new WallProfiler(options)
254
- case 'space':
255
- return new SpaceProfiler(options)
256
- default:
257
- log.error('Unknown profiler "%s"', name)
258
- }
259
- }
260
-
261
- function ensureProfilers (profilers, options) {
262
- const filteredProfilers = []
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
- // Events profiler is a profiler that produces timeline events. It is only
272
- // added if timeline is enabled and there's a wall profiler.
273
- if (options.timelineEnabled && filteredProfilers.some(profiler => profiler instanceof WallProfiler)) {
274
- filteredProfilers.push(new EventsProfiler(options))
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 filteredProfilers
220
+ return { tags, exporters, flushInterval, profilers, uploadCompression, systemInfoReport }
278
221
  }
279
222
 
280
- function buildExportCommand (options) {
281
- const tags = [...Object.entries(options.tags),
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
- const oomExportStrategies = Object.freeze({
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({ pprofPrefix: fileURLToPath(url) })
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 activation = ['true', '1'].includes(profilingEnabled)
22
- ? 'manual'
23
- : profilingEnabled === 'auto'
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
- logger,
29
- uploadTimeout: timeoutMs,
30
- libraryInjected: !!getValueFromEnvSources('DD_INJECTION_ENABLED'),
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
- constructor (config = {}) {
91
+ #backoffTime
92
+ #backoffTries
93
+
94
+ /** @param {import('./event_serializer').TracerConfig} config */
95
+ constructor (config) {
92
96
  super(config)
93
- const { url, uploadTimeout } = config
94
- this._url = url
97
+ this._url = config.url
98
+
99
+ const [backoffTries, backoffTime] = computeRetries(config.DD_PROFILING_UPLOAD_TIMEOUT)
95
100
 
96
- const [backoffTries, backoffTime] = computeRetries(uploadTimeout)
101
+ this.#backoffTime = backoffTime
102
+ this.#backoffTries = backoffTries
103
+ }
97
104
 
98
- this._backoffTime = backoffTime
99
- this._backoffTries = backoffTries
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._backoffTime,
132
- retries: this._backoffTries,
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._backoffTime * 2 ** attempt,
158
+ timeout: this.#backoffTime * 2 ** attempt,
152
159
  }
153
160
 
154
161
  docker.inject(options.headers)
155
162
 
156
- if (this._url.protocol === 'unix:') {
157
- options.socketPath = this._url.pathname
163
+ const url = parseUrl(this._url)
164
+ if (url.protocol === 'unix:') {
165
+ options.socketPath = url.pathname
158
166
  } else {
159
- const httpOptions = urlToHttpOptions(this._url)
160
- options.protocol = httpOptions.protocol
161
- options.hostname = httpOptions.hostname
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
@@ -8,16 +8,47 @@ const version = require('../../../../../package.json').version
8
8
  const { availableParallelism, libuvThreadPoolSize } = require('../libuv-size')
9
9
  const processTags = require('../../process-tags')
10
10
 
11
+ /** @typedef {import('../../config/config-base')} TracerConfig */
12
+
13
+ /**
14
+ * Maps the canonical profiling.enabled value to the activation reported in the
15
+ * profile event.
16
+ *
17
+ * @param {string} [enabled] - config.profiling.enabled ('true' | 'false' | 'auto')
18
+ * @returns {string}
19
+ */
20
+ function getActivation (enabled) {
21
+ if (enabled === 'auto') return 'auto'
22
+ if (enabled === 'true') return 'manual'
23
+ return 'unknown'
24
+ }
25
+
11
26
  class EventSerializer {
12
- constructor ({ env, host, service, version, libraryInjected, activation } = {}) {
13
- this._env = env
14
- this._host = host
15
- this._service = service
16
- this._appVersion = version
17
- this._libraryInjected = libraryInjected
18
- this._activation = activation || 'unknown'
27
+ #activation
28
+ #appVersion
29
+ #env
30
+ #host
31
+ #libraryInjected
32
+ #service
33
+
34
+ /** @param {TracerConfig} config */
35
+ constructor (config) {
36
+ this.#env = config.env
37
+ this.#host = config.reportHostname ? os.hostname() : undefined
38
+ this.#service = config.service
39
+ this.#appVersion = config.version
40
+ this.#libraryInjected = !!config.DD_INJECTION_ENABLED
41
+ this.#activation = getActivation(config.profiling?.enabled)
19
42
  }
20
43
 
44
+ /**
45
+ * Returns the destination URL for the near-OOM export subprocess, or nothing when this exporter
46
+ * does not support OOM export. Overridden by {@link AgentExporter} and {@link FileExporter}.
47
+ *
48
+ * @returns {URL | undefined}
49
+ */
50
+ getExportUrl () {}
51
+
21
52
  typeToFile (type) {
22
53
  return `${type}.pprof`
23
54
  }
@@ -43,21 +74,21 @@ class EventSerializer {
43
74
  endpoint_counts: endpointCounts,
44
75
  info: {
45
76
  application: {
46
- env: this._env,
47
- service: this._service,
77
+ env: this.#env,
78
+ service: this.#service,
48
79
  start_time: new Date(perf.nodeTiming.nodeStart + perf.timeOrigin).toISOString(),
49
- version: this._appVersion,
80
+ version: this.#appVersion,
50
81
  },
51
82
  platform: {
52
- hostname: this._host,
83
+ hostname: this.#host,
53
84
  kernel_name: os.type(),
54
85
  kernel_release: os.release(),
55
86
  kernel_version: os.version(),
56
87
  },
57
88
  profiler: {
58
- activation: this._activation,
89
+ activation: this.#activation,
59
90
  ssi: {
60
- mechanism: this._libraryInjected ? 'injected_agent' : 'none',
91
+ mechanism: this.#libraryInjected ? 'injected_agent' : 'none',
61
92
  },
62
93
  version,
63
94
  ...infos,
@@ -92,4 +123,4 @@ class EventSerializer {
92
123
  }
93
124
  }
94
125
 
95
- module.exports = { EventSerializer }
126
+ module.exports = { EventSerializer, getActivation }
@@ -2,6 +2,7 @@
2
2
 
3
3
  const fs = require('fs')
4
4
  const { promisify } = require('util')
5
+ const { pathToFileURL } = require('url')
5
6
  const { threadId } = require('worker_threads')
6
7
  const writeFile = promisify(fs.writeFile)
7
8
  const { EventSerializer } = require('./event_serializer')
@@ -10,14 +11,24 @@ const pad = (n) => String(n).padStart(2, '0')
10
11
 
11
12
  function formatDateTime (t) {
12
13
  return `${t.getUTCFullYear()}${pad(t.getUTCMonth() + 1)}${pad(t.getUTCDate())}` +
13
- `T${pad(t.getUTCHours())}${pad(t.getUTCMinutes())}${pad(t.getUTCSeconds())}Z`
14
+ `T${pad(t.getUTCHours())}${pad(t.getUTCMinutes())}${pad(t.getUTCSeconds())}Z`
14
15
  }
15
16
 
16
17
  class FileExporter extends EventSerializer {
17
- constructor (config = {}) {
18
+ #pprofPrefix
19
+
20
+ /**
21
+ * @param {import('./event_serializer').TracerConfig} config
22
+ * @param {string} [pprofPrefix] - File path prefix. Defaults to the config value, but the OOM
23
+ * export subprocess passes a path derived from a `file:` URL, which is not a config value.
24
+ */
25
+ constructor (config, pprofPrefix = config.DD_PROFILING_PPROF_PREFIX) {
18
26
  super(config)
19
- const { pprofPrefix } = config
20
- this._pprofPrefix = pprofPrefix || ''
27
+ this.#pprofPrefix = pprofPrefix
28
+ }
29
+
30
+ getExportUrl () {
31
+ return pathToFileURL(this.#pprofPrefix)
21
32
  }
22
33
 
23
34
  export (exportSpec) {
@@ -25,7 +36,7 @@ class FileExporter extends EventSerializer {
25
36
  const types = Object.keys(profiles)
26
37
  const dateStr = formatDateTime(end)
27
38
  const tasks = types.map(type => {
28
- return writeFile(`${this._pprofPrefix}${type}_worker_${threadId}_${dateStr}.pprof`, profiles[type])
39
+ return writeFile(`${this.#pprofPrefix}${type}_worker_${threadId}_${dateStr}.pprof`, profiles[type])
29
40
  })
30
41
  tasks.push(writeFile(`event_worker_${threadId}_${dateStr}.json`, this.getEventJSON(exportSpec)))
31
42
  return Promise.all(tasks)