datadog 2.29.0 → 2.31.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 (214) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -2
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +21 -12
  4. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +9 -7
  5. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +18 -0
  6. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +10 -0
  7. data/ext/datadog_profiling_native_extension/extconf.rb +6 -24
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +5 -6
  9. data/ext/datadog_profiling_native_extension/http_transport.c +51 -64
  10. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -13
  11. data/ext/datadog_profiling_native_extension/profiling.c +3 -1
  12. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +24 -8
  13. data/ext/datadog_profiling_native_extension/setup_signal_handler.h +1 -3
  14. data/ext/datadog_profiling_native_extension/stack_recorder.c +29 -43
  15. data/ext/libdatadog_api/crashtracker.c +5 -8
  16. data/ext/libdatadog_api/crashtracker_report_exception.c +34 -144
  17. data/ext/libdatadog_api/datadog_ruby_common.c +18 -0
  18. data/ext/libdatadog_api/datadog_ruby_common.h +10 -0
  19. data/ext/libdatadog_api/di.c +79 -0
  20. data/ext/libdatadog_api/extconf.rb +5 -20
  21. data/ext/libdatadog_api/init.c +5 -2
  22. data/ext/libdatadog_extconf_helpers.rb +57 -11
  23. data/lib/datadog/ai_guard/component.rb +2 -0
  24. data/lib/datadog/ai_guard/configuration/settings.rb +3 -0
  25. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +41 -3
  26. data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
  27. data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
  28. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +3 -1
  29. data/lib/datadog/ai_guard/evaluation/request.rb +14 -9
  30. data/lib/datadog/ai_guard/evaluation/result.rb +3 -1
  31. data/lib/datadog/ai_guard/evaluation.rb +36 -7
  32. data/lib/datadog/ai_guard.rb +26 -8
  33. data/lib/datadog/appsec/autoload.rb +1 -1
  34. data/lib/datadog/appsec/component.rb +11 -7
  35. data/lib/datadog/appsec/contrib/active_record/patcher.rb +3 -0
  36. data/lib/datadog/appsec/contrib/devise/integration.rb +1 -1
  37. data/lib/datadog/appsec/contrib/excon/patcher.rb +2 -0
  38. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +1 -1
  39. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -1
  40. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  41. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +10 -11
  42. data/lib/datadog/appsec/contrib/rack/integration.rb +1 -1
  43. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +25 -2
  44. data/lib/datadog/appsec/contrib/rack/response_body.rb +36 -0
  45. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +2 -2
  46. data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
  47. data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
  48. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +2 -0
  49. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +2 -2
  50. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +3 -3
  51. data/lib/datadog/appsec/event.rb +1 -17
  52. data/lib/datadog/appsec/instrumentation/gateway/middleware.rb +2 -3
  53. data/lib/datadog/appsec/instrumentation/gateway.rb +2 -15
  54. data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -2
  55. data/lib/datadog/appsec/utils/http/media_type.rb +1 -2
  56. data/lib/datadog/appsec/utils/http/url_encoded.rb +2 -2
  57. data/lib/datadog/appsec.rb +5 -9
  58. data/lib/datadog/core/configuration/base.rb +17 -5
  59. data/lib/datadog/core/configuration/components.rb +21 -8
  60. data/lib/datadog/core/configuration/config_helper.rb +9 -0
  61. data/lib/datadog/core/configuration/option.rb +32 -6
  62. data/lib/datadog/core/configuration/option_definition.rb +38 -12
  63. data/lib/datadog/core/configuration/options.rb +41 -7
  64. data/lib/datadog/core/configuration/settings.rb +42 -3
  65. data/lib/datadog/core/configuration/supported_configurations.rb +17 -0
  66. data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
  67. data/lib/datadog/core/contrib/rails/utils.rb +7 -3
  68. data/lib/datadog/core/crashtracking/component.rb +7 -15
  69. data/lib/datadog/core/environment/container.rb +2 -2
  70. data/lib/datadog/core/environment/ext.rb +1 -0
  71. data/lib/datadog/core/environment/identity.rb +25 -3
  72. data/lib/datadog/core/environment/process.rb +12 -0
  73. data/lib/datadog/core/metrics/client.rb +5 -5
  74. data/lib/datadog/core/process_discovery.rb +5 -0
  75. data/lib/datadog/core/remote/component.rb +38 -21
  76. data/lib/datadog/core/runtime/metrics.rb +2 -3
  77. data/lib/datadog/core/telemetry/component.rb +3 -0
  78. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +2 -3
  79. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
  80. data/lib/datadog/core/telemetry/event/app_started.rb +151 -169
  81. data/lib/datadog/core/telemetry/event.rb +1 -7
  82. data/lib/datadog/core/telemetry/ext.rb +1 -0
  83. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -0
  84. data/lib/datadog/core/telemetry/worker.rb +20 -0
  85. data/lib/datadog/core/utils/base64.rb +1 -1
  86. data/lib/datadog/core/utils/only_once.rb +1 -1
  87. data/lib/datadog/core/utils/spawn_monkey_patch.rb +36 -0
  88. data/lib/datadog/core/workers/async.rb +1 -1
  89. data/lib/datadog/core/workers/interval_loop.rb +13 -6
  90. data/lib/datadog/core/workers/queue.rb +0 -4
  91. data/lib/datadog/core/workers/runtime_metrics.rb +9 -1
  92. data/lib/datadog/core.rb +0 -1
  93. data/lib/datadog/data_streams/pathway_context.rb +1 -1
  94. data/lib/datadog/data_streams/processor.rb +1 -0
  95. data/lib/datadog/di/boot.rb +3 -4
  96. data/lib/datadog/di/component.rb +20 -4
  97. data/lib/datadog/di/instrumenter.rb +20 -10
  98. data/lib/datadog/di/probe_manager.rb +79 -62
  99. data/lib/datadog/di/probe_notification_builder.rb +148 -33
  100. data/lib/datadog/di/probe_notifier_worker.rb +52 -6
  101. data/lib/datadog/di/probe_repository.rb +198 -0
  102. data/lib/datadog/di/remote.rb +5 -6
  103. data/lib/datadog/di/serializer.rb +127 -9
  104. data/lib/datadog/di/transport/http.rb +12 -3
  105. data/lib/datadog/di/transport/input.rb +46 -8
  106. data/lib/datadog/di.rb +81 -0
  107. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  108. data/lib/datadog/open_feature/configuration.rb +2 -0
  109. data/lib/datadog/open_feature/evaluation_engine.rb +1 -1
  110. data/lib/datadog/open_feature/exposures/reporter.rb +1 -1
  111. data/lib/datadog/open_feature/exposures/worker.rb +1 -1
  112. data/lib/datadog/open_feature/remote.rb +1 -1
  113. data/lib/datadog/open_feature/transport.rb +1 -1
  114. data/lib/datadog/opentelemetry/configuration/settings.rb +2 -0
  115. data/lib/datadog/profiling/collectors/code_provenance.rb +2 -3
  116. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +14 -1
  117. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  118. data/lib/datadog/profiling/component.rb +31 -1
  119. data/lib/datadog/profiling/http_transport.rb +5 -6
  120. data/lib/datadog/profiling/load_native_extension.rb +1 -1
  121. data/lib/datadog/profiling/profiler.rb +15 -12
  122. data/lib/datadog/profiling/scheduler.rb +2 -2
  123. data/lib/datadog/profiling/tasks/exec.rb +2 -2
  124. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  125. data/lib/datadog/profiling.rb +1 -2
  126. data/lib/datadog/single_step_instrument.rb +1 -1
  127. data/lib/datadog/tracing/buffer.rb +3 -3
  128. data/lib/datadog/tracing/component.rb +11 -0
  129. data/lib/datadog/tracing/configuration/settings.rb +2 -1
  130. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +2 -2
  131. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +20 -0
  132. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -1
  133. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  134. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  135. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  136. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  137. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  138. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  139. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  140. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  141. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  142. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  143. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  144. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
  145. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -1
  146. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
  147. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
  148. data/lib/datadog/tracing/contrib/configurable.rb +18 -3
  149. data/lib/datadog/tracing/contrib/dalli/integration.rb +4 -1
  150. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -1
  151. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +5 -1
  152. data/lib/datadog/tracing/contrib/ethon/ext.rb +1 -0
  153. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +5 -2
  154. data/lib/datadog/tracing/contrib/excon/ext.rb +1 -0
  155. data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -2
  156. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +5 -2
  157. data/lib/datadog/tracing/contrib/faraday/ext.rb +1 -0
  158. data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -2
  159. data/lib/datadog/tracing/contrib/grape/endpoint.rb +7 -7
  160. data/lib/datadog/tracing/contrib/grape/instrumentation.rb +13 -8
  161. data/lib/datadog/tracing/contrib/grape/patcher.rb +6 -1
  162. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +5 -2
  163. data/lib/datadog/tracing/contrib/grpc/ext.rb +1 -0
  164. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +5 -2
  165. data/lib/datadog/tracing/contrib/http/ext.rb +1 -0
  166. data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -1
  167. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +5 -2
  168. data/lib/datadog/tracing/contrib/httpclient/ext.rb +1 -0
  169. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +5 -2
  170. data/lib/datadog/tracing/contrib/httprb/ext.rb +1 -0
  171. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -1
  172. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +5 -1
  173. data/lib/datadog/tracing/contrib/karafka/ext.rb +1 -0
  174. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -1
  175. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +5 -2
  176. data/lib/datadog/tracing/contrib/que/ext.rb +1 -0
  177. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +5 -1
  178. data/lib/datadog/tracing/contrib/rack/ext.rb +1 -0
  179. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +5 -2
  180. data/lib/datadog/tracing/contrib/rails/ext.rb +1 -0
  181. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  182. data/lib/datadog/tracing/contrib/rails/patcher.rb +0 -1
  183. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  184. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +2 -2
  185. data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
  186. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +5 -2
  187. data/lib/datadog/tracing/contrib/rest_client/ext.rb +1 -0
  188. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +5 -1
  189. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +1 -0
  190. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +5 -1
  191. data/lib/datadog/tracing/contrib/sinatra/ext.rb +1 -0
  192. data/lib/datadog/tracing/contrib/status_range_matcher.rb +4 -0
  193. data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
  194. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +5 -1
  195. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +1 -0
  196. data/lib/datadog/tracing/distributed/datadog.rb +4 -2
  197. data/lib/datadog/tracing/event.rb +1 -1
  198. data/lib/datadog/tracing/metadata/ext.rb +4 -0
  199. data/lib/datadog/tracing/remote.rb +1 -1
  200. data/lib/datadog/tracing/sampling/ext.rb +2 -0
  201. data/lib/datadog/tracing/sampling/priority_sampler.rb +13 -0
  202. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  203. data/lib/datadog/tracing/sampling/rule_sampler.rb +54 -25
  204. data/lib/datadog/tracing/sampling/span/rule_parser.rb +1 -1
  205. data/lib/datadog/tracing/span_operation.rb +1 -1
  206. data/lib/datadog/tracing/sync_writer.rb +0 -1
  207. data/lib/datadog/tracing/trace_operation.rb +50 -6
  208. data/lib/datadog/tracing/tracer.rb +25 -0
  209. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  210. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  211. data/lib/datadog/tracing/writer.rb +0 -1
  212. data/lib/datadog/version.rb +1 -1
  213. metadata +15 -8
  214. data/lib/datadog/tracing/workers/trace_writer.rb +0 -204
