datadog 2.45.0 → 2.46.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -2
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +4 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +2 -0
- data/ext/datadog_profiling_native_extension/profiling.c +6 -0
- data/ext/libdatadog_api/di.c +30 -5
- data/ext/libdatadog_api/extconf.rb +2 -0
- data/ext/libdatadog_api/otel_thread_context.c +20 -14
- data/ext/libdatadog_api/process_discovery.c +15 -1
- data/ext/libdatadog_api/trace_exporter.c +105 -25
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/core/configuration/components.rb +29 -8
- data/lib/datadog/core/configuration/supported_configurations.rb +5 -2
- data/lib/datadog/core/process_discovery.rb +5 -1
- data/lib/datadog/core/remote/client/capabilities.rb +34 -29
- data/lib/datadog/core/remote/component.rb +28 -12
- data/lib/datadog/core/remote/dispatcher.rb +13 -3
- data/lib/datadog/di/boot.rb +1 -0
- data/lib/datadog/di/capture_expression_evaluator/result.rb +30 -0
- data/lib/datadog/di/capture_expression_evaluator.rb +39 -9
- data/lib/datadog/di/component.rb +38 -3
- data/lib/datadog/di/configuration.rb +53 -2
- data/lib/datadog/di/context.rb +7 -2
- data/lib/datadog/di/contrib/active_record.rb +8 -2
- data/lib/datadog/di/evaluation_failure.rb +28 -0
- data/lib/datadog/di/guardrails_telemetry/reason.rb +87 -0
- data/lib/datadog/di/guardrails_telemetry.rb +203 -0
- data/lib/datadog/di/instrumenter.rb +65 -15
- data/lib/datadog/di/logger.rb +30 -8
- data/lib/datadog/di/probe_manager.rb +48 -5
- data/lib/datadog/di/probe_notification_builder.rb +28 -26
- data/lib/datadog/di/probe_notifier_worker.rb +56 -6
- data/lib/datadog/di/serializer.rb +130 -54
- data/lib/datadog/di/snapshot_event.rb +38 -0
- data/lib/datadog/di/transport/http.rb +13 -3
- data/lib/datadog/di/transport/input.rb +32 -7
- data/lib/datadog/open_feature/activation.rb +268 -0
- data/lib/datadog/open_feature/component.rb +31 -15
- data/lib/datadog/open_feature/provider.rb +282 -5
- data/lib/datadog/open_feature.rb +31 -1
- data/lib/datadog/tracing/component.rb +14 -0
- data/lib/datadog/tracing/configuration/settings.rb +3 -1
- data/lib/datadog/tracing/otel_thread_context.rb +3 -5
- data/lib/datadog/tracing/transport/http/telemetry.rb +94 -0
- data/lib/datadog/tracing/transport/http/traces.rb +2 -0
- data/lib/datadog/tracing/transport/native/telemetry.rb +17 -0
- data/lib/datadog/tracing/transport/native.rb +16 -1
- data/lib/datadog/tracing/transport/telemetry.rb +62 -0
- data/lib/datadog/tracing/transport/traces.rb +82 -15
- data/lib/datadog/version.rb +1 -1
- metadata +16 -7
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "agentless/configuration_source"
|
|
4
|
+
require_relative "component"
|
|
5
|
+
require_relative "configuration/agentless_endpoint"
|
|
6
|
+
require_relative "configuration/source"
|
|
7
|
+
require_relative "remote"
|
|
8
|
+
|
|
9
|
+
module Datadog
|
|
10
|
+
module OpenFeature
|
|
11
|
+
# Owns eager Remote Configuration and lazy agentless delivery activation.
|
|
12
|
+
class Activation
|
|
13
|
+
attr_reader :component, :failure
|
|
14
|
+
|
|
15
|
+
def initialize(settings, agent_settings, remote, logger:, telemetry:)
|
|
16
|
+
@settings = settings
|
|
17
|
+
@agent_settings = agent_settings
|
|
18
|
+
@remote = remote
|
|
19
|
+
@logger = logger
|
|
20
|
+
@telemetry = telemetry
|
|
21
|
+
@component = nil
|
|
22
|
+
@configuration_source = nil
|
|
23
|
+
@delivery_source = nil
|
|
24
|
+
@remote_receivers = nil
|
|
25
|
+
@failure = nil
|
|
26
|
+
@providers = {}.compare_by_identity
|
|
27
|
+
@activated = false
|
|
28
|
+
@delivery_started = false
|
|
29
|
+
@shutdown = false
|
|
30
|
+
@mutex = Mutex.new
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def activate(provider)
|
|
34
|
+
@mutex.synchronize do
|
|
35
|
+
return if @shutdown
|
|
36
|
+
return unless open_feature_available?
|
|
37
|
+
|
|
38
|
+
@providers[provider] = true
|
|
39
|
+
return @component if @activated && @delivery_started
|
|
40
|
+
return if @activated
|
|
41
|
+
|
|
42
|
+
resolution = Configuration::Source.resolve(@settings, logger: @logger)
|
|
43
|
+
activate_delivery(resolution)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def startup!
|
|
48
|
+
start!
|
|
49
|
+
OpenFeature.reattach(self)
|
|
50
|
+
rescue => e
|
|
51
|
+
# Feature Flags is optional and must never interrupt library startup.
|
|
52
|
+
report_failure(e, "Feature Flags delivery failed to start")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def start!
|
|
56
|
+
@mutex.synchronize do
|
|
57
|
+
return if @shutdown || @activated
|
|
58
|
+
return unless open_feature_available?
|
|
59
|
+
|
|
60
|
+
resolution = Configuration::Source.resolve(@settings, logger: @logger)
|
|
61
|
+
return unless resolution.enabled? && resolution.source == Configuration::Source::REMOTE_CONFIG
|
|
62
|
+
|
|
63
|
+
activate_delivery(resolution)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def activated?
|
|
68
|
+
@mutex.synchronize { @activated }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def providers
|
|
72
|
+
@mutex.synchronize { @providers.keys }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def after_fork
|
|
76
|
+
inherited_providers, component = @mutex.synchronize do
|
|
77
|
+
@shutdown ? [[], nil] : [@providers.keys, @component]
|
|
78
|
+
end
|
|
79
|
+
inherited_providers.each { |provider| provider.send(:after_fork, component) }
|
|
80
|
+
|
|
81
|
+
configuration_source = @mutex.synchronize do
|
|
82
|
+
if @shutdown || !@delivery_started
|
|
83
|
+
nil
|
|
84
|
+
else
|
|
85
|
+
(@delivery_source == Configuration::Source::REMOTE_CONFIG) ? @remote : @configuration_source
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
configuration_source&.start
|
|
90
|
+
nil
|
|
91
|
+
rescue => e
|
|
92
|
+
# Feature Flags is optional and must never interrupt other post-fork handlers.
|
|
93
|
+
report_failure(e, "Feature Flags delivery failed to restart after fork")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def deactivate(provider)
|
|
97
|
+
configuration_source, component = @mutex.synchronize do
|
|
98
|
+
return if @shutdown
|
|
99
|
+
return unless @providers.delete(provider)
|
|
100
|
+
return unless @providers.empty?
|
|
101
|
+
if @delivery_source == Configuration::Source::REMOTE_CONFIG && @delivery_started
|
|
102
|
+
# Remote Configuration is process-scoped and may already hold configuration needed by the next provider.
|
|
103
|
+
[nil, nil]
|
|
104
|
+
else
|
|
105
|
+
previous_source = @configuration_source
|
|
106
|
+
previous_component = @component
|
|
107
|
+
@component = nil
|
|
108
|
+
@configuration_source = nil
|
|
109
|
+
@delivery_source = nil
|
|
110
|
+
@activated = false
|
|
111
|
+
@delivery_started = false
|
|
112
|
+
@failure = nil
|
|
113
|
+
[previous_source, previous_component]
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
configuration_source&.stop
|
|
118
|
+
component&.shutdown!
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def shutdown!
|
|
122
|
+
configuration_source, component = @mutex.synchronize do
|
|
123
|
+
return if @shutdown
|
|
124
|
+
|
|
125
|
+
@shutdown = true
|
|
126
|
+
[@configuration_source, @component]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
configuration_source&.stop
|
|
130
|
+
configuration_received = @mutex.synchronize do
|
|
131
|
+
!@providers.empty? && !!component&.configuration_received?
|
|
132
|
+
end
|
|
133
|
+
configuration_changed(Component::CONFIGURATION_LOST) if configuration_received
|
|
134
|
+
component&.shutdown!
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
def report_failure(error, description)
|
|
140
|
+
@logger.error("#{description}: #{error.class}: #{error.message}")
|
|
141
|
+
@telemetry.report(error, description: description)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def open_feature_available?
|
|
145
|
+
@settings.respond_to?(:open_feature) && @settings.respond_to?(:feature_flags)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def activate_delivery(resolution)
|
|
149
|
+
@activated = true
|
|
150
|
+
unless resolution.enabled?
|
|
151
|
+
@failure = "Feature Flags are disabled"
|
|
152
|
+
return
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
component = Component.build(
|
|
156
|
+
@settings,
|
|
157
|
+
@agent_settings,
|
|
158
|
+
resolution: resolution,
|
|
159
|
+
on_configuration_change: ->(event) { configuration_changed(event) },
|
|
160
|
+
logger: @logger,
|
|
161
|
+
telemetry: @telemetry,
|
|
162
|
+
)
|
|
163
|
+
unless component
|
|
164
|
+
@failure = "Feature Flags are unavailable on this runtime"
|
|
165
|
+
return
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
@component = component
|
|
169
|
+
@delivery_source = resolution.source
|
|
170
|
+
@delivery_started = start_delivery(resolution.source)
|
|
171
|
+
return component if @delivery_started
|
|
172
|
+
|
|
173
|
+
component.shutdown!
|
|
174
|
+
@component = nil
|
|
175
|
+
@delivery_source = nil
|
|
176
|
+
nil
|
|
177
|
+
rescue
|
|
178
|
+
configuration_source = @configuration_source
|
|
179
|
+
failed_component = @component
|
|
180
|
+
@component = nil
|
|
181
|
+
@configuration_source = nil
|
|
182
|
+
@delivery_source = nil
|
|
183
|
+
@activated = false
|
|
184
|
+
@delivery_started = false
|
|
185
|
+
@failure = nil
|
|
186
|
+
begin
|
|
187
|
+
configuration_source&.stop
|
|
188
|
+
ensure
|
|
189
|
+
failed_component&.shutdown!
|
|
190
|
+
end
|
|
191
|
+
raise
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def start_delivery(source)
|
|
195
|
+
case source
|
|
196
|
+
when Configuration::Source::AGENTLESS
|
|
197
|
+
start_agentless
|
|
198
|
+
when Configuration::Source::REMOTE_CONFIG
|
|
199
|
+
start_remote_configuration
|
|
200
|
+
else
|
|
201
|
+
@failure = "Feature Flags configuration source is unavailable"
|
|
202
|
+
false
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def start_agentless
|
|
207
|
+
component = @component
|
|
208
|
+
return false unless component
|
|
209
|
+
|
|
210
|
+
endpoint = Configuration::AgentlessEndpoint.build(
|
|
211
|
+
site: @settings.site,
|
|
212
|
+
environment: @settings.env,
|
|
213
|
+
base_url: @settings.feature_flags.agentless.base_url,
|
|
214
|
+
logger: @logger,
|
|
215
|
+
)
|
|
216
|
+
unless endpoint
|
|
217
|
+
@failure = "Feature Flags agentless endpoint is unavailable"
|
|
218
|
+
return false
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
configuration_source = Agentless::ConfigurationSource.build(
|
|
222
|
+
@settings,
|
|
223
|
+
endpoint: endpoint,
|
|
224
|
+
apply: ->(configuration) { component.reconfigure!(configuration) },
|
|
225
|
+
logger: @logger,
|
|
226
|
+
)
|
|
227
|
+
unless configuration_source
|
|
228
|
+
@failure = "Feature Flags agentless delivery could not start"
|
|
229
|
+
return false
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
@configuration_source = configuration_source
|
|
233
|
+
started = configuration_source.start
|
|
234
|
+
@failure = "Feature Flags agentless delivery could not start" unless started
|
|
235
|
+
started
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def start_remote_configuration
|
|
239
|
+
remote = @remote
|
|
240
|
+
unless remote
|
|
241
|
+
@failure = "Feature Flags Remote Configuration is unavailable"
|
|
242
|
+
@logger.warn(
|
|
243
|
+
"#{@failure}. To enable Remote Configuration, " \
|
|
244
|
+
"see https://docs.datadoghq.com/remote_configuration/."
|
|
245
|
+
)
|
|
246
|
+
return false
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
receivers = @remote_receivers ||= Remote.receivers(
|
|
250
|
+
@telemetry,
|
|
251
|
+
component_provider: -> { @mutex.synchronize { @component } },
|
|
252
|
+
)
|
|
253
|
+
remote.register(
|
|
254
|
+
capabilities: Remote.capabilities,
|
|
255
|
+
products: Remote.products,
|
|
256
|
+
receivers: receivers,
|
|
257
|
+
)
|
|
258
|
+
remote.start
|
|
259
|
+
true
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def configuration_changed(event)
|
|
263
|
+
providers = @mutex.synchronize { @providers.keys }
|
|
264
|
+
providers.each { |provider| provider.send(:configuration_changed, event) }
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "transport"
|
|
4
4
|
require_relative "evaluation_engine"
|
|
5
5
|
require_relative "../core/utils/time"
|
|
6
|
+
require_relative "configuration/source"
|
|
6
7
|
require_relative "exposures/buffer"
|
|
7
8
|
require_relative "exposures/worker"
|
|
8
9
|
require_relative "exposures/deduplicator"
|
|
@@ -20,19 +21,13 @@ module Datadog
|
|
|
20
21
|
CONFIGURATION_READY = :ready
|
|
21
22
|
CONFIGURATION_TIMEOUT = :timeout
|
|
22
23
|
CONFIGURATION_SHUTDOWN = :shutdown
|
|
24
|
+
CONFIGURATION_CHANGED = :changed
|
|
25
|
+
CONFIGURATION_LOST = :lost
|
|
23
26
|
|
|
24
27
|
attr_reader :engine, :flag_eval_metrics_hook, :flag_eval_evp_hook, :span_enrichment_hook
|
|
25
28
|
|
|
26
|
-
def self.build(settings, agent_settings, logger:, telemetry:)
|
|
27
|
-
return unless
|
|
28
|
-
|
|
29
|
-
unless settings.respond_to?(:remote) && settings.remote.enabled
|
|
30
|
-
message = "OpenFeature could not be enabled as Remote Configuration is currently disabled. " \
|
|
31
|
-
"To enable Remote Configuration, see https://docs.datadoghq.com/remote_configuration/."
|
|
32
|
-
|
|
33
|
-
logger.warn(message)
|
|
34
|
-
return
|
|
35
|
-
end
|
|
29
|
+
def self.build(settings, agent_settings, resolution:, on_configuration_change:, logger:, telemetry:)
|
|
30
|
+
return unless resolution.enabled?
|
|
36
31
|
|
|
37
32
|
unless ["ruby", "truffleruby"].include?(RUBY_ENGINE)
|
|
38
33
|
message = "OpenFeature could not be enabled as MRI is required, " \
|
|
@@ -51,10 +46,16 @@ module Datadog
|
|
|
51
46
|
return
|
|
52
47
|
end
|
|
53
48
|
|
|
54
|
-
new(
|
|
49
|
+
new(
|
|
50
|
+
settings,
|
|
51
|
+
agent_settings,
|
|
52
|
+
on_configuration_change: on_configuration_change,
|
|
53
|
+
logger: logger,
|
|
54
|
+
telemetry: telemetry,
|
|
55
|
+
)
|
|
55
56
|
end
|
|
56
57
|
|
|
57
|
-
def initialize(settings, agent_settings, logger:, telemetry:)
|
|
58
|
+
def initialize(settings, agent_settings, logger:, telemetry:, on_configuration_change: nil)
|
|
58
59
|
transport = Transport::HTTP.build(agent_settings: agent_settings, logger: logger)
|
|
59
60
|
@worker = Exposures::Worker.new(settings: settings, transport: transport, telemetry: telemetry, logger: logger)
|
|
60
61
|
|
|
@@ -65,6 +66,7 @@ module Datadog
|
|
|
65
66
|
@logger = logger
|
|
66
67
|
@settings = settings
|
|
67
68
|
@agent_settings = agent_settings
|
|
69
|
+
@on_configuration_change = on_configuration_change
|
|
68
70
|
@flag_eval_metrics_hook = create_flag_eval_metrics_hook
|
|
69
71
|
@flag_eval_evp_hook = create_flag_eval_evp_hook
|
|
70
72
|
@span_enrichment_hook = create_span_enrichment_hook
|
|
@@ -77,7 +79,7 @@ module Datadog
|
|
|
77
79
|
end
|
|
78
80
|
|
|
79
81
|
def reconfigure!(configuration)
|
|
80
|
-
@reconfiguration_mutex.synchronize do
|
|
82
|
+
event = @reconfiguration_mutex.synchronize do
|
|
81
83
|
return if configuration_shutdown?
|
|
82
84
|
|
|
83
85
|
@engine.reconfigure!(configuration)
|
|
@@ -85,19 +87,28 @@ module Datadog
|
|
|
85
87
|
@configuration_mutex.synchronize do
|
|
86
88
|
return if @configuration_shutdown
|
|
87
89
|
|
|
90
|
+
previously_received = @configuration_received
|
|
88
91
|
@configuration_received = !configuration.nil?
|
|
89
92
|
@configuration_condition.broadcast
|
|
93
|
+
|
|
94
|
+
if @configuration_received
|
|
95
|
+
previously_received ? CONFIGURATION_CHANGED : CONFIGURATION_READY
|
|
96
|
+
elsif previously_received
|
|
97
|
+
CONFIGURATION_LOST
|
|
98
|
+
end
|
|
90
99
|
end
|
|
91
100
|
end
|
|
101
|
+
|
|
102
|
+
@on_configuration_change&.call(event) if event
|
|
92
103
|
end
|
|
93
104
|
|
|
94
|
-
def wait_for_configuration
|
|
105
|
+
def wait_for_configuration(&cancelled)
|
|
95
106
|
timeout_seconds = @settings.feature_flags.initialization_timeout_ms / 1000.0
|
|
96
107
|
deadline = Core::Utils::Time.get_time + timeout_seconds
|
|
97
108
|
|
|
98
109
|
@configuration_mutex.synchronize do
|
|
99
110
|
loop do
|
|
100
|
-
return CONFIGURATION_SHUTDOWN if @configuration_shutdown
|
|
111
|
+
return CONFIGURATION_SHUTDOWN if @configuration_shutdown || cancelled&.call
|
|
101
112
|
return CONFIGURATION_READY if @configuration_received
|
|
102
113
|
|
|
103
114
|
remaining = deadline - Core::Utils::Time.get_time
|
|
@@ -108,6 +119,11 @@ module Datadog
|
|
|
108
119
|
end
|
|
109
120
|
end
|
|
110
121
|
|
|
122
|
+
def wake_configuration_waiters
|
|
123
|
+
@configuration_mutex.synchronize { @configuration_condition.broadcast }
|
|
124
|
+
nil
|
|
125
|
+
end
|
|
126
|
+
|
|
111
127
|
def configuration_received?
|
|
112
128
|
@configuration_mutex.synchronize { @configuration_received }
|
|
113
129
|
end
|