datadog 2.30.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 (137) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -1
  3. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +18 -0
  4. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +10 -0
  5. data/ext/datadog_profiling_native_extension/extconf.rb +2 -0
  6. data/ext/libdatadog_api/crashtracker.c +5 -8
  7. data/ext/libdatadog_api/datadog_ruby_common.c +18 -0
  8. data/ext/libdatadog_api/datadog_ruby_common.h +10 -0
  9. data/ext/libdatadog_api/di.c +79 -0
  10. data/ext/libdatadog_api/extconf.rb +2 -0
  11. data/ext/libdatadog_api/init.c +5 -2
  12. data/ext/libdatadog_extconf_helpers.rb +9 -1
  13. data/lib/datadog/ai_guard/component.rb +2 -0
  14. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +41 -3
  15. data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
  16. data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
  17. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +3 -1
  18. data/lib/datadog/ai_guard/evaluation/request.rb +14 -9
  19. data/lib/datadog/ai_guard/evaluation/result.rb +3 -1
  20. data/lib/datadog/ai_guard/evaluation.rb +36 -7
  21. data/lib/datadog/ai_guard.rb +26 -8
  22. data/lib/datadog/appsec/autoload.rb +1 -1
  23. data/lib/datadog/appsec/component.rb +11 -7
  24. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  25. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +6 -7
  26. data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
  27. data/lib/datadog/appsec/instrumentation/gateway.rb +0 -13
  28. data/lib/datadog/appsec/monitor/gateway/watcher.rb +2 -0
  29. data/lib/datadog/appsec/utils/http/media_type.rb +1 -2
  30. data/lib/datadog/appsec/utils/http/url_encoded.rb +2 -2
  31. data/lib/datadog/appsec.rb +5 -9
  32. data/lib/datadog/core/configuration/base.rb +17 -5
  33. data/lib/datadog/core/configuration/components.rb +21 -8
  34. data/lib/datadog/core/configuration/config_helper.rb +9 -0
  35. data/lib/datadog/core/configuration/option.rb +30 -5
  36. data/lib/datadog/core/configuration/option_definition.rb +38 -12
  37. data/lib/datadog/core/configuration/options.rb +40 -6
  38. data/lib/datadog/core/configuration/settings.rb +15 -0
  39. data/lib/datadog/core/configuration/supported_configurations.rb +1 -0
  40. data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
  41. data/lib/datadog/core/contrib/rails/utils.rb +7 -3
  42. data/lib/datadog/core/crashtracking/component.rb +3 -3
  43. data/lib/datadog/core/environment/container.rb +2 -2
  44. data/lib/datadog/core/environment/ext.rb +1 -0
  45. data/lib/datadog/core/environment/identity.rb +25 -3
  46. data/lib/datadog/core/environment/process.rb +12 -0
  47. data/lib/datadog/core/metrics/client.rb +5 -5
  48. data/lib/datadog/core/remote/component.rb +38 -21
  49. data/lib/datadog/core/runtime/metrics.rb +1 -1
  50. data/lib/datadog/core/telemetry/component.rb +3 -0
  51. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +2 -3
  52. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
  53. data/lib/datadog/core/telemetry/event/app_started.rb +151 -169
  54. data/lib/datadog/core/telemetry/event.rb +1 -7
  55. data/lib/datadog/core/telemetry/ext.rb +1 -0
  56. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -0
  57. data/lib/datadog/core/telemetry/worker.rb +20 -0
  58. data/lib/datadog/core/utils/only_once.rb +1 -1
  59. data/lib/datadog/core/utils/spawn_monkey_patch.rb +36 -0
  60. data/lib/datadog/core/workers/async.rb +1 -1
  61. data/lib/datadog/core.rb +0 -1
  62. data/lib/datadog/data_streams/pathway_context.rb +1 -1
  63. data/lib/datadog/di/boot.rb +2 -4
  64. data/lib/datadog/di/component.rb +4 -0
  65. data/lib/datadog/di/instrumenter.rb +10 -4
  66. data/lib/datadog/di/probe_notification_builder.rb +109 -1
  67. data/lib/datadog/di/serializer.rb +1 -1
  68. data/lib/datadog/di.rb +81 -0
  69. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  70. data/lib/datadog/open_feature/evaluation_engine.rb +1 -1
  71. data/lib/datadog/open_feature/exposures/reporter.rb +1 -1
  72. data/lib/datadog/open_feature/exposures/worker.rb +1 -1
  73. data/lib/datadog/open_feature/remote.rb +1 -1
  74. data/lib/datadog/open_feature/transport.rb +1 -1
  75. data/lib/datadog/opentelemetry/configuration/settings.rb +2 -0
  76. data/lib/datadog/profiling/collectors/code_provenance.rb +2 -3
  77. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
  78. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  79. data/lib/datadog/profiling/component.rb +11 -1
  80. data/lib/datadog/profiling/load_native_extension.rb +1 -1
  81. data/lib/datadog/profiling/profiler.rb +0 -4
  82. data/lib/datadog/profiling/scheduler.rb +2 -2
  83. data/lib/datadog/profiling/tasks/exec.rb +2 -2
  84. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  85. data/lib/datadog/profiling.rb +1 -2
  86. data/lib/datadog/single_step_instrument.rb +1 -1
  87. data/lib/datadog/tracing/buffer.rb +3 -3
  88. data/lib/datadog/tracing/component.rb +11 -0
  89. data/lib/datadog/tracing/configuration/settings.rb +2 -1
  90. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +2 -2
  91. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +20 -0
  92. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -1
  93. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  94. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  95. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  96. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  97. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  98. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  99. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  100. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  101. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  102. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  103. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  104. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
  105. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -1
  106. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
  107. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
  108. data/lib/datadog/tracing/contrib/configurable.rb +18 -3
  109. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -1
  110. data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -2
  111. data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -2
  112. data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -5
  113. data/lib/datadog/tracing/contrib/http/instrumentation.rb +1 -1
  114. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +1 -1
  115. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -1
  116. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  117. data/lib/datadog/tracing/contrib/rails/patcher.rb +0 -1
  118. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  119. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +2 -2
  120. data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
  121. data/lib/datadog/tracing/contrib/status_range_matcher.rb +4 -0
  122. data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
  123. data/lib/datadog/tracing/distributed/datadog.rb +4 -2
  124. data/lib/datadog/tracing/event.rb +1 -1
  125. data/lib/datadog/tracing/remote.rb +1 -1
  126. data/lib/datadog/tracing/sampling/ext.rb +2 -0
  127. data/lib/datadog/tracing/sampling/priority_sampler.rb +13 -0
  128. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  129. data/lib/datadog/tracing/sampling/rule_sampler.rb +54 -25
  130. data/lib/datadog/tracing/sampling/span/rule_parser.rb +1 -1
  131. data/lib/datadog/tracing/span_operation.rb +1 -1
  132. data/lib/datadog/tracing/trace_operation.rb +50 -6
  133. data/lib/datadog/tracing/tracer.rb +25 -0
  134. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  135. data/lib/datadog/tracing/transport/trace_formatter.rb +1 -1
  136. data/lib/datadog/version.rb +1 -1
  137. metadata +13 -7