@@ -45,7 +45,7 @@ module Datadog
45
45
  rescue Client::SyncError => e
46
46
  # Transient errors due to network or agent. Logged the error but not via telemetry
47
47
  logger.error do
48
- "remote worker client sync error: #{e.message} location: #{Array(e.backtrace).first}. skipping sync"
48
+ "remote worker client sync error: #{e.class}: #{e} location: #{Array(e.backtrace).first}. skipping sync"
49
49
  end
50
50
  rescue => e
51
51
  # In case of unexpected errors, reset the negotiation object
@@ -55,7 +55,7 @@ module Datadog
55
55
 
56
56
  # Transient errors due to network or agent. Logged the error but not via telemetry
57
57
  logger.error do
58
- "remote worker error: #{e.class.name} #{e.message} location: #{Array(e.backtrace).first}. " \
58
+ "remote worker error: #{e.class}: #{e} location: #{Array(e.backtrace).first}. " \
59
59
  'resetting client state'
60
60
  end
61
61
 
@@ -105,6 +105,7 @@ module Datadog
105
105
  class Barrier
106
106
  def initialize(timeout = nil)
107
107
  @once = false
108
+ @waited = false
108
109
  @timeout = timeout
109
110
 
110
111
  @mutex = Mutex.new
@@ -112,37 +113,53 @@ module Datadog
112
113
  end
