diff --git a/Steepfile b/Steepfile index 37f9abc0309..a4bbc892f0e 100644 --- a/Steepfile +++ b/Steepfile @@ -9,6 +9,7 @@ target :ddtrace do ignore 'lib/datadog/appsec.rb' ignore 'lib/datadog/appsec/component.rb' + ignore 'lib/datadog/appsec/configuration/settings.rb' ignore 'lib/datadog/appsec/contrib/auto_instrument.rb' ignore 'lib/datadog/appsec/contrib/integration.rb' ignore 'lib/datadog/appsec/contrib/rack/gateway/request.rb' diff --git a/lib/datadog/appsec/configuration/settings.rb b/lib/datadog/appsec/configuration/settings.rb index 16ec128b33e..79745a294d1 100644 --- a/lib/datadog/appsec/configuration/settings.rb +++ b/lib/datadog/appsec/configuration/settings.rb @@ -33,10 +33,9 @@ def self.add_settings!(base) base.class_eval do settings :appsec do option :enabled do |o| - o.default { env_to_bool('DD_APPSEC_ENABLED', DEFAULT_APPSEC_ENABLED) } - o.setter do |v| - v ? true : false - end + o.type :bool + o.env 'DD_APPSEC_ENABLED' + o.default DEFAULT_APPSEC_ENABLED end define_method(:instrument) do |integration_name| @@ -53,73 +52,71 @@ def self.add_settings!(base) end option :ruleset do |o| - o.default { ENV.fetch('DD_APPSEC_RULES', DEFAULT_APPSEC_RULESET) } + o.env 'DD_APPSEC_RULES' + o.default DEFAULT_APPSEC_RULESET end option :ip_denylist do |o| - o.default { [] } + o.type :array + o.default [] end option :user_id_denylist do |o| - o.default { [] } + o.type :array + o.default [] end option :waf_timeout do |o| - o.default { ENV.fetch('DD_APPSEC_WAF_TIMEOUT', DEFAULT_APPSEC_WAF_TIMEOUT) } # us + o.env 'DD_APPSEC_WAF_TIMEOUT' # us + o.default DEFAULT_APPSEC_WAF_TIMEOUT o.setter do |v| Datadog::Core::Utils::Duration.call(v.to_s, base: :us) end end option :waf_debug do |o| - o.default { env_to_bool('DD_APPSEC_WAF_DEBUG', DEFAULT_APPSEC_WAF_DEBUG) } - o.setter do |v| - v ? true : false - end + o.env 'DD_APPSEC_WAF_DEBUG' + o.default DEFAULT_APPSEC_WAF_DEBUG + o.type :bool end option :trace_rate_limit do |o| - o.default { env_to_int('DD_APPSEC_TRACE_RATE_LIMIT', DEFAULT_APPSEC_TRACE_RATE_LIMIT) } # trace/s + o.type :int + o.env 'DD_APPSEC_TRACE_RATE_LIMIT' # trace/s + o.default DEFAULT_APPSEC_TRACE_RATE_LIMIT end option :obfuscator_key_regex do |o| - o.default { ENV.fetch('DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP', DEFAULT_OBFUSCATOR_KEY_REGEX) } + o.type :string + o.env 'DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP' + o.default DEFAULT_OBFUSCATOR_KEY_REGEX end option :obfuscator_value_regex do |o| - o.default do - ENV.fetch( - 'DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP', - DEFAULT_OBFUSCATOR_VALUE_REGEX - ) - end + o.type :string + o.env 'DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP' + o.default DEFAULT_OBFUSCATOR_VALUE_REGEX end settings :track_user_events do option :enabled do |o| - o.default do - ENV.fetch( - 'DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING', - DEFAULT_APPSEC_AUTOMATED_TRACK_USER_EVENTS_ENABLED - ) - end - o.setter do |v| - if v - v.to_s != 'disabled' + o.env 'DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING' + o.default DEFAULT_APPSEC_AUTOMATED_TRACK_USER_EVENTS_ENABLED + o.setter do |value| + if value.is_a?(String) + value != 'disabled' else - false + !!value # rubocop:disable Style/DoubleNegation end end end option :mode do |o| - o.default do - ENV.fetch('DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING', DEFAULT_APPSEC_AUTOMATED_TRACK_USER_EVENTS_MODE) - end + o.env 'DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING' + o.default DEFAULT_APPSEC_AUTOMATED_TRACK_USER_EVENTS_MODE o.setter do |v| - string_value = v.to_s - if APPSEC_VALID_TRACK_USER_EVENTS_MODE.include?(string_value) - string_value + if APPSEC_VALID_TRACK_USER_EVENTS_MODE.include?(v.to_s) + v.to_s else Datadog.logger.warn( 'The appsec.track_user_events.mode value provided is not supported.' \ diff --git a/lib/datadog/ci/configuration/settings.rb b/lib/datadog/ci/configuration/settings.rb index 2a970baaed7..156309c81f4 100644 --- a/lib/datadog/ci/configuration/settings.rb +++ b/lib/datadog/ci/configuration/settings.rb @@ -16,7 +16,9 @@ def self.add_settings!(base) base.class_eval do settings :ci do option :enabled do |o| - o.default { env_to_bool(CI::Ext::Settings::ENV_MODE_ENABLED, false) } + o.type :bool + o.env CI::Ext::Settings::ENV_MODE_ENABLED + o.default false end # DEV: Alias to Datadog::Tracing::Contrib::Extensions::Configuration::Settings#instrument. @@ -34,12 +36,11 @@ def self.add_settings!(base) # TODO: Deprecate in the next major version, as `instrument` better describes this method's purpose alias_method :use, :instrument - option :trace_flush do |o| - o.default { nil } - end + option :trace_flush option :writer_options do |o| - o.default { {} } + o.type :hash + o.default({}) end end end diff --git a/lib/datadog/ci/contrib/cucumber/configuration/settings.rb b/lib/datadog/ci/contrib/cucumber/configuration/settings.rb index 662fd0af8c7..7ce9f9c9622 100644 --- a/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +++ b/lib/datadog/ci/contrib/cucumber/configuration/settings.rb @@ -12,15 +12,20 @@ module Configuration # TODO: mark as `@public_api` when GA class Settings < Datadog::Tracing::Contrib::Configuration::Settings option :enabled do |o| - o.default { env_to_bool(Ext::ENV_ENABLED, true) } + o.type :bool + o.env Ext::ENV_ENABLED + o.default true end option :service_name do |o| + o.type :string o.default { Datadog.configuration.service_without_fallback || Ext::SERVICE_NAME } end option :operation_name do |o| - o.default { ENV.fetch(Ext::ENV_OPERATION_NAME, Ext::OPERATION_NAME) } + o.type :string + o.env Ext::ENV_OPERATION_NAME + o.default Ext::OPERATION_NAME end end end diff --git a/lib/datadog/ci/contrib/minitest/configuration/settings.rb b/lib/datadog/ci/contrib/minitest/configuration/settings.rb index 52970f2799d..eb315690a7d 100644 --- a/lib/datadog/ci/contrib/minitest/configuration/settings.rb +++ b/lib/datadog/ci/contrib/minitest/configuration/settings.rb @@ -12,15 +12,20 @@ module Configuration # TODO: mark as `@public_api` when GA class Settings < Datadog::Tracing::Contrib::Configuration::Settings option :enabled do |o| - o.default { env_to_bool(Ext::ENV_ENABLED, true) } + o.type :bool + o.env Ext::ENV_ENABLED + o.default true end option :service_name do |o| + o.type :string o.default { Datadog.configuration.service_without_fallback || Ext::SERVICE_NAME } end option :operation_name do |o| - o.default { ENV.key?(Ext::ENV_OPERATION_NAME) ? ENV[Ext::ENV_OPERATION_NAME] : Ext::OPERATION_NAME } + o.type :string + o.env Ext::ENV_OPERATION_NAME + o.default Ext::OPERATION_NAME end end end diff --git a/lib/datadog/ci/contrib/rspec/configuration/settings.rb b/lib/datadog/ci/contrib/rspec/configuration/settings.rb index 11e88b8532c..5c9946b06fa 100644 --- a/lib/datadog/ci/contrib/rspec/configuration/settings.rb +++ b/lib/datadog/ci/contrib/rspec/configuration/settings.rb @@ -12,15 +12,20 @@ module Configuration # TODO: mark as `@public_api` when GA class Settings < Datadog::Tracing::Contrib::Configuration::Settings option :enabled do |o| - o.default { env_to_bool(Ext::ENV_ENABLED, true) } + o.type :bool + o.env Ext::ENV_ENABLED + o.default true end option :service_name do |o| + o.type :string o.default { Datadog.configuration.service_without_fallback || Ext::SERVICE_NAME } end option :operation_name do |o| - o.default { ENV.fetch(Ext::ENV_OPERATION_NAME, Ext::OPERATION_NAME) } + o.type :string + o.env Ext::ENV_OPERATION_NAME + o.default Ext::OPERATION_NAME end end end diff --git a/lib/datadog/core/configuration/base.rb b/lib/datadog/core/configuration/base.rb index 01371f27a67..e2201f945e4 100644 --- a/lib/datadog/core/configuration/base.rb +++ b/lib/datadog/core/configuration/base.rb @@ -34,8 +34,6 @@ def settings(name, &block) value.reset! if value.respond_to?(:reset!) value end - - o.type settings_class end end diff --git a/lib/datadog/core/configuration/option.rb b/lib/datadog/core/configuration/option.rb index f30288ead0f..cf90c3e20ee 100644 --- a/lib/datadog/core/configuration/option.rb +++ b/lib/datadog/core/configuration/option.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative '../utils/safe_dup' + module Datadog module Core module Configuration @@ -49,7 +51,7 @@ def set(value, precedence: Precedence::PROGRAMMATIC) end old_value = @value - (@value = context_exec(value, old_value, &definition.setter)).tap do |v| + (@value = context_exec(validate_type(value), old_value, &definition.setter)).tap do |v| @is_set = true @precedence_set = precedence context_exec(v, old_value, &definition.on_set) if definition.on_set @@ -62,7 +64,7 @@ def get elsif definition.delegate_to context_eval(&definition.delegate_to) else - set(default_value, precedence: Precedence::DEFAULT) + set_value_from_env_or_default end end @@ -84,7 +86,7 @@ def default_value if definition.default.instance_of?(Proc) context_eval(&definition.default) else - definition.experimental_default_proc || definition.default + definition.experimental_default_proc || Core::Utils::SafeDup.frozen_or_dup(definition.default) end end @@ -94,6 +96,93 @@ def default_precedence? private + def coerce_env_variable(value) + return context_exec(value, &@definition.env_parser) if @definition.env_parser + + case @definition.type + when :int + # DEV-2.0: Change to a more strict coercion method. Integer(value). + value.to_i + when :float + # DEV-2.0: Change to a more strict coercion method. Float(value). + value.to_f + when :array + values = if value.include?(',') + value.split(',') + else + value.split(' ') # rubocop:disable Style/RedundantArgument + end + + values.map! do |v| + v.gsub!(/\A[\s,]*|[\s,]*\Z/, '') + + v.empty? ? nil : v + end + + values.compact! + values + when :bool + string_value = value + string_value = string_value.downcase + string_value == 'true' || string_value == '1' # rubocop:disable Style/MultipleComparison + when :string, NilClass + value + else + raise ArgumentError, "The option #{@definition.name} is using an unknown type option `#{@definition.type}`" + end + end + + def validate_type(value) + raise_error = false + + valid_type = validate(@definition.type, value) + + unless valid_type + raise_error = if @definition.type_options[:nilable] + !value.is_a?(NilClass) + else + true + end + end + + if raise_error + error_msg = if @definition.type_options[:nilable] + "The option #{@definition.name} support this type `#{@definition.type}` "\ + "and `nil` but the value provided is #{value.class}" + else + "The option #{@definition.name} support this type `#{@definition.type}` "\ + "but the value provided is #{value.class}" + end + + raise ArgumentError, error_msg + end + + value + end + + def validate(type, value) + case type + when :string + value.is_a?(String) + when :int, :float + value.is_a?(Numeric) + when :array + value.is_a?(Array) + when :hash + value.is_a?(Hash) + when :bool + value.is_a?(TrueClass) || value.is_a?(FalseClass) + when :proc + value.is_a?(Proc) + when :symbol + value.is_a?(Symbol) + when NilClass + true + else + raise ArgumentError, "The option #{@definition.name} is using an unknown type option `#{@definition.type}`" + end + end + def context_exec(*args, &block) @context.instance_exec(*args, &block) end @@ -102,6 +191,29 @@ def context_eval(&block) @context.instance_eval(&block) end + def set_value_from_env_or_default + value = nil + precedence = nil + + if definition.env && ENV[definition.env] + value = coerce_env_variable(ENV[definition.env]) + precedence = Precedence::PROGRAMMATIC + end + + if value.nil? && definition.deprecated_env && ENV[definition.deprecated_env] + value = coerce_env_variable(ENV[definition.deprecated_env]) + precedence = Precedence::PROGRAMMATIC + + Datadog::Core.log_deprecation do + "#{definition.deprecated_env} environment variable is deprecated, use #{definition.env} instead." + end + end + + option_value = value.nil? ? default_value : value + + set(option_value, precedence: precedence || Precedence::DEFAULT) + end + # Used for testing attr_reader :precedence_set private :precedence_set diff --git a/lib/datadog/core/configuration/option_definition.rb b/lib/datadog/core/configuration/option_definition.rb index 674157a4d0b..2130330a2c4 100644 --- a/lib/datadog/core/configuration/option_definition.rb +++ b/lib/datadog/core/configuration/option_definition.rb @@ -16,17 +16,24 @@ class OptionDefinition # clear to the reader that they should not rely on it and that is subject to change. # Currently is only use internally. :experimental_default_proc, + :env, + :deprecated_env, + :env_parser, :delegate_to, :depends_on, :name, :on_set, :resetter, :setter, - :type + :type, + :type_options def initialize(name, meta = {}, &block) @default = meta[:default] @experimental_default_proc = meta[:experimental_default_proc] + @env = meta[:env] + @deprecated_env = meta[:deprecated_env] + @env_parser = meta[:env_parser] @delegate_to = meta[:delegate_to] @depends_on = meta[:depends_on] || [] @name = name.to_sym @@ -34,6 +41,7 @@ def initialize(name, meta = {}, &block) @resetter = meta[:resetter] @setter = meta[:setter] || block || IDENTITY @type = meta[:type] + @type_options = meta[:type_options] end # Creates a new Option, bound to the context provided. @@ -50,6 +58,9 @@ class InvalidOptionError < StandardError; end :helpers def initialize(name, options = {}) + @env = nil + @deprecated_env = nil + @env_parser = nil @default = nil @experimental_default_proc = nil @delegate_to = nil @@ -60,7 +71,7 @@ def initialize(name, options = {}) @resetter = nil @setter = OptionDefinition::IDENTITY @type = nil - + @type_options = {} # If options were supplied, apply them. apply_options!(options) @@ -74,11 +85,23 @@ def depends_on(*values) @depends_on = values.flatten end + def env(value) + @env = value + end + + def deprecated_env(value) + @deprecated_env = value + end + + def env_parser(&block) + @env_parser = block + end + def default(value = nil, &block) @default = block || value end - def experimental_default_proc(&block) + def experimental_default_proc(block) @experimental_default_proc = block end @@ -112,8 +135,11 @@ def setter(&block) @setter = block end - def type(value = nil) + def type(value, type_options = {}) @type = value + @type_options = type_options + + value end # For applying options for OptionDefinition @@ -121,14 +147,16 @@ def apply_options!(options = {}) return if options.nil? || options.empty? default(options[:default]) if options.key?(:default) - experimental_default_proc(&options[:experimental_default_proc]) if options.key?(:experimental_default_proc) + env(options[:env]) if options.key?(:env) + deprecated_env(options[:deprecated_env]) if options.key?(:deprecated_env) + env_parser(&options[:env_parser]) if options.key?(:env_parser) delegate_to(&options[:delegate_to]) if options.key?(:delegate_to) depends_on(*options[:depends_on]) if options.key?(:depends_on) lazy(options[:lazy]) if options.key?(:lazy) on_set(&options[:on_set]) if options.key?(:on_set) resetter(&options[:resetter]) if options.key?(:resetter) setter(&options[:setter]) if options.key?(:setter) - type(&options[:type]) if options.key?(:type) + type(options[:type]) if options.key?(:type) end def to_definition @@ -139,12 +167,16 @@ def meta { default: @default, experimental_default_proc: @experimental_default_proc, + env: @env, + deprecated_env: @deprecated_env, + env_parser: @env_parser, delegate_to: @delegate_to, depends_on: @depends_on, on_set: @on_set, resetter: @resetter, setter: @setter, - type: @type + type: @type, + type_options: @type_options } end diff --git a/lib/datadog/core/configuration/settings.rb b/lib/datadog/core/configuration/settings.rb index b1eb701a0f5..7f6c9938766 100644 --- a/lib/datadog/core/configuration/settings.rb +++ b/lib/datadog/core/configuration/settings.rb @@ -79,7 +79,8 @@ def initialize(*_) # @default `DD_API_KEY` environment variable, otherwise `nil` # @return [String,nil] option :api_key do |o| - o.default { ENV.fetch(Core::Environment::Ext::ENV_API_KEY, nil) } + o.type :string, nilable: true + o.env Core::Environment::Ext::ENV_API_KEY end # Datadog diagnostic settings. @@ -98,7 +99,9 @@ def initialize(*_) # @default `DD_TRACE_DEBUG` environment variable, otherwise `false` # @return [Boolean] option :debug do |o| - o.default { env_to_bool(Datadog::Core::Configuration::Ext::Diagnostics::ENV_DEBUG_ENABLED, false) } + o.env Datadog::Core::Configuration::Ext::Diagnostics::ENV_DEBUG_ENABLED + o.default false + o.type :bool o.on_set do |enabled| # Enable rich debug print statements. # We do not need to unnecessarily load 'pp' unless in debugging mode. @@ -115,7 +118,9 @@ def initialize(*_) # @default `DD_HEALTH_METRICS_ENABLED` environment variable, otherwise `false` # @return [Boolean] option :enabled do |o| - o.default { env_to_bool(Datadog::Core::Configuration::Ext::Diagnostics::ENV_HEALTH_METRICS_ENABLED, false) } + o.env Datadog::Core::Configuration::Ext::Diagnostics::ENV_HEALTH_METRICS_ENABLED + o.default false + o.type :bool end # {Datadog::Statsd} instance to collect health metrics. @@ -136,11 +141,12 @@ def initialize(*_) # If `nil`, defaults to logging startup logs when `ddtrace` detects that the application # is *not* running in a development environment. # - # @default `DD_TRACE_STARTUP_LOGS` environment variable, otherwise `nil` - # @return [Boolean,nil] + # @default `DD_TRACE_STARTUP_LOGS` environment variable, otherwise `false` + # @return [Boolean] option :enabled do |o| - # Defaults to nil as we want to know when the default value is being used - o.default { env_to_bool(Datadog::Core::Configuration::Ext::Diagnostics::ENV_STARTUP_LOGS_ENABLED, nil) } + o.env Datadog::Core::Configuration::Ext::Diagnostics::ENV_STARTUP_LOGS_ENABLED + o.default false + o.type :bool end end end @@ -154,7 +160,7 @@ def initialize(*_) o.setter { |v| v.to_s if v } # NOTE: env also gets set as a side effect of tags. See the WORKAROUND note in #initialize for details. - o.default { ENV.fetch(Core::Environment::Ext::ENV_ENVIRONMENT, nil) } + o.env Core::Environment::Ext::ENV_ENVIRONMENT end # Internal `Datadog.logger` configuration. @@ -189,7 +195,9 @@ def initialize(*_) # @default `DD_PROFILING_ENABLED` environment variable, otherwise `false` # @return [Boolean] option :enabled do |o| - o.default { env_to_bool(Profiling::Ext::ENV_ENABLED, false) } + o.env Profiling::Ext::ENV_ENABLED + o.default false + o.type :bool end # @public_api @@ -222,7 +230,9 @@ def initialize(*_) # # @default `DD_PROFILING_MAX_FRAMES` environment variable, otherwise 400 option :max_frames do |o| - o.default { env_to_int(Profiling::Ext::ENV_MAX_FRAMES, 400) } + o.type :int + o.env Profiling::Ext::ENV_MAX_FRAMES + o.default 400 end # @public_api @@ -234,7 +244,9 @@ def initialize(*_) # @default `DD_PROFILING_ENDPOINT_COLLECTION_ENABLED` environment variable, otherwise `true` # @return [Boolean] option :enabled do |o| - o.default { env_to_bool(Profiling::Ext::ENV_ENDPOINT_COLLECTION_ENABLED, true) } + o.env Profiling::Ext::ENV_ENDPOINT_COLLECTION_ENABLED + o.default true + o.type :bool end end end @@ -276,7 +288,9 @@ def initialize(*_) # # @default `DD_PROFILING_FORCE_ENABLE_LEGACY` environment variable, otherwise `false` option :force_enable_legacy_profiler do |o| - o.default { env_to_bool('DD_PROFILING_FORCE_ENABLE_LEGACY', false) } + o.env 'DD_PROFILING_FORCE_ENABLE_LEGACY' + o.default false + o.type :bool o.on_set do |value| if value Datadog.logger.warn( @@ -308,7 +322,9 @@ def initialize(*_) # # @default `DD_PROFILING_FORCE_ENABLE_GC` environment variable, otherwise `false` option :force_enable_gc_profiling do |o| - o.default { env_to_bool('DD_PROFILING_FORCE_ENABLE_GC', false) } + o.env 'DD_PROFILING_FORCE_ENABLE_GC' + o.type :bool + o.default false end # Can be used to enable/disable the Datadog::Profiling.allocation_count feature. @@ -329,7 +345,18 @@ def initialize(*_) # # @default `DD_PROFILING_SKIP_MYSQL2_CHECK` environment variable, otherwise `false` option :skip_mysql2_check do |o| - o.default { env_to_bool('DD_PROFILING_SKIP_MYSQL2_CHECK', false) } + o.type :bool + o.env 'DD_PROFILING_SKIP_MYSQL2_CHECK' + o.default false + end + + # Enables data collection for the timeline feature. This is still experimental and not recommended yet. + # + # @default `DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED` environment variable as a boolean, otherwise `false` + option :experimental_timeline_enabled do |o| + o.type :bool + o.env 'DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED' + o.default false end # The profiler gathers data by sending `SIGPROF` unix signals to Ruby application threads. @@ -352,14 +379,19 @@ def initialize(*_) # # @default `DD_PROFILING_NO_SIGNALS_WORKAROUND_ENABLED` environment variable as a boolean, otherwise `:auto` option :no_signals_workaround_enabled do |o| - o.default { env_to_bool('DD_PROFILING_NO_SIGNALS_WORKAROUND_ENABLED', :auto) } - end - - # Enables data collection for the timeline feature. This is still experimental and not recommended yet. - # - # @default `DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED` environment variable as a boolean, otherwise `false` - option :experimental_timeline_enabled do |o| - o.default { env_to_bool('DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED', false) } + o.env 'DD_PROFILING_NO_SIGNALS_WORKAROUND_ENABLED' + o.default :auto + o.setter do |value| + if ['true', true, '1', 'false', false, :auto].include?(value) + if value == :auto + value + else + ['true', true, '1'].include?(value) + end + else + value + end + end end end @@ -369,8 +401,9 @@ def initialize(*_) # # @default `DD_PROFILING_UPLOAD_TIMEOUT` environment variable, otherwise `30.0` option :timeout_seconds do |o| - o.setter { |value| value.nil? ? 30.0 : value.to_f } - o.default { env_to_float(Profiling::Ext::ENV_UPLOAD_TIMEOUT, 30.0) } + o.type :float + o.env Profiling::Ext::ENV_UPLOAD_TIMEOUT + o.default 30.0 end end end @@ -383,10 +416,12 @@ def initialize(*_) # @default `DD_RUNTIME_METRICS_ENABLED` environment variable, otherwise `false` # @return [Boolean] option :enabled do |o| - o.default { env_to_bool(Core::Runtime::Ext::Metrics::ENV_ENABLED, false) } + o.env Core::Runtime::Ext::Metrics::ENV_ENABLED + o.default false + o.type :bool end - option :opts, default: ->(_i) { {} } + option :opts, default: {}, type: :hash option :statsd end @@ -399,7 +434,8 @@ def initialize(*_) o.setter { |v| v.to_s if v } # NOTE: service also gets set as a side effect of tags. See the WORKAROUND note in #initialize for details. - o.default { ENV.fetch(Core::Environment::Ext::ENV_SERVICE, Core::Environment::Ext::FALLBACK_SERVICE_NAME) } + o.env Core::Environment::Ext::ENV_SERVICE + o.default Core::Environment::Ext::FALLBACK_SERVICE_NAME # There's a few cases where we don't want to use the fallback service name, so this helper allows us to get a # nil instead so that one can do @@ -421,7 +457,8 @@ def initialize(*_) # @default `DD_SITE` environment variable, otherwise `nil` which sends data to `app.datadoghq.com` # @return [String,nil] option :site do |o| - o.default { ENV.fetch(Core::Environment::Ext::ENV_SITE, nil) } + o.type :string, nilable: true + o.env Core::Environment::Ext::ENV_SITE end # Default tags @@ -431,36 +468,51 @@ def initialize(*_) # @default `DD_TAGS` environment variable (in the format `'tag1:value1,tag2:value2'`), otherwise `{}` # @return [Hash] option :tags do |o| - o.default do - tags = {} + o.type :hash, nilable: true + o.env Core::Environment::Ext::ENV_TAGS + o.env_parser do |env_value| + values = if env_value.include?(',') + env_value.split(',') + else + env_value.split(' ') # rubocop:disable Style/RedundantArgument + end + + values.map! do |v| + v.gsub!(/\A[\s,]*|[\s,]*\Z/, '') + + v.empty? ? nil : v + end - # Parse tags from environment - env_to_list(Core::Environment::Ext::ENV_TAGS, comma_separated_only: false).each do |tag| + values.compact! + values.each_with_object({}) do |tag, tags| key, value = tag.split(':', 2) tags[key] = value if value && !value.empty? end + end + o.setter do |new_value, old_value| + raw_tags = new_value || {} - # Override tags if defined - tags[Core::Environment::Ext::TAG_ENV] = env unless env.nil? - tags[Core::Environment::Ext::TAG_VERSION] = version unless version.nil? + env_value = env + version_value = version + service_name = service_without_fallback - tags - end + # Override tags if defined + raw_tags[Core::Environment::Ext::TAG_ENV] = env_value unless env_value.nil? + raw_tags[Core::Environment::Ext::TAG_VERSION] = version_value unless version_value.nil? - o.setter do |new_value, old_value| # Coerce keys to strings - string_tags = new_value.collect { |k, v| [k.to_s, v] }.to_h + string_tags = raw_tags.collect { |k, v| [k.to_s, v] }.to_h # Cross-populate tag values with other settings if env.nil? && string_tags.key?(Core::Environment::Ext::TAG_ENV) self.env = string_tags[Core::Environment::Ext::TAG_ENV] end - if version.nil? && string_tags.key?(Core::Environment::Ext::TAG_VERSION) + if version_value.nil? && string_tags.key?(Core::Environment::Ext::TAG_VERSION) self.version = string_tags[Core::Environment::Ext::TAG_VERSION] end - if service_without_fallback.nil? && string_tags.key?(Core::Environment::Ext::TAG_SERVICE) + if service_name.nil? && string_tags.key?(Core::Environment::Ext::TAG_SERVICE) self.service = string_tags[Core::Environment::Ext::TAG_SERVICE] end @@ -479,9 +531,9 @@ def initialize(*_) # @default `->{ Time.now }` # @return [Proc