dd-trace 5.109.0 → 5.111.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 (208) hide show
  1. package/README.md +13 -8
  2. package/ci/init.js +25 -5
  3. package/ci/vitest-no-worker-init-setup.mjs +430 -0
  4. package/ext/tags.js +2 -0
  5. package/index.d.ts +79 -24
  6. package/initialize.mjs +5 -6
  7. package/loader-hook.mjs +154 -47
  8. package/package.json +20 -16
  9. package/packages/datadog-esbuild/index.js +26 -0
  10. package/packages/datadog-esbuild/src/utils.js +46 -3
  11. package/packages/datadog-instrumentations/src/connect.js +4 -3
  12. package/packages/datadog-instrumentations/src/fastify.js +4 -12
  13. package/packages/datadog-instrumentations/src/fs.js +8 -6
  14. package/packages/datadog-instrumentations/src/graphql.js +26 -484
  15. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
  17. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
  18. package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
  19. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
  20. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
  23. package/packages/datadog-instrumentations/src/koa.js +3 -2
  24. package/packages/datadog-instrumentations/src/mariadb.js +4 -2
  25. package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
  26. package/packages/datadog-instrumentations/src/mongoose.js +14 -2
  27. package/packages/datadog-instrumentations/src/next.js +10 -10
  28. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
  29. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  30. package/packages/datadog-instrumentations/src/restify.js +4 -3
  31. package/packages/datadog-instrumentations/src/router.js +18 -5
  32. package/packages/datadog-instrumentations/src/selenium.js +1 -2
  33. package/packages/datadog-instrumentations/src/tedious.js +28 -0
  34. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
  35. package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
  36. package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
  37. package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
  38. package/packages/datadog-instrumentations/src/vitest.js +11 -1598
  39. package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
  40. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
  41. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
  43. package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
  44. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
  45. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
  46. package/packages/datadog-plugin-cucumber/src/index.js +1 -1
  47. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -5
  48. package/packages/datadog-plugin-cypress/src/support.js +45 -1
  49. package/packages/datadog-plugin-express/src/code_origin.js +1 -1
  50. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
  51. package/packages/datadog-plugin-graphql/src/execute.js +627 -11
  52. package/packages/datadog-plugin-graphql/src/index.js +20 -8
  53. package/packages/datadog-plugin-graphql/src/parse.js +24 -4
  54. package/packages/datadog-plugin-graphql/src/validate.js +16 -5
  55. package/packages/datadog-plugin-http/src/client.js +8 -3
  56. package/packages/datadog-plugin-http/src/index.js +6 -8
  57. package/packages/datadog-plugin-http2/src/client.js +5 -2
  58. package/packages/datadog-plugin-jest/src/index.js +1 -1
  59. package/packages/datadog-plugin-mocha/src/index.js +1 -1
  60. package/packages/datadog-plugin-mysql/src/index.js +7 -2
  61. package/packages/datadog-plugin-openai/src/services.js +2 -2
  62. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  63. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
  64. package/packages/datadog-plugin-undici/src/index.js +5 -2
  65. package/packages/datadog-plugin-vitest/src/index.js +76 -64
  66. package/packages/datadog-shimmer/src/shimmer.js +37 -16
  67. package/packages/datadog-webpack/index.js +17 -1
  68. package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
  69. package/packages/dd-trace/src/aiguard/sdk.js +15 -3
  70. package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
  71. package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
  72. package/packages/dd-trace/src/appsec/channels.js +3 -1
  73. package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
  74. package/packages/dd-trace/src/appsec/graphql.js +5 -5
  75. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
  76. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  77. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
  78. package/packages/dd-trace/src/appsec/index.js +20 -7
  79. package/packages/dd-trace/src/appsec/lambda.js +135 -0
  80. package/packages/dd-trace/src/appsec/reporter.js +50 -10
  81. package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
  82. package/packages/dd-trace/src/appsec/telemetry/index.js +27 -1
  83. package/packages/dd-trace/src/appsec/waf/index.js +16 -4
  84. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  85. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
  86. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
  87. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  88. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  89. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
  90. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
  91. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
  92. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
  93. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
  94. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
  95. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
  96. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
  97. package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
  98. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
  99. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
  100. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
  101. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
  102. package/packages/dd-trace/src/config/defaults.js +25 -5
  103. package/packages/dd-trace/src/config/generated-config-types.d.ts +596 -58
  104. package/packages/dd-trace/src/config/helper.js +134 -33
  105. package/packages/dd-trace/src/config/index.js +39 -23
  106. package/packages/dd-trace/src/config/major-overrides.js +4 -2
  107. package/packages/dd-trace/src/config/parsers.js +19 -1
  108. package/packages/dd-trace/src/config/remote_config.js +1 -1
  109. package/packages/dd-trace/src/config/supported-configurations.json +109 -57
  110. package/packages/dd-trace/src/constants.js +7 -0
  111. package/packages/dd-trace/src/datastreams/processor.js +11 -3
  112. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
  113. package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
  114. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
  115. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
  116. package/packages/dd-trace/src/debugger/index.js +5 -0
  117. package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
  118. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
  119. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
  120. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  121. package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
  122. package/packages/dd-trace/src/exporters/common/request.js +1 -20
  123. package/packages/dd-trace/src/exporters/common/url.js +34 -0
  124. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
  125. package/packages/dd-trace/src/guardrails/index.js +8 -5
  126. package/packages/dd-trace/src/index.js +6 -4
  127. package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
  128. package/packages/dd-trace/src/llmobs/index.js +17 -6
  129. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
  130. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
  131. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
  132. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  133. package/packages/dd-trace/src/llmobs/sdk.js +8 -6
  134. package/packages/dd-trace/src/llmobs/span_processor.js +5 -1
  135. package/packages/dd-trace/src/llmobs/tagger.js +58 -3
  136. package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
  137. package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
  138. package/packages/dd-trace/src/log/index.js +18 -20
  139. package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
  140. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
  141. package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
  142. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
  143. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
  144. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
  145. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
  146. package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
  147. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
  148. package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
  149. package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
  150. package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
  151. package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
  152. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +44 -38
  153. package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
  154. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  155. package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
  156. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  157. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
  158. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
  159. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  160. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
  161. package/packages/dd-trace/src/opentracing/span_context.js +17 -0
  162. package/packages/dd-trace/src/opentracing/tracer.js +6 -1
  163. package/packages/dd-trace/src/plugin_manager.js +14 -8
  164. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -4
  165. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
  166. package/packages/dd-trace/src/plugins/util/llm.js +6 -1
  167. package/packages/dd-trace/src/plugins/util/test.js +91 -9
  168. package/packages/dd-trace/src/plugins/util/url.js +68 -1
  169. package/packages/dd-trace/src/plugins/util/web.js +19 -36
  170. package/packages/dd-trace/src/priority_sampler.js +1 -13
  171. package/packages/dd-trace/src/profiling/config.js +129 -198
  172. package/packages/dd-trace/src/profiling/constants.js +4 -7
  173. package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
  174. package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
  175. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
  176. package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
  177. package/packages/dd-trace/src/profiling/oom.js +71 -0
  178. package/packages/dd-trace/src/profiling/profiler.js +35 -23
  179. package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
  180. package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
  181. package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
  182. package/packages/dd-trace/src/proxy.js +8 -8
  183. package/packages/dd-trace/src/ritm.js +4 -2
  184. package/packages/dd-trace/src/serverless.js +1 -2
  185. package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
  186. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
  187. package/packages/dd-trace/src/span_format.js +2 -1
  188. package/packages/dd-trace/src/span_processor.js +11 -6
  189. package/packages/dd-trace/src/span_stats.js +96 -78
  190. package/packages/dd-trace/src/startup-log.js +2 -1
  191. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  192. package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
  193. package/packages/dd-trace/src/telemetry/index.js +2 -2
  194. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  195. package/packages/dd-trace/src/telemetry/send-data.js +20 -18
  196. package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
  197. package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
  198. package/packages/dd-trace/src/tracer.js +15 -1
  199. package/packages/dd-trace/src/util.js +14 -0
  200. package/register.js +58 -1
  201. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  202. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  203. package/vendor/dist/pprof-format/index.js +1 -1
  204. package/vendor/dist/protobufjs/index.js +1 -1
  205. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  206. package/vendor/dist/shell-quote/index.js +1 -1
  207. package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
  208. package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
