datadog 2.43.0 → 2.45.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 +69 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +136 -132
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -5
- data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +0 -2
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +19 -20
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.h +5 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +7 -7
- data/ext/datadog_profiling_native_extension/collectors_stack.h +1 -1
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +146 -109
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +3 -2
- data/ext/datadog_profiling_native_extension/extconf.rb +6 -2
- data/ext/datadog_profiling_native_extension/heap_recorder.c +2 -2
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +122 -53
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +6 -2
- data/ext/datadog_profiling_native_extension/profiling.c +22 -23
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +3 -1
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +4 -0
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +0 -2
- data/ext/datadog_profiling_native_extension/stack_recorder.c +10 -28
- data/ext/datadog_profiling_native_extension/stack_recorder.h +0 -1
- data/ext/datadog_profiling_native_extension/time_helpers.c +2 -4
- data/ext/datadog_profiling_native_extension/time_helpers.h +13 -14
- data/ext/libdatadog_api/di.c +8 -3
- data/ext/libdatadog_api/extconf.rb +1 -1
- data/ext/libdatadog_api/feature_flags.c +8 -0
- data/ext/libdatadog_api/init.c +3 -1
- data/ext/libdatadog_api/otel_thread_context.c +38 -17
- data/ext/libdatadog_api/trace_exporter.c +77 -11
- data/ext/libdatadog_extconf_helpers.rb +14 -9
- data/lib/datadog/ai_guard/component.rb +17 -8
- data/lib/datadog/ai_guard/configuration/ext.rb +2 -0
- data/lib/datadog/ai_guard/configuration.rb +13 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +48 -55
- data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +1 -1
- data/lib/datadog/ai_guard/contrib/ruby_llm/message_adapter.rb +76 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +1 -0
- data/lib/datadog/ai_guard/contrib/ruby_llm/redaction_change_set.rb +135 -0
- data/lib/datadog/ai_guard/evaluation/client.rb +50 -0
- data/lib/datadog/ai_guard/evaluation/content_part.rb +12 -0
- data/lib/datadog/ai_guard/evaluation/message.rb +25 -7
- data/lib/datadog/ai_guard/evaluation/message_builder.rb +31 -0
- data/lib/datadog/ai_guard/evaluation/no_op_result.rb +6 -6
- data/lib/datadog/ai_guard/evaluation/outcome.rb +24 -0
- data/lib/datadog/ai_guard/evaluation/request.rb +4 -60
- data/lib/datadog/ai_guard/evaluation/response.rb +33 -0
- data/lib/datadog/ai_guard/evaluation/result.rb +11 -18
- data/lib/datadog/ai_guard/evaluation/tool_call.rb +15 -2
- data/lib/datadog/ai_guard/evaluation.rb +25 -11
- data/lib/datadog/ai_guard/ext.rb +1 -0
- data/lib/datadog/ai_guard/{api_client.rb → http_client.rb} +6 -4
- data/lib/datadog/ai_guard/metrics/telemetry.rb +43 -0
- data/lib/datadog/ai_guard/redaction/replacements.rb +97 -0
- data/lib/datadog/ai_guard/redaction/result.rb +29 -0
- data/lib/datadog/ai_guard/redaction.rb +80 -0
- data/lib/datadog/ai_guard.rb +58 -57
- data/lib/datadog/core/configuration/components.rb +9 -1
- data/lib/datadog/core/configuration/settings.rb +5 -2
- data/lib/datadog/core/configuration/supported_configurations.rb +8 -0
- data/lib/datadog/core/remote/client/capabilities.rb +10 -4
- data/lib/datadog/core/remote/component.rb +9 -2
- data/lib/datadog/core/remote/worker.rb +8 -0
- data/lib/datadog/di/instrumenter.rb +1 -1
- data/lib/datadog/di/probe_manager.rb +10 -5
- data/lib/datadog/di/proc_responder.rb +1 -1
- data/lib/datadog/open_feature/agentless/configuration_source.rb +246 -0
- data/lib/datadog/open_feature/agentless/parser.rb +43 -0
- data/lib/datadog/open_feature/agentless/response.rb +19 -0
- data/lib/datadog/open_feature/agentless/transport.rb +103 -0
- data/lib/datadog/open_feature/component.rb +57 -1
- data/lib/datadog/open_feature/configuration/agentless_endpoint.rb +90 -0
- data/lib/datadog/open_feature/configuration/source/resolution.rb +22 -0
- data/lib/datadog/open_feature/configuration/source.rb +43 -0
- data/lib/datadog/open_feature/configuration.rb +118 -0
- data/lib/datadog/open_feature/native_evaluator.rb +1 -14
- data/lib/datadog/open_feature/remote.rb +5 -5
- data/lib/datadog/opentelemetry/signal_configuration.rb +13 -9
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +7 -2
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +5 -7
- data/lib/datadog/profiling/component.rb +2 -3
- data/lib/datadog/profiling/exporter.rb +7 -5
- data/lib/datadog/symbol_database/extractor.rb +84 -102
- data/lib/datadog/tracing/component.rb +3 -1
- data/lib/datadog/tracing/configuration/ext.rb +1 -0
- data/lib/datadog/tracing/configuration/settings.rb +9 -2
- data/lib/datadog/tracing/context.rb +24 -2
- data/lib/datadog/tracing/context_provider.rb +9 -5
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +32 -5
- data/lib/datadog/tracing/contrib/utils/quantization/http.rb +6 -3
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -0
- data/lib/datadog/tracing/metadata/ext.rb +3 -0
- data/lib/datadog/tracing/otel_thread_context.rb +76 -11
- data/lib/datadog/tracing/tracer.rb +15 -2
- data/lib/datadog/tracing/transport/native.rb +23 -22
- data/lib/datadog/tracing/transport/trace_formatter.rb +13 -3
- data/lib/datadog/tracing/transport/traces.rb +1 -1
- data/lib/datadog/version.rb +1 -1
- metadata +23 -7
- data/lib/datadog/ai_guard/evaluation/content_builder.rb +0 -31
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "transport"
|
|
4
4
|
require_relative "evaluation_engine"
|
|
5
|
+
require_relative "../core/utils/time"
|
|
5
6
|
require_relative "exposures/buffer"
|
|
6
7
|
require_relative "exposures/worker"
|
|
7
8
|
require_relative "exposures/deduplicator"
|
|
@@ -16,6 +17,10 @@ module Datadog
|
|
|
16
17
|
module OpenFeature
|
|
17
18
|
# This class is the entry point for the OpenFeature component
|
|
18
19
|
class Component
|
|
20
|
+
CONFIGURATION_READY = :ready
|
|
21
|
+
CONFIGURATION_TIMEOUT = :timeout
|
|
22
|
+
CONFIGURATION_SHUTDOWN = :shutdown
|
|
23
|
+
|
|
19
24
|
attr_reader :engine, :flag_eval_metrics_hook, :flag_eval_evp_hook, :span_enrichment_hook
|
|
20
25
|
|
|
21
26
|
def self.build(settings, agent_settings, logger:, telemetry:)
|
|
@@ -29,7 +34,7 @@ module Datadog
|
|
|
29
34
|
return
|
|
30
35
|
end
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
unless ["ruby", "truffleruby"].include?(RUBY_ENGINE)
|
|
33
38
|
message = "OpenFeature could not be enabled as MRI is required, " \
|
|
34
39
|
"but running on #{RUBY_ENGINE.inspect}"
|
|
35
40
|
|
|
@@ -63,9 +68,56 @@ module Datadog
|
|
|
63
68
|
@flag_eval_metrics_hook = create_flag_eval_metrics_hook
|
|
64
69
|
@flag_eval_evp_hook = create_flag_eval_evp_hook
|
|
65
70
|
@span_enrichment_hook = create_span_enrichment_hook
|
|
71
|
+
|
|
72
|
+
@configuration_mutex = Mutex.new
|
|
73
|
+
@reconfiguration_mutex = Mutex.new
|
|
74
|
+
@configuration_condition = ConditionVariable.new
|
|
75
|
+
@configuration_received = false
|
|
76
|
+
@configuration_shutdown = false
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def reconfigure!(configuration)
|
|
80
|
+
@reconfiguration_mutex.synchronize do
|
|
81
|
+
return if configuration_shutdown?
|
|
82
|
+
|
|
83
|
+
@engine.reconfigure!(configuration)
|
|
84
|
+
|
|
85
|
+
@configuration_mutex.synchronize do
|
|
86
|
+
return if @configuration_shutdown
|
|
87
|
+
|
|
88
|
+
@configuration_received = !configuration.nil?
|
|
89
|
+
@configuration_condition.broadcast
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def wait_for_configuration
|
|
95
|
+
timeout_seconds = @settings.feature_flags.initialization_timeout_ms / 1000.0
|
|
96
|
+
deadline = Core::Utils::Time.get_time + timeout_seconds
|
|
97
|
+
|
|
98
|
+
@configuration_mutex.synchronize do
|
|
99
|
+
loop do
|
|
100
|
+
return CONFIGURATION_SHUTDOWN if @configuration_shutdown
|
|
101
|
+
return CONFIGURATION_READY if @configuration_received
|
|
102
|
+
|
|
103
|
+
remaining = deadline - Core::Utils::Time.get_time
|
|
104
|
+
return CONFIGURATION_TIMEOUT unless remaining.positive?
|
|
105
|
+
|
|
106
|
+
@configuration_condition.wait(@configuration_mutex, remaining)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def configuration_received?
|
|
112
|
+
@configuration_mutex.synchronize { @configuration_received }
|
|
66
113
|
end
|
|
67
114
|
|
|
68
115
|
def shutdown!
|
|
116
|
+
@configuration_mutex.synchronize do
|
|
117
|
+
@configuration_shutdown = true
|
|
118
|
+
@configuration_condition.broadcast
|
|
119
|
+
end
|
|
120
|
+
|
|
69
121
|
@worker.graceful_shutdown
|
|
70
122
|
@flag_eval_evp_writer&.stop
|
|
71
123
|
# Symmetric teardown: drop any accumulated span-enrichment state and
|
|
@@ -75,6 +127,10 @@ module Datadog
|
|
|
75
127
|
|
|
76
128
|
private
|
|
77
129
|
|
|
130
|
+
def configuration_shutdown?
|
|
131
|
+
@configuration_mutex.synchronize { @configuration_shutdown }
|
|
132
|
+
end
|
|
133
|
+
|
|
78
134
|
def create_flag_eval_metrics_hook
|
|
79
135
|
return unless Hooks::FlagEvalMetricsHook.available?
|
|
80
136
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module OpenFeature
|
|
7
|
+
module Configuration
|
|
8
|
+
# Validated endpoint for agentless Feature Flags configuration delivery.
|
|
9
|
+
class AgentlessEndpoint
|
|
10
|
+
DEFAULT_SITE = "datadoghq.com"
|
|
11
|
+
AGENTLESS_SUBDOMAIN = "ufc-server.ff-cdn"
|
|
12
|
+
CONFIGURATION_PATH = "/api/v2/feature-flagging/config/rules-based/server"
|
|
13
|
+
INVALID_SITE_CHARACTERS = /[\s\/?#@:]/
|
|
14
|
+
INTERNAL_WHITESPACE = /\s/
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
def build(site:, environment:, base_url: nil, logger: Datadog.logger)
|
|
18
|
+
if base_url
|
|
19
|
+
build_custom(base_url, logger)
|
|
20
|
+
else
|
|
21
|
+
build_managed(site, environment, logger)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def build_custom(base_url, logger)
|
|
28
|
+
unless base_url.ascii_only?
|
|
29
|
+
logger.warn("Feature Flags agentless base URL is invalid; agentless delivery is disabled")
|
|
30
|
+
return
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if base_url.match?(INTERNAL_WHITESPACE)
|
|
34
|
+
logger.warn("Feature Flags agentless base URL contains whitespace; agentless delivery is disabled")
|
|
35
|
+
return
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
uri = URI.parse(base_url)
|
|
39
|
+
unless uri.is_a?(URI::HTTP) && uri.absolute? && uri.host
|
|
40
|
+
logger.warn("Feature Flags agentless base URL must be an absolute HTTP or HTTPS URL; agentless delivery is disabled")
|
|
41
|
+
return
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
uri.path = CONFIGURATION_PATH if uri.path.to_s.empty? || uri.path == "/"
|
|
45
|
+
new(uri, managed: false)
|
|
46
|
+
rescue URI::InvalidURIError
|
|
47
|
+
logger.warn("Feature Flags agentless base URL is invalid; agentless delivery is disabled")
|
|
48
|
+
nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def build_managed(site, environment, logger)
|
|
52
|
+
string_site = site.to_s
|
|
53
|
+
unless string_site.ascii_only?
|
|
54
|
+
logger.warn("Feature Flags site is invalid; agentless delivery is disabled")
|
|
55
|
+
return
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
normalized_site = string_site.strip.downcase
|
|
59
|
+
normalized_site = DEFAULT_SITE if normalized_site.empty?
|
|
60
|
+
if normalized_site.match?(INVALID_SITE_CHARACTERS)
|
|
61
|
+
logger.warn("Feature Flags site is invalid; agentless delivery is disabled")
|
|
62
|
+
return
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
uri = URI::HTTPS.build(
|
|
66
|
+
host: "#{AGENTLESS_SUBDOMAIN}.#{normalized_site}",
|
|
67
|
+
path: CONFIGURATION_PATH,
|
|
68
|
+
query: environment.nil? ? nil : URI.encode_www_form(dd_env: environment),
|
|
69
|
+
)
|
|
70
|
+
new(uri, managed: true)
|
|
71
|
+
rescue URI::InvalidComponentError
|
|
72
|
+
logger.warn("Feature Flags site is invalid; agentless delivery is disabled")
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
attr_reader :uri
|
|
78
|
+
|
|
79
|
+
def initialize(uri, managed:)
|
|
80
|
+
@uri = uri
|
|
81
|
+
@managed = managed
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def managed?
|
|
85
|
+
@managed
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module OpenFeature
|
|
5
|
+
module Configuration
|
|
6
|
+
module Source
|
|
7
|
+
class Resolution
|
|
8
|
+
attr_reader :source
|
|
9
|
+
|
|
10
|
+
def initialize(source, enabled:)
|
|
11
|
+
@source = source
|
|
12
|
+
@enabled = enabled
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def enabled?
|
|
16
|
+
@enabled
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "source/resolution"
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module OpenFeature
|
|
7
|
+
module Configuration
|
|
8
|
+
module Source
|
|
9
|
+
AGENTLESS = "agentless"
|
|
10
|
+
REMOTE_CONFIG = "remote_config"
|
|
11
|
+
OFFLINE = "offline"
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def resolve(settings, logger: Datadog.logger)
|
|
15
|
+
feature_flags = settings.feature_flags
|
|
16
|
+
source = resolve_source(feature_flags, legacy: settings.open_feature, logger: logger)
|
|
17
|
+
enabled = feature_flags.enabled && source != OFFLINE
|
|
18
|
+
|
|
19
|
+
Resolution.new(source, enabled: enabled)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def resolve_source(settings, legacy:, logger:)
|
|
25
|
+
configured_source = settings.configuration_source
|
|
26
|
+
if !settings.using_default?(:configuration_source) && !configured_source.empty?
|
|
27
|
+
return configured_source if configured_source == AGENTLESS ||
|
|
28
|
+
configured_source == REMOTE_CONFIG ||
|
|
29
|
+
configured_source == OFFLINE
|
|
30
|
+
|
|
31
|
+
logger.warn("Unsupported Feature Flags configuration source; Feature Flags are disabled")
|
|
32
|
+
OFFLINE
|
|
33
|
+
elsif !legacy.using_default?(:enabled)
|
|
34
|
+
legacy.enabled ? REMOTE_CONFIG : OFFLINE
|
|
35
|
+
else
|
|
36
|
+
AGENTLESS
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -5,6 +5,25 @@ module Datadog
|
|
|
5
5
|
module Configuration
|
|
6
6
|
# A settings class for the OpenFeature component.
|
|
7
7
|
module Settings
|
|
8
|
+
DEFAULT_POLL_INTERVAL_SECONDS = 30
|
|
9
|
+
DEFAULT_REQUEST_TIMEOUT_SECONDS = 5
|
|
10
|
+
DEFAULT_INITIALIZATION_TIMEOUT_MS = 30_000
|
|
11
|
+
|
|
12
|
+
MAX_REQUEST_TIMEOUT_SECONDS = 300
|
|
13
|
+
MAX_INITIALIZATION_TIMEOUT_MS = 2_147_483_647
|
|
14
|
+
|
|
15
|
+
def self.configure_integer_option(option, environment_variable:, default:, setting_name:)
|
|
16
|
+
option.type :int
|
|
17
|
+
option.env environment_variable
|
|
18
|
+
option.env_parser do |value|
|
|
19
|
+
Integer(value, 10)
|
|
20
|
+
rescue ArgumentError
|
|
21
|
+
Datadog.logger.warn("#{setting_name} must be an integer; using the default")
|
|
22
|
+
default
|
|
23
|
+
end
|
|
24
|
+
option.default default
|
|
25
|
+
end
|
|
26
|
+
|
|
8
27
|
def self.extended(base)
|
|
9
28
|
base = base.singleton_class unless base.is_a?(Class)
|
|
10
29
|
add_settings!(base)
|
|
@@ -15,10 +34,19 @@ module Datadog
|
|
|
15
34
|
# Steep does not update `self` for this `class_eval` block.
|
|
16
35
|
# @type self: Datadog::Core::Configuration::Base::_DslContext
|
|
17
36
|
settings :open_feature do
|
|
37
|
+
# Deprecated switch retained for existing Remote Configuration adopters.
|
|
18
38
|
option :enabled do |o|
|
|
19
39
|
o.type :bool
|
|
20
40
|
o.env "DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED"
|
|
21
41
|
o.default false
|
|
42
|
+
o.after_set do |_, _, precedence|
|
|
43
|
+
next if precedence == Core::Configuration::Option::Precedence::DEFAULT
|
|
44
|
+
|
|
45
|
+
Datadog.logger.warn(
|
|
46
|
+
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED is deprecated; use " \
|
|
47
|
+
"DD_FEATURE_FLAGS_ENABLED and DD_FEATURE_FLAGS_CONFIGURATION_SOURCE instead"
|
|
48
|
+
)
|
|
49
|
+
end
|
|
22
50
|
end
|
|
23
51
|
|
|
24
52
|
# Opt-in gate for APM feature-flag span enrichment. When enabled,
|
|
@@ -42,6 +70,96 @@ module Datadog
|
|
|
42
70
|
o.default true
|
|
43
71
|
end
|
|
44
72
|
end
|
|
73
|
+
|
|
74
|
+
settings :feature_flags do
|
|
75
|
+
option :enabled do |o|
|
|
76
|
+
o.type :bool
|
|
77
|
+
o.env "DD_FEATURE_FLAGS_ENABLED"
|
|
78
|
+
o.default true
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
option :configuration_source do |o|
|
|
82
|
+
o.type :string
|
|
83
|
+
o.env "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE"
|
|
84
|
+
o.default "agentless"
|
|
85
|
+
o.setter do |value|
|
|
86
|
+
value.to_s.strip.downcase
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
settings :agentless do
|
|
91
|
+
option :base_url do |o|
|
|
92
|
+
o.type :string, nilable: true
|
|
93
|
+
o.env "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL"
|
|
94
|
+
o.skip_telemetry true
|
|
95
|
+
o.setter do |value|
|
|
96
|
+
next unless value
|
|
97
|
+
|
|
98
|
+
stripped_value = value.to_s.strip
|
|
99
|
+
stripped_value.empty? ? nil : stripped_value
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
option :poll_interval_seconds do |o|
|
|
104
|
+
Settings.configure_integer_option(
|
|
105
|
+
o,
|
|
106
|
+
environment_variable: "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS",
|
|
107
|
+
default: Settings::DEFAULT_POLL_INTERVAL_SECONDS,
|
|
108
|
+
setting_name: "Feature Flags agentless poll interval",
|
|
109
|
+
)
|
|
110
|
+
o.setter do |value|
|
|
111
|
+
if value.is_a?(Integer) && value > 0
|
|
112
|
+
value
|
|
113
|
+
else
|
|
114
|
+
Datadog.logger.warn(
|
|
115
|
+
"Feature Flags agentless poll interval must be positive; using the default"
|
|
116
|
+
)
|
|
117
|
+
Settings::DEFAULT_POLL_INTERVAL_SECONDS
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
option :request_timeout_seconds do |o|
|
|
123
|
+
Settings.configure_integer_option(
|
|
124
|
+
o,
|
|
125
|
+
environment_variable: "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS",
|
|
126
|
+
default: Settings::DEFAULT_REQUEST_TIMEOUT_SECONDS,
|
|
127
|
+
setting_name: "Feature Flags agentless request timeout",
|
|
128
|
+
)
|
|
129
|
+
o.setter do |value|
|
|
130
|
+
if value.is_a?(Integer) && value > 0 && value <= Settings::MAX_REQUEST_TIMEOUT_SECONDS
|
|
131
|
+
value
|
|
132
|
+
else
|
|
133
|
+
Datadog.logger.warn(
|
|
134
|
+
"Feature Flags agentless request timeout must be within (0, " \
|
|
135
|
+
"#{Settings::MAX_REQUEST_TIMEOUT_SECONDS}]; using the default"
|
|
136
|
+
)
|
|
137
|
+
Settings::DEFAULT_REQUEST_TIMEOUT_SECONDS
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
option :initialization_timeout_ms do |o|
|
|
144
|
+
Settings.configure_integer_option(
|
|
145
|
+
o,
|
|
146
|
+
environment_variable: "DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS",
|
|
147
|
+
default: Settings::DEFAULT_INITIALIZATION_TIMEOUT_MS,
|
|
148
|
+
setting_name: "Feature Flags provider initialization timeout",
|
|
149
|
+
)
|
|
150
|
+
o.setter do |value|
|
|
151
|
+
if value.is_a?(Integer) && value > 0 && value <= Settings::MAX_INITIALIZATION_TIMEOUT_MS
|
|
152
|
+
value
|
|
153
|
+
else
|
|
154
|
+
Datadog.logger.warn(
|
|
155
|
+
"Feature Flags provider initialization timeout must be within (0, " \
|
|
156
|
+
"#{Settings::MAX_INITIALIZATION_TIMEOUT_MS}]; using the default"
|
|
157
|
+
)
|
|
158
|
+
Settings::DEFAULT_INITIALIZATION_TIMEOUT_MS
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
45
163
|
end
|
|
46
164
|
end
|
|
47
165
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "json"
|
|
4
3
|
require_relative "../core/feature_flags"
|
|
5
4
|
require_relative "ext"
|
|
6
5
|
require_relative "resolution_details"
|
|
@@ -16,7 +15,7 @@ module Datadog
|
|
|
16
15
|
# in the format expected by `libdatadog` without any modifications
|
|
17
16
|
def initialize(configuration)
|
|
18
17
|
@configuration = Core::FeatureFlags::Configuration.new(configuration)
|
|
19
|
-
@observe_full_evaluation_data =
|
|
18
|
+
@observe_full_evaluation_data = @configuration.observe_full_evaluation_data
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
attr_reader :observe_full_evaluation_data
|
|
@@ -42,18 +41,6 @@ module Datadog
|
|
|
42
41
|
|
|
43
42
|
private
|
|
44
43
|
|
|
45
|
-
# Parse observe_full_evaluation_data from the top level of the UFC JSON (a sibling
|
|
46
|
-
# of `environment`). Absent, null, or wrong-typed values return false.
|
|
47
|
-
def parse_observe_full_evaluation_data(configuration)
|
|
48
|
-
return false unless configuration.is_a?(String) && !configuration.empty?
|
|
49
|
-
|
|
50
|
-
parsed = JSON.parse(configuration)
|
|
51
|
-
parsed.is_a?(Hash) && parsed["observeFullEvaluationData"] == true
|
|
52
|
-
rescue
|
|
53
|
-
# This secondary policy parse must not reject configuration accepted by the native evaluator.
|
|
54
|
-
false
|
|
55
|
-
end
|
|
56
|
-
|
|
57
44
|
def build_resolution_details(result, default_value)
|
|
58
45
|
ResolutionDetails.new(
|
|
59
46
|
value: result.variant.nil? ? default_value : result.value,
|
|
@@ -19,11 +19,11 @@ module Datadog
|
|
|
19
19
|
FFE_PRODUCTS
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def receivers(telemetry)
|
|
22
|
+
def receivers(telemetry, component_provider:)
|
|
23
23
|
matcher = Core::Remote::Dispatcher::Matcher::Product.new(FFE_PRODUCTS)
|
|
24
24
|
receiver = Core::Remote::Dispatcher::Receiver.new(matcher) do |repository, changes|
|
|
25
|
-
|
|
26
|
-
next unless
|
|
25
|
+
component = component_provider.call
|
|
26
|
+
next unless component
|
|
27
27
|
|
|
28
28
|
changes.each do |change|
|
|
29
29
|
content = repository[change.path]
|
|
@@ -38,7 +38,7 @@ module Datadog
|
|
|
38
38
|
when :insert, :update
|
|
39
39
|
begin
|
|
40
40
|
# @type var content: Core::Remote::Configuration::Content
|
|
41
|
-
|
|
41
|
+
component.reconfigure!(read_content(content))
|
|
42
42
|
content.applied
|
|
43
43
|
rescue EvaluationEngine::ReconfigurationError => e
|
|
44
44
|
content.errored("Error applying OpenFeature configuration: #{e.class}: #{e.message}")
|
|
@@ -46,7 +46,7 @@ module Datadog
|
|
|
46
46
|
when :delete
|
|
47
47
|
# NOTE: For now, we treat deletion as clearing the configuration
|
|
48
48
|
# In a multi-config scenario, we might track configs per path
|
|
49
|
-
|
|
49
|
+
component.reconfigure!(nil)
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
end
|
|
@@ -12,23 +12,24 @@ module Datadog
|
|
|
12
12
|
def create_resource
|
|
13
13
|
resource_attributes = {}
|
|
14
14
|
|
|
15
|
-
@settings.tags&.each do |key, value|
|
|
15
|
+
@settings.tags&.each do |key, value|
|
|
16
16
|
otel_key = case key
|
|
17
17
|
when "service" then "service.name"
|
|
18
|
-
when "env"
|
|
18
|
+
when "env", "deployment.environment.name", "deployment.environment" then next
|
|
19
19
|
when "version" then "service.version"
|
|
20
20
|
else key
|
|
21
21
|
end
|
|
22
22
|
resource_attributes[otel_key] = value
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
resource_attributes["service.name"] = @settings.service_without_fallback || resource_attributes["service.name"] || Datadog::Core::Environment::Ext::FALLBACK_SERVICE_NAME
|
|
26
|
-
|
|
27
|
-
resource_attributes["
|
|
25
|
+
resource_attributes["service.name"] = @settings.service_without_fallback || resource_attributes["service.name"] || Datadog::Core::Environment::Ext::FALLBACK_SERVICE_NAME
|
|
26
|
+
environment = @settings.env || @settings.tags["env"] || @settings.tags["deployment.environment.name"] || @settings.tags["deployment.environment"]
|
|
27
|
+
resource_attributes["deployment.environment.name"] = environment if environment
|
|
28
|
+
resource_attributes["service.version"] = @settings.version if @settings.version
|
|
28
29
|
|
|
29
|
-
hostname = Datadog::Core::Environment::Socket.resolved_hostname(@settings)
|
|
30
|
+
hostname = Datadog::Core::Environment::Socket.resolved_hostname(@settings)
|
|
30
31
|
if hostname
|
|
31
|
-
if hostname == @settings.hostname
|
|
32
|
+
if hostname == @settings.hostname
|
|
32
33
|
resource_attributes["host.name"] = hostname
|
|
33
34
|
elsif !resource_attributes.key?("host.name")
|
|
34
35
|
resource_attributes["host.name"] = hostname
|
|
@@ -41,9 +42,12 @@ module Datadog
|
|
|
41
42
|
# Returns the signal-specific option value when explicitly set,
|
|
42
43
|
# otherwise falls back to the general OTLP exporter config or computed_default.
|
|
43
44
|
def config_or_exporter_fallback(signal:, option_name:, computed_default: nil)
|
|
44
|
-
signal_settings =
|
|
45
|
+
signal_settings = case signal
|
|
46
|
+
when :logs then @settings.opentelemetry.logs
|
|
47
|
+
when :metrics then @settings.opentelemetry.metrics
|
|
48
|
+
end
|
|
45
49
|
if signal_settings.using_default?(option_name)
|
|
46
|
-
@settings.opentelemetry.exporter.public_send(option_name) || computed_default
|
|
50
|
+
@settings.opentelemetry.exporter.public_send(option_name) || computed_default
|
|
47
51
|
else
|
|
48
52
|
signal_settings.public_send(option_name)
|
|
49
53
|
end
|
|
@@ -51,7 +51,7 @@ module Datadog
|
|
|
51
51
|
# profiler overhead!
|
|
52
52
|
dynamic_sampling_rate_enabled: true,
|
|
53
53
|
skip_idle_samples_for_testing: false,
|
|
54
|
-
idle_sampling_helper: IdleSamplingHelper.new
|
|
54
|
+
idle_sampling_helper: IdleSamplingHelper.new
|
|
55
55
|
)
|
|
56
56
|
unless dynamic_sampling_rate_enabled
|
|
57
57
|
Datadog.logger.warn(
|
|
@@ -98,7 +98,7 @@ module Datadog
|
|
|
98
98
|
|
|
99
99
|
Datadog.logger.debug { "Starting thread for: #{self}" }
|
|
100
100
|
|
|
101
|
-
@idle_sampling_helper.start
|
|
101
|
+
@idle_sampling_helper.start(self)
|
|
102
102
|
|
|
103
103
|
@worker_thread = Thread.new do
|
|
104
104
|
Thread.current.name = self.class.name
|
|
@@ -147,6 +147,11 @@ module Datadog
|
|
|
147
147
|
end
|
|
148
148
|
end
|
|
149
149
|
|
|
150
|
+
# @rbs return: Datadog::Profiling::StackRecorder
|
|
151
|
+
def prepare_serialize
|
|
152
|
+
self.class._native_prepare_serialize(self)
|
|
153
|
+
end
|
|
154
|
+
|
|
150
155
|
#: () -> true
|
|
151
156
|
def reset_after_fork
|
|
152
157
|
self.class._native_reset_after_fork(self)
|
|
@@ -10,7 +10,6 @@ module Datadog
|
|
|
10
10
|
class IdleSamplingHelper
|
|
11
11
|
# @rbs @worker_thread: untyped
|
|
12
12
|
# @rbs @start_stop_mutex: ::Thread::Mutex
|
|
13
|
-
# @rbs @thread_context_collector: Datadog::Profiling::Collectors::ThreadContext?
|
|
14
13
|
|
|
15
14
|
private
|
|
16
15
|
|
|
@@ -18,15 +17,14 @@ module Datadog
|
|
|
18
17
|
|
|
19
18
|
public
|
|
20
19
|
|
|
21
|
-
#: (
|
|
22
|
-
def initialize
|
|
20
|
+
#: () -> void
|
|
21
|
+
def initialize
|
|
23
22
|
@worker_thread = nil
|
|
24
23
|
@start_stop_mutex = Mutex.new
|
|
25
|
-
@thread_context_collector = thread_context_collector
|
|
26
24
|
end
|
|
27
25
|
|
|
28
|
-
#: () ->
|
|
29
|
-
def start
|
|
26
|
+
#: (CpuAndWallTimeWorker cpu_and_wall_time_worker) -> true?
|
|
27
|
+
def start(cpu_and_wall_time_worker)
|
|
30
28
|
@start_stop_mutex.synchronize do
|
|
31
29
|
return if @worker_thread&.alive?
|
|
32
30
|
|
|
@@ -39,7 +37,7 @@ module Datadog
|
|
|
39
37
|
@worker_thread = Thread.new do
|
|
40
38
|
Thread.current.name = self.class.name
|
|
41
39
|
|
|
42
|
-
self.class._native_idle_sampling_loop(self,
|
|
40
|
+
self.class._native_idle_sampling_loop(self, cpu_and_wall_time_worker)
|
|
43
41
|
|
|
44
42
|
Datadog.logger.debug("IdleSamplingHelper thread stopping cleanly")
|
|
45
43
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
@@ -73,7 +73,7 @@ module Datadog
|
|
|
73
73
|
heap_sample_every: heap_sample_every,
|
|
74
74
|
}.freeze
|
|
75
75
|
|
|
76
|
-
exporter = build_profiler_exporter(settings,
|
|
76
|
+
exporter = build_profiler_exporter(settings, worker, internal_metadata: internal_metadata)
|
|
77
77
|
transport = build_profiler_transport(settings, agent_settings)
|
|
78
78
|
scheduler = Profiling::Scheduler.new(exporter: exporter, transport: transport, interval: upload_period_seconds)
|
|
79
79
|
profiler = Profiling::Profiler.new(worker: worker, scheduler: scheduler)
|
|
@@ -109,13 +109,12 @@ module Datadog
|
|
|
109
109
|
)
|
|
110
110
|
end
|
|
111
111
|
|
|
112
|
-
private_class_method def self.build_profiler_exporter(settings,
|
|
112
|
+
private_class_method def self.build_profiler_exporter(settings, worker, internal_metadata:)
|
|
113
113
|
info_collector = Profiling::Collectors::Info.new(settings)
|
|
114
114
|
code_provenance_collector =
|
|
115
115
|
(Profiling::Collectors::CodeProvenance.new if settings.profiling.advanced.code_provenance_enabled)
|
|
116
116
|
|
|
117
117
|
Profiling::Exporter.new(
|
|
118
|
-
pprof_recorder: recorder,
|
|
119
118
|
worker: worker,
|
|
120
119
|
info_collector: info_collector,
|
|
121
120
|
code_provenance_collector: code_provenance_collector,
|
|
@@ -20,7 +20,6 @@ module Datadog
|
|
|
20
20
|
|
|
21
21
|
private
|
|
22
22
|
|
|
23
|
-
attr_reader :pprof_recorder #: Datadog::Profiling::StackRecorder
|
|
24
23
|
# The code provenance collector acts both as collector and as a recorder
|
|
25
24
|
attr_reader :code_provenance_collector #: Datadog::Profiling::Collectors::CodeProvenance?
|
|
26
25
|
attr_reader :minimum_duration_seconds #: ::Integer
|
|
@@ -33,7 +32,6 @@ module Datadog
|
|
|
33
32
|
|
|
34
33
|
public
|
|
35
34
|
|
|
36
|
-
# @rbs pprof_recorder: Datadog::Profiling::StackRecorder
|
|
37
35
|
# @rbs worker: Datadog::Profiling::Collectors::CpuAndWallTimeWorker
|
|
38
36
|
# @rbs info_collector: Datadog::Profiling::Collectors::Info
|
|
39
37
|
# @rbs code_provenance_collector: Datadog::Profiling::Collectors::CodeProvenance?
|
|
@@ -43,7 +41,6 @@ module Datadog
|
|
|
43
41
|
# @rbs sequence_tracker: singleton(Datadog::Profiling::SequenceTracker)
|
|
44
42
|
# @rbs return: void
|
|
45
43
|
def initialize(
|
|
46
|
-
pprof_recorder:,
|
|
47
44
|
worker:,
|
|
48
45
|
info_collector:,
|
|
49
46
|
code_provenance_collector:,
|
|
@@ -52,7 +49,6 @@ module Datadog
|
|
|
52
49
|
time_provider: Time,
|
|
53
50
|
sequence_tracker: Datadog::Profiling::SequenceTracker
|
|
54
51
|
)
|
|
55
|
-
@pprof_recorder = pprof_recorder
|
|
56
52
|
@worker = worker
|
|
57
53
|
@code_provenance_collector = code_provenance_collector
|
|
58
54
|
@minimum_duration_seconds = minimum_duration_seconds
|
|
@@ -68,6 +64,7 @@ module Datadog
|
|
|
68
64
|
|
|
69
65
|
#: () -> Datadog::Profiling::Flush?
|
|
70
66
|
def flush
|
|
67
|
+
pprof_recorder = @worker.prepare_serialize
|
|
71
68
|
worker_stats = @worker.stats_and_reset_not_thread_safe
|
|
72
69
|
serialization_result = pprof_recorder.serialize
|
|
73
70
|
return if serialization_result.nil?
|
|
@@ -111,7 +108,7 @@ module Datadog
|
|
|
111
108
|
worker_stats: worker_stats,
|
|
112
109
|
profile_stats: profile_stats,
|
|
113
110
|
recorder_stats: pprof_recorder.stats,
|
|
114
|
-
gc:
|
|
111
|
+
gc: gc_stats,
|
|
115
112
|
}
|
|
116
113
|
),
|
|
117
114
|
info_json: info_json,
|
|
@@ -131,6 +128,11 @@ module Datadog
|
|
|
131
128
|
|
|
132
129
|
private
|
|
133
130
|
|
|
131
|
+
# @rbs return: Hash[Symbol, Integer | bool]
|
|
132
|
+
def gc_stats
|
|
133
|
+
GC.stat.merge(auto_compact: GC.respond_to?(:auto_compact) && GC.auto_compact)
|
|
134
|
+
end
|
|
135
|
+
|
|
134
136
|
#: (::Time, ::Time) -> bool
|
|
135
137
|
def duration_below_threshold?(start, finish)
|
|
136
138
|
(finish - start) < minimum_duration_seconds
|