# typed: false require 'logger' require_relative 'base' require_relative '../environment/ext' require_relative '../runtime/ext' require_relative '../telemetry/ext' require_relative '../../profiling/ext' require_relative '../../tracing/configuration/ext' module Datadog module Core module Configuration # Global configuration settings for the trace library. # @public_api # rubocop:disable Metrics/BlockLength # rubocop:disable Layout/LineLength class Settings include Base # @!visibility private def initialize(*_) super # WORKAROUND: The values for services, version, and env can get set either directly OR as a side effect of # accessing tags (reading or writing). This is of course really confusing and error-prone, e.g. in an app # WITHOUT this workaround where you define `DD_TAGS=env:envenvtag,service:envservicetag,version:envversiontag` # and do: # # puts Datadog.configuration.instance_exec { "#{service} #{env} #{version}" } # Datadog.configuration.tags # puts Datadog.configuration.instance_exec { "#{service} #{env} #{version}" } # # the output will be: # # [empty] # envservicetag envenvtag envversiontag # # That is -- the proper values for service/env/version are only set AFTER something accidentally or not triggers # the resolution of the tags. # This is really confusing, error prone, etc, so calling tags here is a really hacky but effective way to # avoid this. I could not think of a better way of fixing this issue without massive refactoring of tags parsing # (so that the individual service/env/version get correctly set even from their tags values, not as a side # effect). Sorry :( tags end # {https://docs.datadoghq.com/agent/ Datadog Agent} configuration. # @public_api settings :agent do # Agent hostname or IP. # @default `DD_AGENT_HOST` environment variable, otherwise `127.0.0.1` # @return [String,nil] option :host # Agent APM TCP port. # @see https://docs.datadoghq.com/getting_started/tracing/#datadog-apm # @default `DD_TRACE_AGENT_PORT` environment variable, otherwise `8126` # @return [String,nil] option :port # TODO: add declarative statsd configuration. Currently only usable via an environment variable. # Statsd configuration for agent access. # @public_api # settings :statsd do # # Agent Statsd UDP port. # # @configure_with {Datadog::Statsd} # # @default `DD_AGENT_HOST` environment variable, otherwise `8125` # # @return [String,nil] # option :port # end end # Datadog API key. # # For internal use only. # # @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.lazy end # Datadog diagnostic settings. # # Enabling these surfaces debug information that can be helpful to # diagnose issues related to Datadog internals. # @public_api settings :diagnostics do # Outputs all spans created by the host application to `Datadog.logger`. # # **This option is very verbose!** It's only recommended for non-production # environments. # # This option is helpful when trying to understand what information the # Datadog features are sending to the Agent or backend. # @default `DD_TRACE_DEBUG` environment variable, otherwise `false` # @return [Boolean] option :debug do |o| o.default { env_to_bool(Datadog::Core::Diagnostics::Ext::DD_TRACE_DEBUG, false) } o.lazy o.on_set do |enabled| # Enable rich debug print statements. # We do not need to unnecessarily load 'pp' unless in debugging mode. require 'pp' if enabled end end # Internal {Datadog::Statsd} metrics collection. # # The list of metrics collected can be found in {Datadog::Core::Diagnostics::Ext::Health::Metrics}. # @public_api settings :health_metrics do # Enable health metrics collection. # # @default `DD_HEALTH_METRICS_ENABLED` environment variable, otherwise `false` # @return [Boolean] option :enabled do |o| o.default { env_to_bool(Datadog::Core::Diagnostics::Ext::Health::Metrics::ENV_ENABLED, false) } o.lazy end # {Datadog::Statsd} instance to collect health metrics. # # If `nil`, health metrics creates a new {Datadog::Statsd} client with default agent configuration. # # @default `nil` # @return [Datadog::Statsd,nil] a custom {Datadog::Statsd} instance # @return [nil] an instance with default agent configuration will be lazily created option :statsd end # Tracer startup debug log statement configuration. # @public_api settings :startup_logs do # Enable startup logs collection. # # 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] 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::Diagnostics::Ext::DD_TRACE_STARTUP_LOGS, nil) } o.lazy end end end # The `env` tag in Datadog. Use it to separate out your staging, development, and production environments. # @see https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging # @default `DD_ENV` environment variable, otherwise `nil` # @return [String,nil] option :env do |o| # 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.lazy end # Internal `Datadog.logger` configuration. # # This logger instance is only used internally by the gem. # @public_api settings :logger do # The `Datadog.logger` object. # # Can be overwritten with a custom logger object that respects the # [built-in Ruby Logger](https://ruby-doc.org/stdlib-3.0.1/libdoc/logger/rdoc/Logger.html) # interface. # # @return Logger::Severity option :instance do |o| o.on_set { |value| set_option(:level, value.level) unless value.nil? } end # Log level for `Datadog.logger`. # @see Logger::Severity # @return Logger::Severity option :level, default: ::Logger::INFO end # Datadog Profiler-specific configurations. # # @see https://docs.datadoghq.com/tracing/profiler/ # @public_api settings :profiling do # Enable profiling. # # @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.lazy end # @public_api settings :exporter do option :transport end # @public_api settings :advanced do # This should never be reduced, as it can cause the resulting profiles to become biased. # The current default should be enough for most services, allowing 16 threads to be sampled around 30 times # per second for a 60 second period. option :max_events, default: 32768 # Controls the maximum number of frames for each thread sampled. Can be tuned to avoid omitted frames in the # produced profiles. Increasing this may increase the overhead of profiling. option :max_frames do |o| o.default { env_to_int(Profiling::Ext::ENV_MAX_FRAMES, 400) } o.lazy end # @public_api settings :endpoint do settings :collection do # When using profiling together with tracing, this controls if endpoint names # are gathered and reported together with profiles. # # @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.lazy end end end # Disable gathering of names and versions of gems in use by the service, used to power grouping and # categorization of stack traces. option :code_provenance_enabled, default: true # Use legacy transport code instead of HttpTransport. Temporarily added for migration to HttpTransport, # and will be removed soon. Do not use unless instructed to by support. option :legacy_transport_enabled do |o| o.default { env_to_bool('DD_PROFILING_LEGACY_TRANSPORT_ENABLED', false) } o.lazy end # Forces enabling the new profiler. We do not yet recommend turning on this option. # # Note that setting this to "false" (or not setting it) will not prevent the new profiler from # being automatically used in the future. # This option will be deprecated for removal once the new profiler gets enabled by default for all customers. option :force_enable_new_profiler do |o| o.default { env_to_bool('DD_PROFILING_FORCE_ENABLE_NEW', false) } o.lazy end end # @public_api settings :upload do 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.lazy end end end # [Runtime Metrics](https://docs.datadoghq.com/tracing/runtime_metrics/) # are StatsD metrics collected by the tracer to gain additional insights into an application's performance. # @public_api settings :runtime_metrics do # Enable runtime metrics. # @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.lazy end option :opts, default: ->(_i) { {} }, lazy: true option :statsd end # The `service` tag in Datadog. Use it to group related traces into a service. # @see https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging # @default `DD_SERVICE` environment variable, otherwise the program name (e.g. `'ruby'`, `'rails'`, `'pry'`) # @return [String] option :service do |o| # 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.lazy # 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 # nice_service_name = Datadog.configuration.service_without_fallback || nice_service_name_default o.helper(:service_without_fallback) do service_name = service service_name unless service_name.equal?(Core::Environment::Ext::FALLBACK_SERVICE_NAME) end end # The Datadog site host to send data to. # By default, data is sent to the Datadog US site: `app.datadoghq.com`. # # If your organization is on another site, you must update this value to the new site. # # For internal use only. # # @see https://docs.datadoghq.com/agent/troubleshooting/site/ # @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.lazy end # Default tags # # These tags are used by all Datadog products, when applicable. # e.g. trace spans, profiles, etc. # @default `DD_TAGS` environment variable (in the format `'tag1:value1,tag2:value2'`), otherwise `{}` # @return [Hash<String,String>] option :tags do |o| o.default do tags = {} # Parse tags from environment env_to_list(Core::Environment::Ext::ENV_TAGS, comma_separated_only: false).each do |tag| key, value = tag.split(':', 2) tags[key] = value if value && !value.empty? end # Override tags if defined tags[Core::Environment::Ext::TAG_ENV] = env unless env.nil? tags[Core::Environment::Ext::TAG_VERSION] = version unless version.nil? tags end o.setter do |new_value, old_value| # Coerce keys to strings string_tags = new_value.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) self.version = string_tags[Core::Environment::Ext::TAG_VERSION] end if service_without_fallback.nil? && string_tags.key?(Core::Environment::Ext::TAG_SERVICE) self.service = string_tags[Core::Environment::Ext::TAG_SERVICE] end # Merge with previous tags (old_value || {}).merge(string_tags) end o.lazy end # The time provider used by Datadog. It must respect the interface of [Time](https://ruby-doc.org/core-3.0.1/Time.html). # # When testing, it can be helpful to use a different time provider. # # For [Timecop](https://rubygems.org/gems/timecop), for example, `->{ Time.now_without_mock_time }` # allows Datadog features to use the real wall time when time is frozen. # # @default `->{ Time.now }` # @return [Proc<Time>] option :time_now_provider do |o| o.default { ::Time.now } o.on_set do |time_provider| Core::Utils::Time.now_provider = time_provider end o.resetter do |_value| # TODO: Resetter needs access to the default value # TODO: to help reduce duplication. -> { ::Time.now }.tap do |default| Core::Utils::Time.now_provider = default end end end # Tracer specific configurations. # @public_api settings :tracing do # Legacy [App Analytics](https://docs.datadoghq.com/tracing/legacy_app_analytics/) configuration. # # @configure_with {Datadog::Tracing} # @deprecated Use [Trace Retention and Ingestion](https://docs.datadoghq.com/tracing/trace_retention_and_ingestion/) # controls. # @public_api settings :analytics do # @default `DD_TRACE_ANALYTICS_ENABLED` environment variable, otherwise `nil` # @return [Boolean,nil] option :enabled do |o| o.default { env_to_bool(Tracing::Configuration::Ext::Analytics::ENV_TRACE_ANALYTICS_ENABLED, nil) } o.lazy end end # [Distributed Tracing](https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#distributed-tracing) propagation # style configuration. # # The supported formats are: # * `Datadog`: Datadog propagation format, described by [Distributed Tracing](https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#distributed-tracing). # * `B3`: B3 Propagation using multiple headers, described by [openzipkin/b3-propagation](https://github.com/openzipkin/b3-propagation#multiple-headers). # * `B3 single header`: B3 Propagation using a single header, described by [openzipkin/b3-propagation](https://github.com/openzipkin/b3-propagation#single-header). # # @public_api settings :distributed_tracing do # An ordered list of what data propagation styles the tracer will use to extract distributed tracing propagation # data from incoming requests and messages. # # The tracer will try to find distributed headers in the order they are present in the list provided to this option. # The first format to have valid data present will be used. # # @default `DD_PROPAGATION_STYLE_EXTRACT` environment variable (comma-separated list), # otherwise `['Datadog','B3','B3 single header']`. # @return [Array<String>] option :propagation_extract_style do |o| o.default do # Look for all headers by default env_to_list( Tracing::Configuration::Ext::Distributed::ENV_PROPAGATION_STYLE_EXTRACT, [ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG, Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_B3, Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_B3_SINGLE_HEADER ], comma_separated_only: true ) end o.lazy end # The data propagation styles the tracer will use to inject distributed tracing propagation # data into outgoing requests and messages. # # The tracer will inject data from all styles specified in this option. # # @default `DD_PROPAGATION_STYLE_INJECT` environment variable (comma-separated list), otherwise `['Datadog']`. # @return [Array<String>] option :propagation_inject_style do |o| o.default do env_to_list( Tracing::Configuration::Ext::Distributed::ENV_PROPAGATION_STYLE_INJECT, [Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG], comma_separated_only: true # Only inject Datadog headers by default ) end o.lazy end end # Enable trace collection and span generation. # # You can use this option to disable tracing without having to # remove the library as a whole. # # @default `DD_TRACE_ENABLED` environment variable, otherwise `true` # @return [Boolean] option :enabled do |o| o.default { env_to_bool(Datadog::Core::Diagnostics::Ext::DD_TRACE_ENABLED, true) } o.lazy end # A custom tracer instance. # # It must respect the contract of {Datadog::Tracing::Tracer}. # It's recommended to delegate methods to {Datadog::Tracing::Tracer} to ease the implementation # of a custom tracer. # # This option will not return the live tracer instance: it only holds a custom tracing instance, if any. # # For internal use only. # # @default `nil` # @return [Object,nil] option :instance # Automatic correlation between tracing and logging. # @see https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#trace-correlation # @return [Boolean] option :log_injection do |o| o.default { env_to_bool(Tracing::Configuration::Ext::Correlation::ENV_LOGS_INJECTION_ENABLED, true) } o.lazy end # Configures an alternative trace transport behavior, where # traces can be sent to the agent and backend before all spans # have finished. # # This is useful for long-running jobs or very large traces. # # The trace flame graph will display the partial trace as it is received and constantly # update with new spans as they are flushed. # @public_api settings :partial_flush do # Enable partial trace flushing. # # @default `false` # @return [Boolean] option :enabled, default: false # Minimum number of finished spans required in a single unfinished trace before # the tracer will consider that trace for partial flushing. # # This option helps preserve a minimum amount of batching in the # flushing process, reducing network overhead. # # This threshold only applies to unfinished traces. Traces that have finished # are always flushed immediately. # # @default 500 # @return [Integer] option :min_spans_threshold, default: 500 end # Enables {https://docs.datadoghq.com/tracing/trace_retention_and_ingestion/#datadog-intelligent-retention-filter # Datadog intelligent retention filter}. # @default `true` # @return [Boolean,nil] option :priority_sampling option :report_hostname do |o| o.default { env_to_bool(Tracing::Configuration::Ext::NET::ENV_REPORT_HOSTNAME, false) } o.lazy end # A custom sampler instance. # The object must respect the {Datadog::Tracing::Sampling::Sampler} interface. # @default `nil` # @return [Object,nil] option :sampler # Client-side sampling configuration. # @see https://docs.datadoghq.com/tracing/trace_ingestion/mechanisms/ # @public_api settings :sampling do # Default sampling rate for the tracer. # # If `nil`, the trace uses an automatic sampling strategy that tries to ensure # the collection of traces that are considered important (e.g. traces with an error, traces # for resources not seen recently). # # @default `DD_TRACE_SAMPLE_RATE` environment variable, otherwise `nil`. # @return [Float,nil] option :default_rate do |o| o.default { env_to_float(Tracing::Configuration::Ext::Sampling::ENV_SAMPLE_RATE, nil) } o.lazy end # Rate limit for number of spans per second. # # Spans created above the limit will contribute to service metrics, but won't # have their payload stored. # # @default `DD_TRACE_RATE_LIMIT` environment variable, otherwise 100. # @return [Numeric,nil] option :rate_limit do |o| o.default { env_to_float(Tracing::Configuration::Ext::Sampling::ENV_RATE_LIMIT, 100) } o.lazy end # Single span sampling rules. # These rules allow a span to be kept when its encompassing trace is dropped. # # The syntax for single span sampling rules can be found here: # TODO: <Single Span Sampling documentation URL here> # # @default `DD_SPAN_SAMPLING_RULES` environment variable. # Otherwise, `ENV_SPAN_SAMPLING_RULES_FILE` environment variable. # Otherwise `nil`. # @return [String,nil] # @public_api option :span_rules do |o| o.default do rules = ENV[Tracing::Configuration::Ext::Sampling::Span::ENV_SPAN_SAMPLING_RULES] rules_file = ENV[Tracing::Configuration::Ext::Sampling::Span::ENV_SPAN_SAMPLING_RULES_FILE] if rules if rules_file Datadog.logger.warn( 'Both DD_SPAN_SAMPLING_RULES and DD_SPAN_SAMPLING_RULES_FILE were provided: only ' \ 'DD_SPAN_SAMPLING_RULES will be used. Please do not provide DD_SPAN_SAMPLING_RULES_FILE when ' \ 'also providing DD_SPAN_SAMPLING_RULES as their configuration conflicts. ' \ "DD_SPAN_SAMPLING_RULES_FILE=#{rules_file} DD_SPAN_SAMPLING_RULES=#{rules}" ) end rules elsif rules_file begin File.read(rules_file) rescue => e # `File#read` errors have clear and actionable messages, no need to add extra exception info. Datadog.logger.warn( "Cannot read span sampling rules file `#{rules_file}`: #{e.message}." \ 'No span sampling rules will be applied.' ) nil end end end o.lazy end end # [Continuous Integration Visibility](https://docs.datadoghq.com/continuous_integration/) configuration. # @public_api settings :test_mode do # Enable test mode. This allows the tracer to collect spans from test runs. # # It also prevents the tracer from collecting spans in a production environment. Only use in a test environment. # # @default `DD_TRACE_TEST_MODE_ENABLED` environment variable, otherwise `false` # @return [Boolean] option :enabled do |o| o.default { env_to_bool(Tracing::Configuration::Ext::Test::ENV_MODE_ENABLED, false) } o.lazy end option :trace_flush do |o| o.default { nil } o.lazy end option :writer_options do |o| o.default { {} } o.lazy end end # @see file:docs/GettingStarted.md#configuring-the-transport-layer Configuring the transport layer # # A {Proc} that configures a custom tracer transport. # @yield Receives a {Datadog::Transport::HTTP} that can be modified with custom adapters and settings. # @yieldparam [Datadog::Transport::HTTP] t transport to be configured. # @default `nil` # @return [Proc,nil] option :transport_options, default: nil # A custom writer instance. # The object must respect the {Datadog::Tracing::Writer} interface. # # This option is recommended for internal use only. # # @default `nil` # @return [Object,nil] option :writer # A custom {Hash} with keyword options to be passed to {Datadog::Tracing::Writer#initialize}. # # This option is recommended for internal use only. # # @default `{}` # @return [Hash,nil] option :writer_options, default: ->(_i) { {} }, lazy: true # Client IP configuration # @public_api settings :client_ip do # Whether client IP collection is enabled. When enabled client IPs from HTTP requests will # be reported in traces. # # @see https://docs.datadoghq.com/tracing/configure_data_security#configuring-a-client-ip-header # # @default The negated value of the `DD_TRACE_CLIENT_IP_HEADER_DISABLED` environment # variable or `true` if it doesn't exist. # @return [Boolean] option :enabled do |o| o.default { !env_to_bool(Tracing::Configuration::Ext::ClientIp::ENV_DISABLED, false) } o.lazy end # An optional name of a custom header to resolve the client IP from. # # @default `DD_TRACE_CLIENT_IP_HEADER` environment variable, otherwise `nil`. # @return [String,nil] option :header_name do |o| o.default { ENV.fetch(Tracing::Configuration::Ext::ClientIp::ENV_HEADER_NAME, nil) } o.lazy end end end # The `version` tag in Datadog. Use it to enable [Deployment Tracking](https://docs.datadoghq.com/tracing/deployment_tracking/). # @see https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging # @default `DD_VERSION` environment variable, otherwise `nils` # @return [String,nil] option :version do |o| # NOTE: version 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_VERSION, nil) } o.lazy end # Client-side telemetry configuration # @public_api settings :telemetry do # Enable telemetry collection. This allows telemetry events to be emitted to the telemetry API. # # @default `DD_INSTRUMENTATION_TELEMETRY_ENABLED` environment variable, otherwise `false`. In a future release, # this value will be changed to `true` by default as documented [here](https://docs.datadoghq.com/tracing/configure_data_security/#telemetry-collection). # @return [Boolean] option :enabled do |o| o.default { env_to_bool(Core::Telemetry::Ext::ENV_ENABLED, false) } o.lazy end end end # rubocop:enable Metrics/BlockLength # rubocop:enable Layout/LineLength end end end