diff --git a/sdk/lib/opentelemetry/sdk/configurator.rb b/sdk/lib/opentelemetry/sdk/configurator.rb index 94a5020ebf..3ce92a0091 100644 --- a/sdk/lib/opentelemetry/sdk/configurator.rb +++ b/sdk/lib/opentelemetry/sdk/configurator.rb @@ -33,6 +33,9 @@ def fields attr_writer :propagators, :error_handler, :id_generator + Components = Struct.new(:tracer_provider, :propagators, keyword_init: true) + Config = Struct.new(:propagator, :tracer_provider, keyword_init: true) + def initialize @instrumentation_names = [] @instrumentation_config_map = {} @@ -142,12 +145,12 @@ def add_log_record_processor(log_record_processor); end # - setup tracer_provider, meter_provider, and logger_provider # - install instrumentation def configure + configuration = parse(ENV.fetch('OTEL_CONFIG_FILE', '')) OpenTelemetry.logger = logger OpenTelemetry.error_handler = error_handler - configure_propagation - configure_span_processors - tracer_provider.id_generator = @id_generator - OpenTelemetry.tracer_provider = tracer_provider + components = create(configuration) + OpenTelemetry.propagation = Context::Propagation::CompositeTextMapPropagator.compose_propagators(components.propagators.compact) + OpenTelemetry.tracer_provider = components.tracer_provider metrics_configuration_hook logs_configuration_hook install_instrumentation @@ -160,7 +163,7 @@ def metrics_configuration_hook; end def logs_configuration_hook; end def tracer_provider - @tracer_provider ||= Trace::TracerProvider.new(resource: @resource) + @tracer_provider ||= Trace::TracerProviderConfig.new end def check_use_mode!(mode) @@ -178,8 +181,8 @@ def install_instrumentation end def configure_span_processors - processors = @span_processors.empty? ? wrapped_exporters_from_env.compact : @span_processors - processors.each { |p| tracer_provider.add_span_processor(p) } + @span_processors += tracer_provider&.span_processors || [] + @span_processors.empty? ? wrapped_exporters_from_env.compact : @span_processors end def wrapped_exporters_from_env # rubocop:disable Metrics/CyclomaticComplexity @@ -206,8 +209,20 @@ def wrapped_exporters_from_env # rubocop:disable Metrics/CyclomaticComplexity end end - def configure_propagation # rubocop:disable Metrics/CyclomaticComplexity - propagators = ENV.fetch('OTEL_PROPAGATORS', 'tracecontext,baggage').split(',').uniq.collect do |propagator| + def configure_propagation(propagator_config) + if propagator_config + propagator_list = propagator_config.composite_list&.split(',') || [] + Array(propagator_config.composite&.instance_variables).each do |propagator| + propagatorList << propagator.to_s.delete_prefix('@').strip + end + else + propagator_list = ENV.fetch('OTEL_PROPAGATORS', 'tracecontext,baggage').split(',') + end + build_propagators(propagator_list) + end + + def build_propagators(propagator_list) # rubocop:disable Metrics/CyclomaticComplexity + propagator_list.uniq.collect do |propagator| case propagator when 'tracecontext' then OpenTelemetry::Trace::Propagation::TraceContext.text_map_propagator when 'baggage' then OpenTelemetry::Baggage::Propagation.text_map_propagator @@ -222,7 +237,6 @@ def configure_propagation # rubocop:disable Metrics/CyclomaticComplexity NoopTextMapPropagator.new end end - OpenTelemetry.propagation = Context::Propagation::CompositeTextMapPropagator.compose_propagators((@propagators || propagators).compact) end def fetch_propagator(name, class_name, gem_suffix = name) @@ -238,6 +252,24 @@ def fetch_exporter(name, class_name) OpenTelemetry.logger.warn "The #{name} exporter cannot be configured - please add opentelemetry-exporter-#{name} to your Gemfile, spans will not be exported" nil end + + def parse(filepath) + Config.new + end + + def create(config) + Components.new.tap do |c| + c.tracer_provider = Trace::TracerProvider.new( + sampler: tracer_provider&.sampler, + resource: @resource, + id_generator: @id_generator || tracer_provider&.id_generator, + span_limits: tracer_provider&.span_limits, + span_processors: configure_span_processors, + tracer_provider_config: config&.tracer_provider + ) + c.propagators = @propagators || configure_propagation(config&.propagator) + end + end end end end diff --git a/sdk/lib/opentelemetry/sdk/trace.rb b/sdk/lib/opentelemetry/sdk/trace.rb index 1fe41757f6..82392d19df 100644 --- a/sdk/lib/opentelemetry/sdk/trace.rb +++ b/sdk/lib/opentelemetry/sdk/trace.rb @@ -22,3 +22,4 @@ module Trace require 'opentelemetry/sdk/trace/span' require 'opentelemetry/sdk/trace/tracer' require 'opentelemetry/sdk/trace/tracer_provider' +require 'opentelemetry/sdk/trace/tracer_provider_config' diff --git a/sdk/lib/opentelemetry/sdk/trace/samplers/parent_based.rb b/sdk/lib/opentelemetry/sdk/trace/samplers/parent_based.rb index 97940a7b1d..985fa4f4e3 100644 --- a/sdk/lib/opentelemetry/sdk/trace/samplers/parent_based.rb +++ b/sdk/lib/opentelemetry/sdk/trace/samplers/parent_based.rb @@ -18,12 +18,12 @@ module Samplers # * Local parent (!SpanContext.remote? with trace_flags.sampled?) # * Local parent (!SpanContext.remote? with !trace_flags.sampled?) class ParentBased - def initialize(root, remote_parent_sampled, remote_parent_not_sampled, local_parent_sampled, local_parent_not_sampled) - @root = root - @remote_parent_sampled = remote_parent_sampled - @remote_parent_not_sampled = remote_parent_not_sampled - @local_parent_sampled = local_parent_sampled - @local_parent_not_sampled = local_parent_not_sampled + def initialize(root = nil, remote_parent_sampled = nil, remote_parent_not_sampled = nil, local_parent_sampled = nil, local_parent_not_sampled = nil) + @root = root || Samplers::ALWAYS_ON + @remote_parent_sampled = remote_parent_sampled || Samplers::ALWAYS_ON + @remote_parent_not_sampled = remote_parent_not_sampled || Samplers::ALWAYS_OFF + @local_parent_sampled = local_parent_sampled || Samplers::ALWAYS_ON + @local_parent_not_sampled = local_parent_not_sampled || Samplers::ALWAYS_OFF end def ==(other) diff --git a/sdk/lib/opentelemetry/sdk/trace/samplers/trace_id_ratio_based.rb b/sdk/lib/opentelemetry/sdk/trace/samplers/trace_id_ratio_based.rb index 841d76a32d..14362c0f79 100644 --- a/sdk/lib/opentelemetry/sdk/trace/samplers/trace_id_ratio_based.rb +++ b/sdk/lib/opentelemetry/sdk/trace/samplers/trace_id_ratio_based.rb @@ -14,8 +14,10 @@ module Samplers class TraceIdRatioBased attr_reader :description - def initialize(probability) - @probability = probability + def initialize(probability = nil) + @probability = probability || Float(ENV.fetch('OTEL_TRACES_SAMPLER_ARG', 1.0)) + raise ArgumentError, 'ratio must be in range [0.0, 1.0]' unless (0.0..1.0).cover?(@probability) + @id_upper_bound = (probability * (2**64 - 1)).ceil @description = format('TraceIdRatioBased{%.6f}', probability) end diff --git a/sdk/lib/opentelemetry/sdk/trace/tracer_provider.rb b/sdk/lib/opentelemetry/sdk/trace/tracer_provider.rb index d1689a9d45..820cea0200 100644 --- a/sdk/lib/opentelemetry/sdk/trace/tracer_provider.rb +++ b/sdk/lib/opentelemetry/sdk/trace/tracer_provider.rb @@ -25,21 +25,28 @@ class TracerProvider < OpenTelemetry::Trace::TracerProvider # rubocop:disable Me # @param [optional SpanLimits] span_limits The limits to apply to attribute, # event and link counts for Spans created by Tracers created by this # TracerProvider + # @param [optional SpanProcessors] span_processors The span span processors to be used to, + # process & export traces to a backend. + # @param [optional TracerProviderConfig] tracer_provider_config The configuration block used to + # configure the tracer provider which includes the config for tracers as well as plugins. # # @return [TracerProvider] - def initialize(sampler: sampler_from_environment(Samplers.parent_based(root: Samplers::ALWAYS_ON)), + def initialize(sampler: nil, resource: OpenTelemetry::SDK::Resources::Resource.create, - id_generator: OpenTelemetry::Trace, - span_limits: SpanLimits::DEFAULT) + id_generator: nil, + span_limits: nil, + span_processors: nil, + tracer_provider_config: nil) @mutex = Mutex.new @registry = {} @registry_mutex = Mutex.new @span_processors = [] - @span_limits = span_limits - @sampler = sampler - @id_generator = id_generator + @span_limits = span_limits || tracer_provider_config&.limits || SpanLimits::DEFAULT + @sampler = sampler || tracer_provider_config&.samplar ? sampler_from_config(tracer_provider_config&.sampler) : sampler_from_environment + @id_generator = id_generator || OpenTelemetry::Trace @stopped = false @resource = resource + Array(span_processors).each { |p| add_span_processor(p) } end # Returns a {Tracer} instance. @@ -169,10 +176,33 @@ def internal_start_span(name, kind, attributes, links, start_timestamp, parent_c private - def sampler_from_environment(default_sampler) - case ENV.fetch('OTEL_TRACES_SAMPLER', nil) + def sampler_from_config(sampler_config) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + ivar = sampler_config.instance_variables.first + sampler_name = ivar.to_s.delete_prefix('@') + config = obj.instance_variable_get(ivar) + + if sampler_config.parent_based&.root&.always_off + root = Samplers::ALWAYS_OFF + elsif sampler_config.parent_based&.root&.always_on + root = Samplers::ALWAYS_ON + elsif sampler_config.parent_based # this acts as a default + root = TraceIdRatioBased.new(probability: sampler_config.parent_based.root&.trace_id_ratio_based&.ratio || 1.0) + end + + build_sampler(sampler_name, config, root) + end # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + + def sampler_from_environment + build_sampler(ENV.fetch('OTEL_TRACES_SAMPLER', nil)) + end + + def build_sampler(sampler, config = nil, root = nil) # rubocop:disable Metrics/CyclomaticComplexity + default_sampler = Samplers.parent_based(root: Samplers::ALWAYS_ON) + case sampler when 'always_on' then Samplers::ALWAYS_ON when 'always_off' then Samplers::ALWAYS_OFF + when 'trace_id_ratio_based' then TraceIdRatioBased.new(probability: config&.ratio || 1.0) + when 'parent_based' then ParentBased.new(root) when 'traceidratio' then Samplers.trace_id_ratio_based(Float(ENV.fetch('OTEL_TRACES_SAMPLER_ARG', 1.0))) when 'parentbased_always_on' then Samplers.parent_based(root: Samplers::ALWAYS_ON) when 'parentbased_always_off' then Samplers.parent_based(root: Samplers::ALWAYS_OFF) @@ -182,7 +212,7 @@ def sampler_from_environment(default_sampler) rescue StandardError => e OpenTelemetry.handle_error(exception: e, message: "installing default sampler #{default_sampler.description}") default_sampler - end + end # rubocop:enable Metrics/CyclomaticComplexity end end end diff --git a/sdk/lib/opentelemetry/sdk/trace/tracer_provider_config.rb b/sdk/lib/opentelemetry/sdk/trace/tracer_provider_config.rb new file mode 100644 index 0000000000..0643030fdf --- /dev/null +++ b/sdk/lib/opentelemetry/sdk/trace/tracer_provider_config.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Trace + # The manually specified tracer provider configuring for the SDK to use. + class TracerProviderConfig + attr_accessor :sampler, :id_generator, :span_limits, :span_processors + + def initialize + @span_processors = [] + end + + def add_span_processor(processor) + @span_processors << processor + end + end + end + end +end