Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -2875,6 +2875,23 @@ In addition, all metrics include the following tags:
| `language` | Programming language traced. (e.g. `ruby`) |
| `service` | List of services this associated with this metric. |

### Feature Flags

The OpenFeature provider supports agentless configuration delivery by default. Configuration delivery does not start until the application adopts the provider.

Configure Feature Flags with environment variables or inside a `Datadog.configure` block:

| Setting | Environment variable | Type | Description | Default |
|---|---|---|---|---|
| `c.open_feature.feature_flags_enabled` | `DD_FEATURE_FLAGS_ENABLED` | `Boolean` | Enables or disables Feature Flags. Setting this to `false` takes precedence over the configured source. | `true` |
| `c.open_feature.configuration_source` | `DD_FEATURE_FLAGS_CONFIGURATION_SOURCE` | `String` | Selects `agentless`, `remote_config`, or `offline` configuration delivery. Values are case-insensitive and surrounding whitespace is ignored. Invalid values fail closed. | `agentless` |
| `c.open_feature.agentless_base_url` | `DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL` | `String` | Overrides the agentless configuration endpoint. The value can contain credentials and is excluded from configuration reporting. | `nil` |
| `c.open_feature.agentless_poll_interval_seconds` | `DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS` | `Integer` | Sets the agentless polling interval in seconds. Valid values are greater than `0` and at most `3600`. | `30` |
| `c.open_feature.agentless_request_timeout_seconds` | `DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS` | `Integer` | Sets the timeout for an agentless configuration request in seconds. Valid values are greater than `0` and at most `300`. | `5` |
| `c.open_feature.initialization_timeout_ms` | `DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS` | `Integer` | Sets how long provider initialization waits for the first configuration, in milliseconds. | `30000` |

`DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED` and `c.open_feature.enabled` are deprecated. When neither stable setting is explicitly configured, the legacy value `true` selects `remote_config` and `false` disables delivery, preserving existing behavior.

### Profiling

`datadog` can produce profiles that measure method-level application resource usage within production environments. These profiles can give insight into resources spent in Ruby code outside of existing trace instrumentation.
Expand Down
6 changes: 6 additions & 0 deletions lib/datadog/core/configuration/supported_configurations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,16 @@ module Configuration
"DD_ERROR_TRACKING_HANDLED_ERRORS",
"DD_ERROR_TRACKING_HANDLED_ERRORS_INCLUDE",
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED",
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS",
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_SPAN_ENRICHMENT_ENABLED",
"DD_EXPERIMENTAL_NATIVE_TRANSPORT_ENABLED",
"DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED",
"DD_EXTERNAL_ENV",
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE",
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL",
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS",
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS",
"DD_FEATURE_FLAGS_ENABLED",
"DD_FLAGGING_EVALUATION_COUNTS_ENABLED",
"DD_GIT_COMMIT_SHA",
"DD_GIT_REPOSITORY_URL",
Expand Down
164 changes: 164 additions & 0 deletions lib/datadog/open_feature/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@ module OpenFeature
module Configuration
# A settings class for the OpenFeature component.
module Settings
AGENTLESS_SOURCE = "agentless"
REMOTE_CONFIG_SOURCE = "remote_config"
OFFLINE_SOURCE = "offline"

DEFAULT_POLL_INTERVAL_SECONDS = 30
DEFAULT_REQUEST_TIMEOUT_SECONDS = 5
DEFAULT_INITIALIZATION_TIMEOUT_MS = 30_000

MAX_POLL_INTERVAL_SECONDS = 3600
MAX_REQUEST_TIMEOUT_SECONDS = 300
MAX_INITIALIZATION_TIMEOUT_MS = 2_147_483_647

def self.parse_integer(value, default:, setting:)
Integer(value, 10)
rescue ArgumentError
Datadog.logger.warn("#{setting} must be an integer; using the default")
default
end

def self.extended(base)
base = base.singleton_class unless base.is_a?(Class)
add_settings!(base)
Expand All @@ -15,10 +34,155 @@ def self.add_settings!(base)
# Steep does not update `self` for this `class_eval` block.
# @type self: Datadog::Core::Configuration::Base::_DslContext
settings :open_feature do
# Legacy switch. When the stable settings are unset, true keeps existing
# adopters on Remote Configuration and false disables the provider.
option :enabled do |o|
o.type :bool
o.env "DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED"
o.default false
o.after_set do |_, _, precedence|
next if precedence == Core::Configuration::Option::Precedence::DEFAULT

Datadog.logger.warn(
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED is deprecated; use " \
"DD_FEATURE_FLAGS_ENABLED and DD_FEATURE_FLAGS_CONFIGURATION_SOURCE instead"
)
end
end

option :feature_flags_enabled do |o|
o.type :bool
o.env "DD_FEATURE_FLAGS_ENABLED"
o.default true
Comment thread
pavlokhrebto marked this conversation as resolved.
end

option :configuration_source do |o|
o.type :string
o.env "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE"
o.env_parser do |value|
normalized = value.strip.downcase
normalized.empty? ? nil : normalized
end
o.default do
if !using_default?(:feature_flags_enabled)
Settings::AGENTLESS_SOURCE
elsif !using_default?(:enabled)
(get_option(:enabled) == true) ? Settings::REMOTE_CONFIG_SOURCE : Settings::OFFLINE_SOURCE
else
Settings::AGENTLESS_SOURCE
end
end
o.setter do |value|
normalized = value.to_s.strip.downcase
if normalized == Settings::AGENTLESS_SOURCE ||
normalized == Settings::REMOTE_CONFIG_SOURCE ||
normalized == Settings::OFFLINE_SOURCE
normalized
else
Datadog.logger.warn(
"Unsupported Feature Flags configuration source; no configuration will be delivered"
)
Settings::OFFLINE_SOURCE
end
end
o.helper(:configuration_source=) do |value|
if value.is_a?(String) && value.strip.empty?
unset_option(:configuration_source)
else
set_option(:configuration_source, value)
end
end
end