@@ -0,0 +1,71 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+
5
+ const log = require('../log')
6
+ const { snapshotKinds } = require('./constants')
7
+
8
+ const oomExportStrategies = Object.freeze({
9
+ PROCESS: 'process',
10
+ ASYNC_CALLBACK: 'async',
11
+ LOGS: 'logs',
12
+ })
13
+
14
+ /** @param {string} name */
15
+ function getExportStrategy (name) {
16
+ const strategy = Object.values(oomExportStrategies).find(value => value === name)
17
+ if (strategy === undefined) {
18
+ log.error('Unknown oom export strategy "%s"', name)
19
+ }
20
+ return strategy
21
+ }
22
+
23
+ /**
24
+ * Validates the configured strategies, dropping any the tracer does not recognize (each logged once).
25
+ *
26
+ * @param {string[]} strategies
27
+ * @returns {string[]}
28
+ */
29
+ function ensureOOMExportStrategies (strategies) {
30
+ const valid = new Set()
31
+ for (const strategy of strategies) {
32
+ const resolved = getExportStrategy(strategy)
33
+ if (resolved !== undefined) {
34
+ valid.add(resolved)
35
+ }
36
+ }
37
+ return [...valid]
38
+ }
39
+
40
+ /**
41
+ * @param {string[]} strategies
42
+ * @param {{ Async: number }} callbackMode
43
+ */
44
+ function strategiesToCallbackMode (strategies, callbackMode) {
45
+ return strategies.includes(oomExportStrategies.ASYNC_CALLBACK) ? callbackMode.Async : 0
46
+ }
47
+
48
+ /**
49
+ * Builds the argv the near-OOM export subprocess ({@link ./exporter_cli.js}) is spawned with. Each
50
+ * exporter reports its own destination URL, so the command stays agnostic of the exporter types.
51
+ *
52
+ * @param {Array<{ getExportUrl(): URL | undefined }>} exporters
53
+ * @param {Record<string, string>} tags
54
+ * @returns {string[]}
55
+ */
56
+ function buildExportCommand (exporters, tags) {
57
+ const tagString = [...Object.entries(tags),
58
+ ['snapshot', snapshotKinds.ON_OUT_OF_MEMORY]].map(([key, value]) => `${key}:${value}`).join(',')
59
+ const urls = []
60
+ for (const exporter of exporters) {
61
+ const url = exporter.getExportUrl()
62
+ if (url !== undefined) {
63
+ urls.push(url.toString())
64
+ }
65
+ }
66
+ return [process.execPath,
67
+ path.join(__dirname, 'exporter_cli.js'),
68
+ urls.join(','), tagString, 'space']
69
+ }
70
+
71
+ module.exports = { oomExportStrategies, ensureOOMExportStrategies, strategiesToCallbackMode, buildExportCommand }
@@ -4,7 +4,7 @@ const { EventEmitter } = require('events')
4
4
  const dc = require('dc-polyfill')