113
114
 
114
115
  # Wait for first lift to happen, otherwise don't wait
116
+ #
117
+ # Returns:
118
+ # - :lift if the barrier was lifted (worker completed a cycle)
119
+ # - :timeout if the wait timed out before the barrier was lifted
120
+ # - :pass if wait_once was already called previously
121
+ #
122
+ # Uses a separate @waited flag to distinguish "already waited" (:pass)
123
+ # from "worker lifted before we could wait" (:lift). Without this,
124
+ # a race between Worker#start and wait_once can cause the first call
125
+ # to return :pass if the worker completes before wait_once runs.
115
126
  def wait_once(timeout = nil)
116
- # TTAS (Test and Test-And-Set) optimisation
117
- # Since @once only ever goes from false to true, this is semantically valid
118
- return :pass if @once
119
-
120
- begin
121
- @mutex.lock
127
+ # TTAS (Test and Test-And-Set) optimisation for subsequent calls.
128
+ # @waited is only set inside the mutex and only transitions false -> true,
129
+ # so an unsynchronized read is safe: a stale `false` just falls through
130
+ # to the synchronized path which re-checks.
131
+ return :pass if @waited
122
132
 
123
- return :pass if @once
124
-
125
- timeout ||= @timeout
133
+ @mutex.synchronize do
134
+ return :pass if @waited
126
135
 
127
- # - starting with Ruby 3.2, ConditionVariable#wait returns nil on
128
- # timeout and an integer otherwise
129
- # - before Ruby 3.2, ConditionVariable returns itself
130
- # so we have to rely on @once having been set
131
- if RUBY_VERSION >= '3.2'
132
- lifted = @condition.wait(@mutex, timeout)
136
+ if @once
137
+ # Worker lifted the barrier before we could wait.
138
+ # This is still the first call, so return :lift not :pass.
139
+ lifted = true
133
140
  else
134
- @condition.wait(@mutex, timeout)
135
- lifted = @once
141
+ timeout ||= @timeout
142
+
143
+ # - starting with Ruby 3.2, ConditionVariable#wait returns nil on
144
+ # timeout and an integer otherwise
145
+ # - before Ruby 3.2, ConditionVariable returns itself
146
+ # so we have to rely on @once having been set
147
+ if RUBY_VERSION >= '3.2'
148
+ lifted = @condition.wait(@mutex, timeout)
149
+ else
150
+ @condition.wait(@mutex, timeout)
151
+ lifted = @once
152
+ end
136
153
  end
137
154
 
155
+ @waited = true
156
+
138
157
  if lifted
139
158
  :lift
140
159
  else
141
160
  @once = true
142
161
  :timeout
143
162
  end
144
- ensure
145
- @mutex.unlock
146
163
  end
147
164
  end
148
165
 
@@ -26,8 +26,7 @@ module Datadog
26
26
  # Initialize the collection of runtime-id
27
27
  @runtime_id_enabled = options.fetch(:experimental_runtime_id_enabled, false)
28
28
 
29
- # Initialized process tags support
30
- @process_tags_enabled = options.fetch(:experimental_propagate_process_tags_enabled, false)
29
+ @process_tags_enabled = options.fetch(:experimental_propagate_process_tags_enabled)
31
30
  end