option :agentless_base_url do |o|
o.type :string, nilable: true
o.env "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL"
o.skip_telemetry true
o.env_parser do |value|
stripped = value.strip
stripped.empty? ? nil : stripped
end
o.setter do |value|
next unless value

stripped = value.to_s.strip
stripped.empty? ? nil : stripped
end
end

option :agentless_poll_interval_seconds do |o|
o.type :int
o.env "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS"
o.env_parser do |value|
Settings.parse_integer(
value,
default: Settings::DEFAULT_POLL_INTERVAL_SECONDS,
setting: "Feature Flags agentless poll interval",
)
end
o.default Settings::DEFAULT_POLL_INTERVAL_SECONDS
o.setter do |value|
integer_value = value.to_s.to_i
if integer_value > 0 && integer_value <= Settings::MAX_POLL_INTERVAL_SECONDS
integer_value
else
Datadog.logger.warn(
"Feature Flags agentless poll interval must be within (0, " \
"#{Settings::MAX_POLL_INTERVAL_SECONDS}]; using the default"
)
Settings::DEFAULT_POLL_INTERVAL_SECONDS
end
end
end

option :agentless_request_timeout_seconds do |o|
o.type :int
o.env "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS"
o.env_parser do |value|
Settings.parse_integer(
value,
default: Settings::DEFAULT_REQUEST_TIMEOUT_SECONDS,
setting: "Feature Flags agentless request timeout",
)
end
o.default Settings::DEFAULT_REQUEST_TIMEOUT_SECONDS
o.setter do |value|
integer_value = value.to_s.to_i
if integer_value > 0 && integer_value <= Settings::MAX_REQUEST_TIMEOUT_SECONDS
integer_value
else
Datadog.logger.warn(
"Feature Flags agentless request timeout must be within (0, " \
"#{Settings::MAX_REQUEST_TIMEOUT_SECONDS}]; using the default"
)
Settings::DEFAULT_REQUEST_TIMEOUT_SECONDS
end
end
end

option :initialization_timeout_ms do |o|
o.type :int
o.env "DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS"
Comment on lines +171 to +173

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply the initialization timeout during provider startup

When a user changes DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS, the value cannot affect initialization: the only production reference is this option declaration, Component never reads it, and Provider#init remains a no-op. Consequently the newly documented promise to wait this long for the first configuration is silently ignored for every source.

AGENTS.md reference: lib/datadog/open_feature/AGENTS.md:L180-L180

Useful? React with 👍 / 👎.

o.env_parser do |value|
Settings.parse_integer(
value,
default: Settings::DEFAULT_INITIALIZATION_TIMEOUT_MS,
setting: "Feature Flags provider initialization timeout",
)
end
o.default Settings::DEFAULT_INITIALIZATION_TIMEOUT_MS
o.setter do |value|
integer_value = value.to_s.to_i
if integer_value > 0 && integer_value <= Settings::MAX_INITIALIZATION_TIMEOUT_MS
integer_value
else
Datadog.logger.warn(
"Feature Flags provider initialization timeout must be within (0, " \
"#{Settings::MAX_INITIALIZATION_TIMEOUT_MS}]; using the default"
)
Settings::DEFAULT_INITIALIZATION_TIMEOUT_MS
end
end
end

# Opt-in gate for APM feature-flag span enrichment. When enabled,
Expand Down
26 changes: 26 additions & 0 deletions sig/datadog/core/configuration/settings.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,32 @@ module Datadog
interface _OpenFeature
def enabled: () -> bool

def enabled=: (bool) -> void

def feature_flags_enabled: () -> bool

def feature_flags_enabled=: (bool) -> void

def configuration_source: () -> String

def configuration_source=: (String) -> void

def agentless_base_url: () -> String?

def agentless_base_url=: (String?) -> void

def agentless_poll_interval_seconds: () -> Integer

def agentless_poll_interval_seconds=: (Integer) -> void

def agentless_request_timeout_seconds: () -> Integer

def agentless_request_timeout_seconds=: (Integer) -> void

def initialization_timeout_ms: () -> Integer

def initialization_timeout_ms=: (Integer) -> void

def span_enrichment_enabled: () -> bool

def span_enrichment_enabled=: (bool) -> void
Expand Down
14 changes: 14 additions & 0 deletions sig/datadog/open_feature/configuration.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@ module Datadog
module OpenFeature
module Configuration
module Settings
AGENTLESS_SOURCE: String
REMOTE_CONFIG_SOURCE: String
OFFLINE_SOURCE: String

DEFAULT_POLL_INTERVAL_SECONDS: Integer
DEFAULT_REQUEST_TIMEOUT_SECONDS: Integer
DEFAULT_INITIALIZATION_TIMEOUT_MS: Integer

MAX_POLL_INTERVAL_SECONDS: Integer
MAX_REQUEST_TIMEOUT_SECONDS: Integer
MAX_INITIALIZATION_TIMEOUT_MS: Integer

def self.parse_integer: (String value, default: Integer, setting: String) -> Integer

def self.extended: (::Class | ::Module base) -> void

def self.add_settings!: (untyped base) -> void
Expand Down
Loading
Loading