5
5
  const crashtracker = require('../crashtracking')
6
6
  const log = require('../log')
7
- const { Config } = require('./config')
7
+ const { buildProfilingRuntime } = require('./config')
8
8
  const { snapshotKinds } = require('./constants')
9
9
  const { threadNamePrefix } = require('./profilers/shared')
10
10
  const { isWebServerSpan, endpointNameFromTags, getStartedSpans } = require('./webspan-utils')
@@ -54,14 +54,19 @@ class Profiler extends EventEmitter {
54
54
  #compressionFn
55
55
  #compressionFnInitialized = false
56
56
  #compressionOptions
57
- #config
58
57
  #customLabelKeys = new Set()
59
58
  #enabled = false
60
59
  #endpointCounts = new Map()
60
+ #exporters
61
+ #flushInterval
61
62
  #lastStart
62
63
  #profileSeq = 0
64
+ #profilers
63
65
  #spanFinishListener
66
+ #systemInfoReport
67
+ #tags
64
68
  #timer
69
+ #uploadCompression
65
70
 
66
71
  constructor () {
67
72
  super()
@@ -71,7 +76,7 @@ class Profiler extends EventEmitter {
71
76
  get serverless () { return false }
72
77
 
73
78
  get flushInterval () {
74
- return this.#config?.flushInterval
79
+ return this.#flushInterval
75
80
  }
76
81
 
77
82
  get enabled () {
@@ -90,8 +95,8 @@ class Profiler extends EventEmitter {
90
95
  for (const key of keys) {
91
96
  this.#customLabelKeys.add(key)
92
97
  }
93
- if (this.#config) {
94
- for (const profiler of this.#config.profilers) {
98
+ if (this.#profilers) {
99
+ for (const profiler of this.#profilers) {
95
100
  profiler.setCustomLabelKeys?.(this.#customLabelKeys)
96
101
  }
97
102
  }
@@ -106,10 +111,10 @@ class Profiler extends EventEmitter {
106
111
  * @template T
107
112
  */
108
113
  runWithLabels (labels, fn) {
109
- if (!this.#enabled || !this.#config) {
114
+ if (!this.#enabled || !this.#profilers) {
110
115
  return fn()
111
116
  }
112
- for (const profiler of this.#config.profilers) {
117
+ for (const profiler of this.#profilers) {
113
118
  if (profiler.runWithLabels) {
114
119
  return profiler.runWithLabels(labels, fn)
115
120
  }
@@ -123,7 +128,7 @@ class Profiler extends EventEmitter {
123
128
  try {
124
129
  const { promisify } = require('util')
125
130
  const zlib = require('zlib')
126
- const { method, level: clevel } = this.#config.uploadCompression
131
+ const { method, level: clevel } = this.#uploadCompression
127
132
  switch (method) {
128
133
  case 'gzip':
129
134
  this.#compressionFn = promisify(zlib.gzip)
@@ -161,13 +166,20 @@ class Profiler extends EventEmitter {
161
166
  }
162
167
 
163
168
  /**
164
- * @param {import('../config/config-base')} options - Tracer configuration
169
+ * @param {import('../config/config-base')} config - Tracer configuration
165
170
  */
166
- start (options) {
171
+ start (config) {
167
172
  if (this.enabled) return true
168
173
  this.#enabled = true
169
174
 
170
- const config = this.#config = new Config(options)
175
+ const { tags, exporters, flushInterval, profilers, uploadCompression, systemInfoReport } =
176
+ buildProfilingRuntime(config)
177
+ this.#tags = tags
178
+ this.#exporters = exporters
179
+ this.#flushInterval = flushInterval
180
+ this.#profilers = profilers
181
+ this.#uploadCompression = uploadCompression
182
+ this.#systemInfoReport = systemInfoReport
171
183
 
172
184
  this._setInterval()
173
185
  // Log errors if the source map finder fails, but don't prevent the rest
@@ -176,11 +188,11 @@ class Profiler extends EventEmitter {
176
188
  const { setLogger, SourceMapper } = require('@datadog/pprof')
177
189
  setLogger(pprofLogger)
178
190
 
179
- if (config.sourceMap) {
180
- mapper = new SourceMapper(config.debugSourceMaps)
191
+ if (config.DD_PROFILING_SOURCE_MAP) {
192
+ mapper = new SourceMapper(config.DD_PROFILING_DEBUG_SOURCE_MAPS)
181
193
  mapper.loadDirectory(process.cwd())
182
194
  .then(() => {
183
- if (config.debugSourceMaps) {
195
+ if (config.DD_PROFILING_DEBUG_SOURCE_MAPS) {
184
196
  const count = mapper.infoMap.size
185
197
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
186
198
  log.debug(() => {
@@ -198,7 +210,7 @@ class Profiler extends EventEmitter {
198
210
  try {
199
211
  const start = new Date()
200
212
  const nearOOMCallback = this.#nearOOMExport.bind(this)
201
- for (const profiler of config.profilers) {
213
+ for (const profiler of profilers) {
202
214
  // TODO: move this out of Profiler when restoring sourcemap support
203
215
  profiler.start({
204
216
  mapper,
@@ -207,7 +219,7 @@ class Profiler extends EventEmitter {
207
219
  log.debug('Started %s profiler in %s thread', profiler.type, threadNamePrefix)
208
220
  }
209
221
 
210
- if (config.endpointCollectionEnabled) {
222
+ if (config.DD_PROFILING_ENDPOINT_COLLECTION_ENABLED) {
211
223
  this.#spanFinishListener = this.#onSpanFinish.bind(this)
212
224
  spanFinishedChannel.subscribe(this.#spanFinishListener)
213
225
  }
@@ -233,7 +245,7 @@ class Profiler extends EventEmitter {
233
245
  }
234
246
 
235
247
  _setInterval () {
236
- this._timeoutInterval = this.#config.flushInterval
248
+ this._timeoutInterval = this.#flushInterval
237
249
  }
238
250
 
239
251
  stop () {
@@ -255,7 +267,7 @@ class Profiler extends EventEmitter {
255
267
  this.#spanFinishListener = undefined
256
268
  }
257
269
 
258
- for (const profiler of this.#config.profilers) {
270
+ for (const profiler of this.#profilers) {
259
271
  profiler.stop()
260
272
  log.debug('Stopped %s profiler in %s thread', profiler.type, threadNamePrefix)
261
273
  }
@@ -298,7 +310,7 @@ class Profiler extends EventEmitter {
298
310
  #createInitialInfos () {
299
311
  return {
300
312
  serverless: this.serverless,
301
- settings: this.#config.systemInfoReport,
313
+ settings: this.#systemInfoReport,
302
314
  hasMissingSourceMaps: false,
303
315
  }
304
316
  }
@@ -307,7 +319,7 @@ class Profiler extends EventEmitter {
307
319
  if (!this.enabled) return
308
320
 
309
321
  try {
310
- if (this.#config.profilers.length === 0) {
322
+ if (this.#profilers.length === 0) {
311
323
  throw new Error('No profile types configured.')
312
324
  }
313
325
 
@@ -317,7 +329,7 @@ class Profiler extends EventEmitter {
317
329
 
318
330
  crashtracker.withProfilerSerializing(() => {
319
331
  // collect profiles synchronously so that profilers can be safely stopped asynchronously
320
- for (const profiler of this.#config.profilers) {
332
+ for (const profiler of this.#profilers) {
321
333
  const info = profiler.getInfo()
322
334
  const profile = profiler.profile(restart, startDate, endDate)
323
335
  if (!restart) {
@@ -377,7 +389,7 @@ class Profiler extends EventEmitter {
377
389
  }
378
390
 
379
391
  #submit (profiles, infos, start, end, snapshotKind) {
380
- const { tags } = this.#config
392
+ const tags = this.#tags
381
393
 
382
394
  // Flatten endpoint counts
383
395
  const endpointCounts = {}
@@ -392,7 +404,7 @@ class Profiler extends EventEmitter {
392
404
  ? [...this.#customLabelKeys]
393
405
  : undefined
394
406
  const exportSpec = { profiles, infos, start, end, tags, endpointCounts, customAttributes }
395
- const tasks = this.#config.exporters.map(exporter =>
407
+ const tasks = this.#exporters.map(exporter =>
396
408
  exporter.export(exportSpec).catch(error => {
397
409
  log.warn(error)
398
410
  })
@@ -12,6 +12,7 @@ const {
12
12
  ValueType,
13
13
  } = require('../../../../../vendor/dist/pprof-format')
14
14
  const { availableParallelism, effectiveLibuvThreadCount } = require('../libuv-size')
15
+ const { SAMPLING_INTERVAL } = require('../constants')
15
16
  const { END_TIMESTAMP_LABEL, SPAN_ID_LABEL, LOCAL_ROOT_SPAN_ID_LABEL, encodeProfileAsync } = require('./shared')
16
17
  const PoissonProcessSamplingFilter = require('./poisson')
17
18
  // perf_hooks uses millis, with fractional part representing nanos. We emit nanos into the pprof file.
@@ -19,6 +20,8 @@ const MS_TO_NS = 1_000_000
19
20
  // The number of sampling intervals that need to pass before we reset the Poisson process sampling instant.
20
21
  const POISSON_RESET_FACTOR = 2
21
22
 
23
+ /** @typedef {import('../../config/config-base')} TracerConfig */
24
+
22
25
  // While this is an "events profiler", meaning it emits a pprof file based on events observed as
23
26
  // perf_hooks events, the emitted pprof file uses the type "timeline".
24
27
  const pprofValueType = 'timeline'
@@ -50,8 +53,8 @@ function labelFromStrStr (stringTable, keyStr, valStr) {
50
53
  return labelFromStr(stringTable, stringTable.dedup(keyStr), valStr)
51
54
  }
52
55
 
53
- function getMaxSamples (options) {
54
- const maxCpuSamples = options.flushInterval / options.samplingInterval
56
+ function getMaxSamples (flushInterval, samplingInterval) {
57
+ const maxCpuSamples = flushInterval / samplingInterval
55
58
 
56
59
  // The lesser of max parallelism and libuv thread pool size, plus one so we can detect
57
60
  // oversubscription on libuv thread pool, plus another one for GC.
@@ -439,14 +442,18 @@ class EventsProfiler {
439
442
 
440
443
  get type () { return 'events' }
441
444
 
442
- constructor (options) {
443
- this.#maxSamples = getMaxSamples(options)
444
- this.#timelineSamplingEnabled = !!options.timelineSamplingEnabled
445
+ /**
446
+ * @param {TracerConfig} config
447
+ * @param {{ flushInterval: number }} runtime
448
+ */
449
+ constructor (config, { flushInterval }) {
450
+ this.#maxSamples = getMaxSamples(flushInterval, SAMPLING_INTERVAL)
451
+ this.#timelineSamplingEnabled = config.DD_INTERNAL_PROFILING_TIMELINE_SAMPLING_ENABLED
445
452
  this.#eventSerializer = new EventSerializer(this.#maxSamples)
446
453
 
447
454
  const eventHandler = event => this.#eventSerializer.addEvent(event)
448
455
  const eventFilter = this.#timelineSamplingEnabled
449
- ? createPoissonProcessSamplingFilter(options.samplingInterval)
456
+ ? createPoissonProcessSamplingFilter(SAMPLING_INTERVAL)
450
457
  : () => true
451
458
  const filteringEventHandler = event => {
452
459
  if (eventFilter(event)) {
@@ -454,7 +461,7 @@ class EventsProfiler {
454
461
  }
455
462
  }
456
463
 
457
- this.#eventSources = options.codeHotspotsEnabled
464
+ this.#eventSources = config.DD_PROFILING_CODEHOTSPOTS_ENABLED
458
465
  // Use Datadog instrumentation to collect events with span IDs. Still use
459
466
  // Node API for GC events.
460
467
  ? [
@@ -1,27 +1,35 @@
1
1
  'use strict'
2
2
 
3
- const { oomExportStrategies } = require('../constants')
3
+ const { oomExportStrategies, ensureOOMExportStrategies, strategiesToCallbackMode, buildExportCommand } =
4
+ require('../oom')
4
5
  const { encodeProfileAsync, getThreadLabels } = require('./shared')
5
6
 
6
- function strategiesToCallbackMode (strategies, callbackMode) {
7
- return strategies.includes(oomExportStrategies.ASYNC_CALLBACK) ? callbackMode.Async : 0
8
- }
7
+ /** @typedef {import('../../config/config-base')} TracerConfig */
8
+ /**
9
+ * @typedef {import('../exporters/agent').AgentExporter
10
+ * | import('../exporters/file').FileExporter} ProfilingExporter
11
+ */
9
12
 
10
13
  const STACK_DEPTH = 64
11
14
 
12
15
  class NativeSpaceProfiler {
16
+ #config
17
+ #exporters
13
18
  #mapper
14
- #oomMonitoring
15
19
  #pprof
16
- #allocationProfilingEnabled = false
17
- #samplingInterval = 512 * 1024
20
+ #samplingInterval
18
21
  #started = false
22
+ #tags
19
23
 
20
- constructor (options = {}) {
21
- // TODO: Remove default value. It is only used in testing.
22
- this.#samplingInterval = options.heapSamplingInterval || 512 * 1024
23
- this.#allocationProfilingEnabled = options.allocationProfilingEnabled
24
- this.#oomMonitoring = options.oomMonitoring || {}
24
+ /**
25
+ * @param {TracerConfig} config
26
+ * @param {{ tags: Record<string, string>, exporters: ProfilingExporter[] }} runtime
27
+ */
28
+ constructor (config, { tags, exporters }) {
29
+ this.#config = config
30
+ this.#tags = tags
31
+ this.#exporters = exporters
32
+ this.#samplingInterval = config.DD_PROFILING_HEAP_SAMPLING_INTERVAL
25
33
  }
26
34
 
27
35
  get type () {
@@ -31,16 +39,17 @@ class NativeSpaceProfiler {
31
39
  start ({ mapper, nearOOMCallback } = {}) {
32
40
  if (this.#started) return
33
41
 
42
+ const config = this.#config
34
43
  this.#mapper = mapper
35
44
  this.#pprof = require('@datadog/pprof')
36
- this.#pprof.heap.start(this.#samplingInterval, STACK_DEPTH, this.#allocationProfilingEnabled)
37
- if (this.#oomMonitoring.enabled) {
38
- const strategies = this.#oomMonitoring.exportStrategies
45
+ this.#pprof.heap.start(this.#samplingInterval, STACK_DEPTH, config.DD_PROFILING_ALLOCATION_ENABLED)
46
+ if (config.DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED) {
47
+ const strategies = ensureOOMExportStrategies(config.DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES)
39
48
  this.#pprof.heap.monitorOutOfMemory(
40
- this.#oomMonitoring.heapLimitExtensionSize,
41
- this.#oomMonitoring.maxHeapExtensionCount,
49
+ config.DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
50
+ config.DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
42
51
  strategies.includes(oomExportStrategies.LOGS),
43
- strategies.includes(oomExportStrategies.PROCESS) ? this.#oomMonitoring.exportCommand : [],
52
+ strategies.includes(oomExportStrategies.PROCESS) ? buildExportCommand(this.#exporters, this.#tags) : [],
44
53
  (profile) => nearOOMCallback(this.type, this.#pprof.encodeSync(profile), this.getInfo()),
45
54
  strategiesToCallbackMode(strategies, this.#pprof.heap.CallbackMode)
46
55
  )
@@ -7,6 +7,7 @@ const log = require('../../log')
7
7
  const runtimeMetrics = require('../../runtime_metrics')
8
8
  const telemetryMetrics = require('../../telemetry/metrics')
9
9
  const { isWebServerSpan, endpointNameFromTags, getStartedSpans } = require('../webspan-utils')
10
+ const { SAMPLING_INTERVAL } = require('../constants')
10
11
 
11
12
  const {
12
13
  END_TIMESTAMP_LABEL,
@@ -18,6 +19,8 @@ const {
18
19
  } = require('./shared')
19
20
  const TRACE_ENDPOINT_LABEL = 'trace endpoint'
20
21
 
22
+ /** @typedef {import('../../config/config-base')} TracerConfig */
23
+
21
24
  let beforeCh
22
25
  const enterCh = dc.channel('dd-trace:storage:enter')
23
26
  const spanFinishCh = dc.channel('dd-trace:span:finish')
@@ -108,13 +111,17 @@ class NativeWallProfiler {
108
111
  #asyncContextFrameEnabled = false
109
112
  #captureSpanData = false
110
113
  #codeHotspotsEnabled = false
114
+ #contextCountGaugeUpdater
111
115
  #cpuProfilingEnabled = false
116
+ #currentContext
112
117
  #customLabelsActive = false
113
118
  #customLabelKeys
114
119
  #endpointCollectionEnabled = false
115
120
  #flushIntervalMillis = 0
121
+ #lastSampleCount = 0
116
122
  #mapper
117
123
  #pprof
124
+ #profilerState
118
125
  #samplingIntervalMicros = 0
119
126
  #started = false
120
127
  #telemetryHeartbeatIntervalMillis = 0
@@ -130,17 +137,20 @@ class NativeWallProfiler {
130
137
 
131
138
  get type () { return 'wall' }
132
139
 
133
- constructor (options = {}) {
134
- this.#asyncContextFrameEnabled = !!options.asyncContextFrameEnabled
135
- this.#codeHotspotsEnabled = !!options.codeHotspotsEnabled
136
- this.#cpuProfilingEnabled = !!options.cpuProfilingEnabled
137
- this.#endpointCollectionEnabled = !!options.endpointCollectionEnabled
138
- this.#flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
139
- // TODO: Remove default value. It is only used in testing.
140
- this.#samplingIntervalMicros = (options.samplingInterval || 1e3 / 99) * 1000
141
- this.#telemetryHeartbeatIntervalMillis = options.heartbeatInterval || 60 * 1e3 // 60 seconds
142
- this.#timelineEnabled = !!options.timelineEnabled
143
- this.#v8ProfilerBugWorkaroundEnabled = !!options.v8ProfilerBugWorkaroundEnabled
140
+ /**
141
+ * @param {TracerConfig} config
142
+ * @param {{ asyncContextFrameEnabled: boolean, flushInterval: number }} runtime
143
+ */
144
+ constructor (config, { asyncContextFrameEnabled, flushInterval }) {
145
+ this.#asyncContextFrameEnabled = asyncContextFrameEnabled
146
+ this.#codeHotspotsEnabled = config.DD_PROFILING_CODEHOTSPOTS_ENABLED
147
+ this.#cpuProfilingEnabled = config.DD_PROFILING_CPU_ENABLED
148
+ this.#endpointCollectionEnabled = config.DD_PROFILING_ENDPOINT_COLLECTION_ENABLED
149
+ this.#flushIntervalMillis = flushInterval
150
+ this.#samplingIntervalMicros = SAMPLING_INTERVAL * 1000
151
+ this.#telemetryHeartbeatIntervalMillis = config.telemetry.DD_TELEMETRY_HEARTBEAT_INTERVAL
152
+ this.#timelineEnabled = config.DD_PROFILING_TIMELINE_ENABLED
153
+ this.#v8ProfilerBugWorkaroundEnabled = config.DD_PROFILING_V8_PROFILER_BUG_WORKAROUND
144
154
 
145
155
  // We need to capture span data into the sample context for either code hotspots
146
156
  // or endpoint collection.
@@ -192,8 +202,8 @@ class NativeWallProfiler {
192
202
  }
193
203
 
194
204
  if (this.#captureSpanData) {
195
- this._profilerState = this.#pprof.time.getState()
196
- this._lastSampleCount = 0
205
+ this.#profilerState = this.#pprof.time.getState()
206
+ this.#lastSampleCount = 0
197
207
 
198
208
  ensureChannelsActivated(this.#asyncContextFrameEnabled)
199
209
 
@@ -217,12 +227,12 @@ class NativeWallProfiler {
217
227
  const asyncContextsLiveGauge = profilerTelemetryMetrics.gauge('wall.async_contexts_live')
218
228
  const asyncContextsUsedGauge = profilerTelemetryMetrics.gauge('wall.async_contexts_used')
219
229
 
220
- this._contextCountGaugeUpdater = setInterval(() => {
230
+ this.#contextCountGaugeUpdater = setInterval(() => {
221
231
  const { totalAsyncContextCount, usedAsyncContextCount } = this.#pprof.time.getMetrics()
222
232
  asyncContextsLiveGauge.mark(totalAsyncContextCount)
223
233
  asyncContextsUsedGauge.mark(usedAsyncContextCount)
224
234
  }, this.#telemetryHeartbeatIntervalMillis)
225
- this._contextCountGaugeUpdater.unref?.()
235
+ this.#contextCountGaugeUpdater.unref?.()
226
236
  }
227
237
 
228
238
  #enter () {
@@ -271,16 +281,16 @@ class NativeWallProfiler {
271
281
  this.#pprof.time.setContext(sampleContext)
272
282
  }
273
283
  } else {
274
- const sampleCount = this._profilerState[kSampleCount]
275
- if (sampleCount !== this._lastSampleCount) {
276
- this._lastSampleCount = sampleCount
277
- const context = this._currentContext.ref
284
+ const sampleCount = this.#profilerState[kSampleCount]
285
+ if (sampleCount !== this.#lastSampleCount) {
286
+ this.#lastSampleCount = sampleCount
287
+ const context = this.#currentContext.ref
278
288
  this.#setNewContext()
279
289
 
280
290
  updateContext(context)
281
291
  }
282
292
 
283
- this._currentContext.ref = sampleContext
293
+ this.#currentContext.ref = sampleContext
284
294
  }
285
295
  }
286
296
 
@@ -322,10 +332,10 @@ class NativeWallProfiler {
322
332
  }
323
333
 
324
334
  #setNewContext () {
325
- this._currentContext = {
335
+ this.#currentContext = {
326
336
  ref: {},
327
337
  }
328
- this.#pprof.time.setContext(this._currentContext)
338
+ this.#pprof.time.setContext(this.#currentContext)
329
339
  }
330
340
 
331
341
  #spanFinished (span) {
@@ -360,7 +370,7 @@ class NativeWallProfiler {
360
370
  if (this.#captureSpanData && !this.#asyncContextFrameEnabled) {
361
371
  // update last sample context if needed
362
372
  this.#enter()
363
- this._lastSampleCount = 0
373
+ this.#lastSampleCount = 0
364
374
  }
365
375
 
366
376
  // Mark thread labels and trace endpoint label as good deduplication candidates
@@ -382,7 +392,7 @@ class NativeWallProfiler {
382
392
  this.#reportV8bug(v8BugDetected === 1)
383
393
  }
384
394
  } else {
385
- clearInterval(this._contextCountGaugeUpdater)
395
+ clearInterval(this.#contextCountGaugeUpdater)
386
396
  if (this.#captureSpanData) {
387
397
  if (!this.#asyncContextFrameEnabled) {
388
398
  beforeCh.unsubscribe(this.#boundEnter)
@@ -392,7 +402,7 @@ class NativeWallProfiler {
392
402
  if (this.#endpointCollectionEnabled) {
393
403
  tagsUpdateCh.unsubscribe(this.#boundSpanTagsUpdated)
394
404
  }
395
- this._profilerState = undefined
405
+ this.#profilerState = undefined
396
406
  }
397
407
  this.#started = false
398
408
  }
@@ -138,7 +138,7 @@ class Tracer extends NoopProxy {
138
138
  spanleak.startScrubber()
139
139
  }
140
140
 
141
- if (config.remoteConfig.enabled && !config.isCiVisibility) {
141
+ if (config.remoteConfig.DD_REMOTE_CONFIGURATION_ENABLED && !config.isCiVisibility) {
142
142
  const RemoteConfig = require('./remote_config')
143
143
  const rc = new RemoteConfig(config)
144
144
 
@@ -180,11 +180,11 @@ class Tracer extends NoopProxy {
180
180
  openfeatureRemoteConfig.enable(rc, config, () => this.openfeature)
181
181
  }
182
182
 
183
- if (config.profiling.enabled === 'true') {
183
+ if (config.profiling.DD_PROFILING_ENABLED === 'true') {
184
184
  this._profilerStarted = this._startProfiler(config)
185
185
  } else {
186
186
  this._profilerStarted = false
187
- if (config.profiling.enabled === 'auto') {
187
+ if (config.profiling.DD_PROFILING_ENABLED === 'auto') {
188
188
  const { SSIHeuristics } = require('./profiling/ssi-heuristics')
189
189
  const ssiHeuristics = new SSIHeuristics(config)
190
190
  ssiHeuristics.start()
@@ -216,7 +216,7 @@ class Tracer extends NoopProxy {
216
216
 
217
217
  this._modules.rewriter.enable(config)
218
218
 
219
- if (config.tracing && config.DD_CIVISIBILITY_MANUAL_API_ENABLED) {
219
+ if (config.DD_TRACE_ENABLED && config.testOptimization.DD_CIVISIBILITY_MANUAL_API_ENABLED) {
220
220
  const TestApiManualPlugin = require('./ci-visibility/test-api-manual/test-api-manual-plugin')
221
221
  this._testApiManualPlugin = new TestApiManualPlugin(this)
222
222
  // `shouldGetEnvironmentData` is passed as false so that we only lazily calculate it
@@ -225,7 +225,7 @@ class Tracer extends NoopProxy {
225
225
  this._testApiManualPlugin.configure({ ...config, enabled: true }, false)
226
226
  }
227
227
  if (config.DD_AGENTLESS_LOG_SUBMISSION_ENABLED) {
228
- if (config.apiKey) {
228
+ if (config.DD_API_KEY) {
229
229
  const LogSubmissionPlugin = require('./ci-visibility/log-submission/log-submission-plugin')
230
230
  const automaticLogPlugin = new LogSubmissionPlugin(this)
231
231
  automaticLogPlugin.configure({ ...config, enabled: true })
@@ -237,7 +237,7 @@ class Tracer extends NoopProxy {
237
237
  }
238
238
  }
239
239
 
240
- if (config.isTestDynamicInstrumentationEnabled) {
240
+ if (config.testOptimization.DD_TEST_FAILED_TEST_REPLAY_ENABLED) {
241
241
  const getDynamicInstrumentationClient = require('./ci-visibility/dynamic-instrumentation')
242
242
  // We instantiate the client but do not start the Worker here. The worker is started lazily
243
243
  getDynamicInstrumentationClient(config)
@@ -270,11 +270,11 @@ class Tracer extends NoopProxy {
270
270
  * @param {import('./config/config-base')} config - Tracer configuration
271
271
  */
272
272
  #updateTracing (config) {
273
- if (config.tracing !== false) {
273
+ if (config.DD_TRACE_ENABLED !== false) {
274
274
  if (config.appsec.enabled) {
275
275
  this._modules.appsec.enable(config)
276
276
  }
277
- if (config.llmobs.enabled) {
277
+ if (config.llmobs.DD_LLMOBS_ENABLED) {
278
278
  this._modules.llmobs.enable(config)
279
279
  }
280
280
  if (!this._tracingInitialized) {
@@ -8,7 +8,7 @@ const dc = require('dc-polyfill')
8
8
 
9
9
  const parse = require('../../../vendor/dist/module-details-from-path')
10
10
  const { isRelativeRequire } = require('../../datadog-instrumentations/src/helpers/shared-utils')
11
- const { getEnvironmentVariable, getValueFromEnvSources } = require('./config/helper')
11
+ const { getConfiguredEnvName, getEnvironmentVariable } = require('./config/helper')
12
12
 
13
13
  const origRequire = Module.prototype.require
14
14
  // derived from require-in-the-middle@3 with tweaks
@@ -143,7 +143,9 @@ function Hook (modules, options, onrequire) {
143
143
  name = moduleId
144
144
  } else {
145
145
  const inAWSLambda = getEnvironmentVariable('AWS_LAMBDA_FUNCTION_NAME') !== undefined
146
- const hasLambdaHandler = getValueFromEnvSources('DD_LAMBDA_HANDLER') !== undefined
146
+ // Presence check over all sources (incl. stable config) without parsing —
147
+ // parsing here would re-enter this require hook via config/defaults.
148
+ const hasLambdaHandler = getConfiguredEnvName('DD_LAMBDA_HANDLER') !== undefined
147
149
  const segments = filename.split(path.sep)
148
150
  const filenameFromNodeModule = segments.includes('node_modules')
149
151
  // decide how to assign the stat
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const { getEnvironmentVariable, getValueFromEnvSources } = require('./config/helper')
4
- const { isFalse } = require('./util')
5
4
 
6
5
  function getIsGCPFunction () {
7
6
  const isDeprecatedGCPFunction =
@@ -23,7 +22,7 @@ function getIsGCPFunction () {
23
22
  */
24
23
  function enableGCPPubSubPushSubscription () {
25
24
  return getEnvironmentVariable('K_SERVICE') !== undefined &&
26
- !isFalse(getValueFromEnvSources('DD_TRACE_GCP_PUBSUB_PUSH_ENABLED'))
25
+ getValueFromEnvSources('DD_TRACE_GCP_PUBSUB_PUSH_ENABLED')
27
26
  }
28
27
 
29
28
  function getIsAzureFunction () {