@@ -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
@@ -27,6 +27,9 @@ module Datadog
27
27
  metrics_manager:,
28
28
  dependency_collection:,
29
29
  logger:,
30
+ settings:,
31
+ agent_settings:,
32
+ extended_heartbeat_interval_seconds:,
30
33
  enabled: true,
31
34
  shutdown_timeout: Workers::Polling::DEFAULT_SHUTDOWN_TIMEOUT,
32
35
  buffer_size: DEFAULT_BUFFER_MAX_SIZE
@@ -35,8 +38,11 @@ module Datadog
35
38
  @metrics_manager = metrics_manager
36
39
  @dependency_collection = dependency_collection
37
40
  @logger = logger
41
+ @settings = settings
42
+ @agent_settings = agent_settings
38
43
 
39
44
  @ticks_per_heartbeat = (heartbeat_interval_seconds / metrics_aggregation_interval_seconds).to_i
45
+ @ticks_per_extended_heartbeat = (extended_heartbeat_interval_seconds / metrics_aggregation_interval_seconds).to_i
40
46
  @current_ticks = 0
41
47
 
42
48
  # Workers::Polling settings
@@ -63,6 +69,7 @@ module Datadog
63
69
  self.buffer = buffer_klass.new(@buffer_size)
64
70
 
65
71
  @initial_event_once = Utils::OnlyOnceSuccessful.new(APP_STARTED_EVENT_RETRIES)
72
+ @extended_heartbeat_ticks = 0
66
73
  end
67
74
 
68
75
  attr_reader :logger
@@ -151,6 +158,13 @@ module Datadog
151
158
  end
152
159
 
153
160
  @current_ticks += 1
161
+ @extended_heartbeat_ticks += 1
162
+
163
+ if @extended_heartbeat_ticks >= @ticks_per_extended_heartbeat
164
+ @extended_heartbeat_ticks = 0
165
+ extended_heartbeat!
166
+ end
167
+
154
168
  return if @current_ticks < @ticks_per_heartbeat
155
169
 
156
170
  @current_ticks = 0
@@ -170,6 +184,12 @@ module Datadog
170
184
  send_event(Event::AppHeartbeat.new)
171
185
  end
172
186
 
187
+ def extended_heartbeat!
188
+ return if !enabled? || !sent_initial_event?
189
+
190
+ send_event(Event::AppExtendedHeartbeat.new(settings: @settings, agent_settings: @agent_settings))
191
+ end
192
+
173
193
  def started!
174
194
  return unless enabled?
175
195
 
@@ -3,7 +3,7 @@
3
3
  module Datadog
4
4
  module Core
5
5
  module Utils
6
- # Helper class to execute something only once such as not repeating warning logs, and instrumenting classes
6
+ # Helper class to execute something only once, such as not repeating warning logs and instrumenting classes
7
7
  # only once.
8
8
  #