32
31
 
33
32
  # Associate service with runtime metrics
@@ -101,7 +100,7 @@ module Datadog
101
100
  def try_flush
102
101
  yield
103
102
  rescue => e
104
- Datadog.logger.warn("Error while sending runtime metric. Cause: #{e.class.name} #{e.message}")
103
+ Datadog.logger.warn("Error while sending runtime metric. Cause: #{e.class}: #{e}")
105
104
  end
106
105
 
107
106
  def default_metric_options
@@ -99,6 +99,7 @@ module Datadog
99
99
  @worker = Telemetry::Worker.new(
100
100
  enabled: @enabled,
101
101
  heartbeat_interval_seconds: settings.telemetry.heartbeat_interval_seconds,
102
+ extended_heartbeat_interval_seconds: settings.telemetry.extended_heartbeat_interval_seconds,
102
103
  metrics_aggregation_interval_seconds: settings.telemetry.metrics_aggregation_interval_seconds,
103
104
  emitter: Emitter.new(
104
105
  @transport,
@@ -108,6 +109,8 @@ module Datadog
108
109
  metrics_manager: @metrics_manager,
109
110
  dependency_collection: settings.telemetry.dependency_collection,
110
111
  logger: logger,
112
+ settings: settings,
113
+ agent_settings: agent_settings,
111
114
  shutdown_timeout: settings.telemetry.shutdown_timeout_seconds,
112
115
  )
113
116
 
@@ -26,14 +26,13 @@ module Datadog
26
26
 
27
27
  def configuration
28
28
  config = Datadog.configuration
29
- seq_id = Event.configuration_sequence.next
30
29
 
31
30
  res = @changes.map do |name, value|
32
31
  {
33
32
  name: name,
34
33
  value: value,
35
34
  origin: @origin,
36
- seq_id: seq_id,
35
+ seq_id: Configuration::Option::Precedence::REMOTE_CONFIGURATION.numeric.next,
37
36
  }
38
37
  end
39
38
 
@@ -43,7 +42,7 @@ module Datadog
43
42
  name: 'appsec.sca_enabled',
44
43
  value: config.appsec.sca_enabled,
45
44
  origin: 'code',
46
- seq_id: seq_id,
45
+ seq_id: Configuration::Option::Precedence::PROGRAMMATIC.numeric.next,
47
46
  }
48
47
  end
49
48
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'app_started'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Telemetry
8
+ module Event
9
+ # Telemetry class for the 'app-extended-heartbeat' event
10
+ class AppExtendedHeartbeat < AppStarted
11
+ def initialize(settings:, agent_settings:)
12
+ @configuration = configuration(settings, agent_settings)
13
+ end
14
+
15
+ def type
16
+ 'app-extended-heartbeat'
17
+ end
18
+
19
+ def payload
20
+ {
21
+ configuration: @configuration,
22
+ }
23
+ end
24
+
25
+ def app_started?
26
+ false
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -51,11 +51,10 @@ module Datadog
51
51
  # @type var products: telemetry_products