9
9
  # Thread-safe when used correctly (e.g. be careful of races when lazily initializing instances of this class).
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ module Utils
6
+ module SpawnMonkeyPatch
7
+ # @param lineage_envs_provider [#call] returns a Hash of env vars to merge into the child process
8
+ def self.apply!(lineage_envs_provider:)
9
+ @lineage_envs_provider = lineage_envs_provider
10
+ ::Process.singleton_class.prepend(ProcessSpawnPatch)
11
+ true
12
+ end
13
+
14
+ module ProcessSpawnPatch
15
+ def spawn(*args, **opts)
16
+ args.replace(SpawnMonkeyPatch.inject_lineage_envs(args))
17
+ super
18
+ end
19
+ end
20
+
21
+ # Process.spawn(env?, cmd, ...): env is optional first arg (Hash). When present, merge
22
+ # runtime_ids into it; when absent, prepend full ENV + runtime_ids so the child inherits both.
23
+ def self.inject_lineage_envs(args)
24
+ runtime_ids = @lineage_envs_provider.call
25
+ env_provided = Hash === args.first
26
+
27
+ base_env = env_provided ? args.first : DATADOG_ENV.to_h
28
+ env = base_env.merge(runtime_ids)
29
+ rest = env_provided ? args.drop(1) : args
30
+
31
+ [env, *rest]
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -168,7 +168,7 @@ module Datadog
168
168
  rescue Exception => e
169
169
  @error = e
170
170
  Datadog.logger.debug(
171
- "Worker thread error. Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
171
+ "Worker thread error. Cause: #{e.class}: #{e} Location: #{Array(e.backtrace).first}"
172
172
  )
173
173
  raise
174
174
 
data/lib/datadog/core.rb CHANGED
@@ -22,7 +22,6 @@ module Datadog
22
22
  end
23
23
  end
24
24
 
25
- DATADOG_ENV = Core::Configuration::ConfigHelper.new
26
25
  extend Core::Extensions
27
26
 
28
27
  # Add shutdown hook:
@@ -46,7 +46,7 @@ module Datadog
46
46
  decode(binary_data)
47
47
  rescue ArgumentError => e
48
48
  # Invalid base64 encoding - may indicate version mismatch or corruption
49
- Datadog.logger.debug("Failed to decode DSM pathway context: #{e.message}")
49
+ Datadog.logger.debug { "Failed to decode DSM pathway context: #{e.class}: #{e}" }
50
50
  nil
51
51
  end
52
52
  end
@@ -18,8 +18,7 @@ require_relative 'serializer'
18
18
  require_relative 'transport/http'
19
19
  require_relative 'utils'
20
20
 
21
- # Steep: https://github.com/ruby/rbs/pull/2715
22
- if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore ArgumentTypeMismatch
21
+ if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED'])
23
22
 
24
23
  # For initial release of Dynamic Instrumentation, activate code tracking
25
24
  # only if DI is explicitly requested in the environment.
@@ -37,8 +36,7 @@ require_relative 'contrib'
37
36
 
38
37
  Datadog::DI::Contrib.load_now_or_later
39
38
 
40
- # Steep: https://github.com/ruby/rbs/pull/2715
41
- if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore ArgumentTypeMismatch
39
+ if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED'])
42
40
  if Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE']
43
41
  require_relative 'probe_file_loader'
44
42
  Datadog::DI::ProbeFileLoader.load_now_or_later
@@ -49,6 +49,10 @@ module Datadog
49
49
  logger.warn("di: cannot enable dynamic instrumentation: Ruby 2.6+ is required, but running on #{RUBY_VERSION}")
50
50
  return false
51
51
  end
52
+ unless DI.respond_to?(:exception_message)
53
+ logger.warn("di: cannot enable dynamic instrumentation: C extension is not available")
54
+ return false
55
+ end
52
56
  true
53
57
  end
54
58
  end
@@ -229,8 +229,7 @@ module Datadog
229
229
  # that location here.
230
230
  []
231
231
  end
232
- # Steep: https://github.com/ruby/rbs/pull/2745
233
- caller_locs = method_frame + caller_locations # steep:ignore ArgumentTypeMismatch
232
+ caller_locs = method_frame + caller_locations
234
233
  # TODO capture arguments at exit
235
234
 
236
235
  context = Context.new(locals: nil, target_self: self,
@@ -239,9 +238,16 @@ module Datadog
239
238
  caller_locations: caller_locs,
240
239
  return_value: rv, duration: duration, exception: exc,)
241
240
 
242
- responder.probe_executed_callback(context)
241
+ begin
242
+ responder.probe_executed_callback(context)
243
+
244
+ instrumenter.send(:check_and_disable_if_exceeded, probe, responder, di_start_time, di_duration)
245
+ rescue => di_exc
246
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
243
247
 
244
- instrumenter.send(:check_and_disable_if_exceeded, probe, responder, di_start_time, di_duration)
248
+ instrumenter.logger.debug { "di: unhandled exception in method probe: #{di_exc.class}: #{di_exc}" }
249
+ instrumenter.telemetry&.report(di_exc, description: "Unhandled exception in method probe")
250
+ end
245
251
 
246
252
  if exc
247
253
  raise exc