52
52
  products = {
53
53
  appsec: {
54
- # TODO take appsec status out of component tree?
55
- enabled: components.settings.appsec.enabled,
54
+ enabled: !!components.appsec,
56
55
  },
57
56
  profiler: {
58
- enabled: !!components.profiler&.enabled?,
57
+ enabled: !!components.profiler,
59
58
  },
60
59
  dynamic_instrumentation: {
61
60
  enabled: !!components.dynamic_instrumentation,
@@ -72,138 +71,39 @@ module Datadog
72
71
  products
73
72
  end
74
73
 
75
- TARGET_OPTIONS = %w[
76
- dynamic_instrumentation.enabled
77
- logger.level
78
- profiling.advanced.code_provenance_enabled
79
- profiling.advanced.endpoint.collection.enabled
80
- profiling.enabled
81
- runtime_metrics.enabled
82
- tracing.analytics.enabled
83
- tracing.propagation_style_extract
84
- tracing.propagation_style_inject
85
- tracing.enabled
86
- tracing.log_injection
87
- tracing.partial_flush.enabled
88
- tracing.partial_flush.min_spans_threshold
89
- tracing.report_hostname
90
- tracing.sampling.rate_limit
91
- apm.tracing.enabled
92
- ].freeze
93
-
94
- # standard:disable Metrics/AbcSize
95
- # standard:disable Metrics/MethodLength
96
74
  def configuration(settings, agent_settings)
97
- seq_id = Event.configuration_sequence.next
98
-
99
- # tracing.writer_options.buffer_size and tracing.writer_options.flush_interval have the same origin.
100
- writer_option_origin = get_telemetry_origin(settings, 'tracing.writer_options')
101
-
75
+ # Special values that are not tied to a configuration option
102
76
  list = [
103
- # Only set using env var as of June 2025
104
- conf_value('DD_GIT_REPOSITORY_URL', Core::Environment::Git.git_repository_url, seq_id, 'env_var'),
105
- conf_value('DD_GIT_COMMIT_SHA', Core::Environment::Git.git_commit_sha, seq_id, 'env_var'),
106
-
107
- # Set by the customer application (eg. `require 'datadog/auto_instrument'`)
108
77
  conf_value(
109
- 'tracing.auto_instrument.enabled',
110
- !defined?(Datadog::AutoInstrument::LOADED).nil?,
111
- seq_id,
112
- 'code'
78
+ 'DD_GIT_REPOSITORY_URL',
79
+ Core::Environment::Git.git_repository_url,
80
+ (Core::Environment::Git.git_repository_url ? Configuration::Option::Precedence::ENVIRONMENT : Configuration::Option::Precedence::DEFAULT)
113
81
  ),
114
82
  conf_value(
115
- 'tracing.opentelemetry.enabled',
116
- !defined?(Datadog::OpenTelemetry::LOADED).nil?,
117
- seq_id,
118
- 'code'
83
+ 'DD_GIT_COMMIT_SHA',
84
+ Core::Environment::Git.git_commit_sha,
85
+ (Core::Environment::Git.git_commit_sha ? Configuration::Option::Precedence::ENVIRONMENT : Configuration::Option::Precedence::DEFAULT)
119
86
  ),
120
87
 
121
88
  # Mix of env var, programmatic and default config, so we use unknown
122
- conf_value('DD_AGENT_TRANSPORT', agent_transport(agent_settings), seq_id, 'unknown'), # rubocop:disable CustomCops/EnvStringValidationCop
123
-
124
- # writer_options is defined as an option that has a Hash value.
125
- conf_value(
126
- 'tracing.writer_options.buffer_size',
127
- to_value(settings.tracing.writer_options[:buffer_size]),
128
- seq_id,
129
- writer_option_origin
130
- ),
131
- conf_value(
132
- 'tracing.writer_options.flush_interval',
133
- to_value(settings.tracing.writer_options[:flush_interval]),
134
- seq_id,
135
- writer_option_origin
136
- ),
137
-
138
- conf_value('DD_AGENT_HOST', settings.agent.host, seq_id, get_telemetry_origin(settings, 'agent.host')),
139
- conf_value(
140
- 'DD_TRACE_SAMPLE_RATE',
141
- to_value(settings.tracing.sampling.default_rate),
142
- seq_id,
143
- get_telemetry_origin(settings, 'tracing.sampling.default_rate')
144
- ),
145
- conf_value(
146
- 'DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED',
147
- settings.tracing.contrib.global_default_service_name.enabled,
148
- seq_id,
149
- get_telemetry_origin(settings, 'tracing.contrib.global_default_service_name.enabled')
150
- ),
151
- conf_value(
152
- 'DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED',
153
- settings.tracing.contrib.peer_service_defaults,
154
- seq_id,
155
- get_telemetry_origin(settings, 'tracing.contrib.peer_service_defaults')
156
- ),
157
- conf_value(
158
- 'DD_TRACE_DEBUG',
159
- settings.diagnostics.debug,
160
- seq_id,
161
- get_telemetry_origin(settings, 'diagnostics.debug')
162
- )
89
+ unknown_conf_value('DD_AGENT_TRANSPORT', agent_transport(agent_settings)), # rubocop:disable CustomCops/EnvStringValidationCop
163
90
  ]
164
91
 
165
- peer_service_mapping_str = ''
166
- unless settings.tracing.contrib.peer_service_mapping.empty?
167
- peer_service_mapping = settings.tracing.contrib.peer_service_mapping
168
- peer_service_mapping_str = peer_service_mapping.map { |key, value| "#{key}:#{value}" }.join(',')
169
- end
92
+ # Set by the customer application (eg. `require 'datadog/auto_instrument'`)
93
+ auto_instrument_enabled = !defined?(Datadog::AutoInstrument::LOADED).nil?
170
94
  list << conf_value(
171
- 'DD_TRACE_PEER_SERVICE_MAPPING',
172
- peer_service_mapping_str,
173
- seq_id,
174
- get_telemetry_origin(settings, 'tracing.contrib.peer_service_mapping')
95
+ 'tracing.auto_instrument.enabled',
96
+ auto_instrument_enabled,
97
+ auto_instrument_enabled ? Configuration::Option::Precedence::PROGRAMMATIC : Configuration::Option::Precedence::DEFAULT
175
98
  )
176
-
177
- # OpenTelemetry configuration options (using environment variable names)
178
- otel_exporter_headers_string = settings.opentelemetry.exporter.headers&.map { |key, value| "#{key}=#{value}" }&.join(',')
179
- otel_exporter_metrics_headers_string = settings.opentelemetry.metrics.headers&.map { |key, value| "#{key}=#{value}" }&.join(',')
180
- list.push(
181
- conf_value('OTEL_EXPORTER_OTLP_ENDPOINT', settings.opentelemetry.exporter.endpoint, seq_id, get_telemetry_origin(settings, 'opentelemetry.exporter.endpoint')),
182
- conf_value('OTEL_EXPORTER_OTLP_HEADERS', otel_exporter_headers_string, seq_id, get_telemetry_origin(settings, 'opentelemetry.exporter.headers')),
183
- conf_value('OTEL_EXPORTER_OTLP_PROTOCOL', settings.opentelemetry.exporter.protocol, seq_id, get_telemetry_origin(settings, 'opentelemetry.exporter.protocol')),
184
- conf_value('OTEL_EXPORTER_OTLP_TIMEOUT', settings.opentelemetry.exporter.timeout_millis, seq_id, get_telemetry_origin(settings, 'opentelemetry.exporter.timeout_millis')),
185
- conf_value('DD_METRICS_OTEL_ENABLED', settings.opentelemetry.metrics.enabled, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.enabled')),
186
- conf_value('OTEL_METRICS_EXPORTER', settings.opentelemetry.metrics.exporter, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.exporter')),
187
- conf_value('OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', settings.opentelemetry.metrics.endpoint, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.endpoint')),
188
- conf_value('OTEL_EXPORTER_OTLP_METRICS_HEADERS', otel_exporter_metrics_headers_string, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.headers')),
189
- conf_value('OTEL_EXPORTER_OTLP_METRICS_PROTOCOL', settings.opentelemetry.metrics.protocol, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.protocol')),
190
- conf_value('OTEL_EXPORTER_OTLP_METRICS_TIMEOUT', settings.opentelemetry.metrics.timeout_millis, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.timeout_millis')),
191
- conf_value('OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE', settings.opentelemetry.metrics.temporality_preference, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.temporality_preference')),
192
- conf_value('OTEL_METRIC_EXPORT_INTERVAL', settings.opentelemetry.metrics.export_interval_millis, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.export_interval_millis')),
193
- conf_value('OTEL_METRIC_EXPORT_TIMEOUT', settings.opentelemetry.metrics.export_timeout_millis, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.export_timeout_millis')),
99
+ opentelemetry_enabled = !defined?(Datadog::OpenTelemetry::LOADED).nil?
100
+ list << conf_value(
101
+ 'tracing.opentelemetry.enabled',
102
+ opentelemetry_enabled,
103
+ opentelemetry_enabled ? Configuration::Option::Precedence::PROGRAMMATIC : Configuration::Option::Precedence::DEFAULT
194
104
  )
195
105
 
196
- # Whitelist of configuration options to send in additional payload object
197
- TARGET_OPTIONS.each do |option_path|
198
- split_option = option_path.split('.')
199
- list << conf_value(
200
- option_path,
201
- to_value(settings.dig(*split_option)),
202
- seq_id,
203
- get_telemetry_origin(settings, option_path)
204
- )
205
- end
206
-
106
+ # Track ssi configurations
207
107
  instrumentation_source = if Datadog.const_defined?(:SingleStepInstrument, false) &&
208
108
  Datadog::SingleStepInstrument.const_defined?(:LOADED, false) &&
209
109
  Datadog::SingleStepInstrument::LOADED
@@ -211,50 +111,81 @@ module Datadog
211
111
  else
212
112
  'manual'
213
113
  end
214
- # Track ssi configurations
114
+
215
115
  list.push(
216
- conf_value('instrumentation_source', instrumentation_source, seq_id, 'code'),
217
- conf_value('DD_INJECT_FORCE', Core::Environment::VariableHelpers.env_to_bool('DD_INJECT_FORCE', false), seq_id, 'env_var'),
218
- conf_value('DD_INJECTION_ENABLED', DATADOG_ENV['DD_INJECTION_ENABLED'] || '', seq_id, 'env_var'),
116
+ conf_value(
117
+ 'instrumentation_source',
118
+ instrumentation_source,
119
+ (instrumentation_source == 'ssi') ? Configuration::Option::Precedence::PROGRAMMATIC : Configuration::Option::Precedence::DEFAULT
120
+ ),
121
+ conf_value(
122
+ 'DD_INJECT_FORCE',
123
+ Core::Environment::VariableHelpers.env_to_bool('DD_INJECT_FORCE', false),
124
+ (DATADOG_ENV.key?('DD_INJECT_FORCE') ? Configuration::Option::Precedence::ENVIRONMENT : Configuration::Option::Precedence::DEFAULT)
125
+ ),
126
+ conf_value(
127
+ 'DD_INJECTION_ENABLED',
128
+ DATADOG_ENV['DD_INJECTION_ENABLED'] || '',
129
+ (DATADOG_ENV.key?('DD_INJECTION_ENABLED') ? Configuration::Option::Precedence::ENVIRONMENT : Configuration::Option::Precedence::DEFAULT)
130
+ ),
219
131
  )
220
132
 
221
- # Add some more custom additional payload values here
222
- if settings.logger.instance
133
+ # Extract writer options as separate configuration payloads.
134
+ resolve_option(settings, 'tracing.writer_options').values_per_precedence.each do |precedence, value|
223
135
  list << conf_value(
224
- 'logger.instance',
225
- settings.logger.instance.class.to_s,
226
- seq_id,
227
- get_telemetry_origin(settings, 'logger.instance')
136
+ 'tracing.writer_options.buffer_size',
137
+ # Steep: Value is always a hash for writer_options (ensured by o.type :hash)
138
+ to_telemetry_value(value[:buffer_size]), # steep:ignore NoMethod
139
+ precedence
228
140
  )
229
- end
230
- if settings.respond_to?('appsec')
231
141
  list << conf_value(
232
- 'appsec.enabled',
233
- settings.dig('appsec', 'enabled'),
234
- seq_id,
235
- get_telemetry_origin(settings, 'appsec.enabled')
142
+ 'tracing.writer_options.flush_interval',
143
+ # Steep: Value is always a hash for writer_options (ensured by o.type :hash)
144
+ to_telemetry_value(value[:flush_interval]), # steep:ignore NoMethod
145
+ precedence
236
146
  )
147
+ end
148
+
149
+ # OpenTelemetry configuration options (using environment variable names)
150
+ otel_exporter_headers_option = resolve_option(settings, 'opentelemetry.exporter.headers')
151
+ otel_exporter_headers_option.values_per_precedence.each do |precedence, value|
237
152
  list << conf_value(
238
- 'appsec.sca_enabled',
239
- settings.dig('appsec', 'sca_enabled'),
240
- seq_id,
241
- get_telemetry_origin(settings, 'appsec.sca_enabled')
153
+ option_telemetry_name(otel_exporter_headers_option),
154
+ # Steep: Value is always a hash for opentelemetry.exporter.headers (ensured by o.type :hash)
155
+ value&.map { |key, header_value| "#{key}=#{header_value}" }&.join(','), # steep:ignore NoMethod
156
+ precedence
242
157
  )
243
158
  end
244
- if settings.respond_to?('ci')
159
+
160
+ otel_metrics_headers_option = resolve_option(settings, 'opentelemetry.metrics.headers')
161
+ otel_metrics_headers_option.values_per_precedence.each do |precedence, value|
245
162
  list << conf_value(
246
- 'ci.enabled',
247
- settings.dig('ci', 'enabled'),
248
- seq_id,
249
- get_telemetry_origin(settings, 'ci.enabled')
163
+ option_telemetry_name(otel_metrics_headers_option),
164
+ # Steep: Value is always a hash for opentelemetry.metrics.headers (ensured by o.type :hash)
165
+ value&.map { |key, header_value| "#{key}=#{header_value}" }&.join(','), # steep:ignore NoMethod
166
+ precedence
250
167
  )
251
168
  end
252
169
 
253
- list.reject! { |entry| entry[:value].nil? }
170
+ # Add some more custom additional payload values here
171
+ if settings.logger.instance
172
+ logger_instance_option = resolve_option(settings, 'logger.instance')
173
+ logger_instance_option.values_per_precedence.each do |precedence, value|
174
+ list << conf_value(option_telemetry_name(logger_instance_option), value.nil? ? nil : value.class.to_s, precedence)
175
+ end
176
+ end
177
+
178
+ # Configuration options (regular + integration specific)
179
+ collect_all_configuration_options(settings).each do |option|
180
+ option.values_per_precedence.each do |precedence, value|
181
+ list << conf_value(option_telemetry_name(option), to_telemetry_value(value), precedence)
182
+ end
183
+ end
184
+
185
+ # We still want to report nil default and programmatic values as they are valid values
186
+ list.reject! { |entry| entry[:origin] != 'default' && entry[:origin] != 'code' && entry[:value].nil? }
254
187
  list
255
188
  end
256
- # standard:enable Metrics/AbcSize
257
- # standard:enable Metrics/MethodLength
258
189
 
259
190
  def agent_transport(agent_settings)
260
191
  adapter = agent_settings.adapter
@@ -266,19 +197,25 @@ module Datadog
266
197
  end
267
198
 
268
199
  # `origin`: Source of the configuration. One of :
269
- # - `fleet_stable_config`: configuration is set via the fleet automation Datadog UI
270
- # - `local_stable_config`: configuration set via a user-managed file
271
- # - `env_var`: configurations that are set through environment variables
272
- # - `jvm_prop`: JVM system properties passed on the command line
273
- # - `code`: configurations that are set through the customer application
274
- # - `dd_config`: set by the dd.yaml file or json
275
- # - `remote_config`: values that are set using remote config
276
- # - `app.config`: only applies to .NET
277
- # - `default`: set when the user has not set any configuration for the key (defaults to a value)
278
- # - `unknown`: set for cases where it is difficult/not possible to determine the source of a config.
279
- def conf_value(name, value, seq_id, origin)
280
- # @type var result: telemetry_configuration
200
+ # - 1: `default`: set when the user has not set any configuration for the key (defaults to a value)
201
+ # - 2:`local_stable_config`: configuration set via a user-managed file
202
+ # - 3:`env_var`: configurations that are set through environment variables
203
+ # - 4:`fleet_stable_config`: configuration is set via the fleet automation Datadog UI
204
+ # - 5:`code`: configurations that are set through the customer application
205
+ # - 6:`remote_config`: values that are set using remote config
206
+ # - 7:`unknown`: set for cases where it is difficult/not possible to determine the source of a config.
207
+ def conf_value(name, value, precedence)
208
+ build_conf_value(name, value, precedence.origin, precedence.numeric + 1)
209
+ end
210
+
211
+ def unknown_conf_value(name, value)
212
+ build_conf_value(name, value, 'unknown', Configuration::Option::Precedence::LIST.size + 1)
213
+ end
214
+
215
+ def build_conf_value(name, value, origin, seq_id)
216
+ # @type var result: Event::telemetry_configuration
281
217
  result = {name: name, value: value, origin: origin, seq_id: seq_id}
218
+
282
219
  if origin == 'fleet_stable_config'
283
220
  fleet_id = Core::Configuration::StableConfig.configuration.dig(:fleet, :id)
284
221
  result[:config_id] = fleet_id if fleet_id
@@ -286,16 +223,27 @@ module Datadog
286
223
  local_id = Core::Configuration::StableConfig.configuration.dig(:local, :id)
287
224
  result[:config_id] = local_id if local_id
288
225
  end
226
+
289
227
  result
290
228
  end
291
229
 
292
- def to_value(value)
230
+ def to_telemetry_value(value)
293
231
  # TODO: Add float if telemetry starts accepting it
294
232
  case value
295
233
  when Integer, String, true, false, nil
296
234
  value
235
+ when Hash
236
+ value.map { |key, entry_value| "#{key}:#{entry_value}" }.join(',')
237
+ when Array
238
+ value.join(',')
239
+ when Module
240
+ value.name.to_s
297
241
  else
298
- value.to_s
242
+ if implements_to_s?(value)
243
+ value.to_s
244
+ else
245
+ value.class.to_s
246
+ end
299
247
  end
300
248
  end
301
249
 
@@ -307,16 +255,50 @@ module Datadog
307
255
  }
308
256
  end
309
257
 
310
- def get_telemetry_origin(settings, config_path)
258
+ def collect_all_configuration_options(settings)
259
+ collect_configuration_options_from(settings).concat(collect_integration_configuration_options(settings.tracing))
260
+ end
261
+
262
+ def collect_integration_configuration_options(tracing_settings)
263
+ return [] unless tracing_settings.respond_to?(:instrumented_integrations)
264
+
265
+ tracing_settings.instrumented_integrations.each_value.with_object([]) do |integration, entries|
266
+ integration.configurations.each_value do |configuration|
267
+ entries.concat(collect_configuration_options_from(configuration))
268
+ end
269
+ end
270
+ end
271
+
272
+ def collect_configuration_options_from(settings)
273
+ settings.class.options.each_key.with_object([]) do |name, options|
274
+ option = settings.send(:resolve_option, name)
275
+ next if option.definition.skip_telemetry
276
+
277
+ if option.settings?
278
+ options.concat(collect_configuration_options_from(option.get))
279
+ else
280
+ options << option
281
+ end
282
+ end
283
+ end
284
+
285
+ def option_telemetry_name(option)
286
+ option.definition.env || option.name_with_settings_path
287
+ end
288
+
289
+ def resolve_option(settings, config_path)
311
290
  split_option = config_path.split('.')
312
291
  option_name = split_option.pop
313
- return 'unknown' if option_name.nil?
292
+ raise ArgumentError, "Invalid config path: #{config_path}" if option_name.nil?
314
293
 
315
- # @type var parent_setting: Core::Configuration::Options
316
- # @type var option: Core::Configuration::Option
317
294
  parent_setting = settings.dig(*split_option)
318
- option = parent_setting.send(:resolve_option, option_name.to_sym)
319
- option.precedence_set&.origin || 'unknown'
295
+ parent_setting.send(:resolve_option, option_name.to_sym)
296
+ end
297
+
298
+ def implements_to_s?(value)
299
+ value.method(:to_s).owner != Kernel
300
+ rescue NameError
301
+ false
320
302
  end
321
303
  end
322
304
  end
@@ -10,13 +10,6 @@ module Datadog
10
10
  #
11
11
  # @api private
12
12
  module Event
13
- extend Core::Utils::Forking
14
-
15
- # returns sequence that increments every time the configuration changes
16
- def self.configuration_sequence
17
- after_fork! { @sequence = Datadog::Core::Utils::Sequence.new(1) }
18
- @sequence ||= Datadog::Core::Utils::Sequence.new(1)
19
- end
20
13
  end
21
14
  end
22
15
  end
@@ -25,6 +18,7 @@ end
25
18
  require_relative 'event/base'
26
19
  require_relative 'event/app_client_configuration_change'
27
20
  require_relative 'event/app_closing'
21
+ require_relative 'event/app_extended_heartbeat'
28
22
  require_relative 'event/app_dependencies_loaded'
29
23
  require_relative 'event/app_endpoints_loaded'
30
24
  require_relative 'event/app_heartbeat'
@@ -7,6 +7,7 @@ module Datadog
7
7
  ENV_ENABLED = 'DD_INSTRUMENTATION_TELEMETRY_ENABLED'
8
8
  ENV_METRICS_ENABLED = 'DD_TELEMETRY_METRICS_ENABLED'
9
9
  ENV_HEARTBEAT_INTERVAL = 'DD_TELEMETRY_HEARTBEAT_INTERVAL'
10
+ ENV_EXTENDED_HEARTBEAT_INTERVAL = 'DD_TELEMETRY_EXTENDED_HEARTBEAT_INTERVAL'
10
11
  ENV_METRICS_AGGREGATION_INTERVAL = 'DD_TELEMETRY_METRICS_AGGREGATION_INTERVAL'
11
12
  ENV_DEPENDENCY_COLLECTION = 'DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED'
12
13
  ENV_INSTALL_ID = 'DD_INSTRUMENTATION_INSTALL_ID'
@@ -44,11 +44,16 @@ module Datadog
44
44
  'DD-Telemetry-Request-Type' => request_type,
45
45
  'DD-Client-Library-Language' => Core::Environment::Ext::LANG,
46
46
  'DD-Client-Library-Version' => Core::Environment::Identity.gem_datadog_version_semver2,
47
+ 'DD-Session-ID' => Core::Environment::Identity.id,
47
48
 
48
49
  # Enable debug mode for telemetry
49
50
  # 'DD-Telemetry-Debug-Enabled' => 'true',
50
51
  }.tap do |result|
51
52
  result['DD-API-KEY'] = api_key unless api_key.nil?
53
+ root = Core::Environment::Identity.root_runtime_id
54
+ result['DD-Root-Session-ID'] = root if root
55
+ parent = Core::Environment::Identity.parent_runtime_id
56
+ result['DD-Parent-Session-ID'] = parent if parent
52
57
  end
53
58
  end
54
59
  end