# frozen_string_literal: true require_relative "opentelemetry/version" require_relative "opentelemetry/configurator" require_relative "opentelemetry/resource/detectors/aspecto" require_relative "opentelemetry/resource/detectors/deployment" require_relative "opentelemetry/config/remote_config" require "opentelemetry/sdk" require "opentelemetry/exporter/otlp" require "opentelemetry/instrumentation/all" require "opentelemetry-instrumentation-aws_sdk" module Aspecto # Aspecto OpenTelemetry Distro module OpenTelemetry module_function class Error < StandardError; end def configure # rubocop:disable Metrics/AbcSize, Metrics/MethodLength configurator = Configurator.new yield configurator if block_given? validate_configurator_options(configurator) ::OpenTelemetry::SDK.configure do |c| c.logger = Logger.new($stdout, level: configurator.log_level) c.resource = Aspecto::OpenTelemetry::Resource::Detectors::Aspecto.detect c.resource = Aspecto::OpenTelemetry::Resource::Detectors::Deployment.detect c.resource = configurator.config_override_resource # must be last c.use_all "OpenTelemetry::Instrumentation::ActionPack" => { enable_recognize_route: true }, "OpenTelemetry::Instrumentation::AwsSdk" => { suppress_internal_instrumentation: true } otlp_exporter = ::OpenTelemetry::Exporter::OTLP::Exporter.new(endpoint: configurator.otel_exporter_otlp_traces_endpoint, headers: { "Authorization" => configurator.aspecto_auth }) span_processor = ::OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new otlp_exporter c.add_span_processor span_processor at_exit do # at_exit might be call when service terminates # but can also be the initiator or the application like with sinatra: # https://github.com/sinatra/sinatra/blob/cd503e6c590cd48c2c9bb7869522494bfc62cb14/lib/sinatra/main.rb#L25 span_processor.force_flush timeout: 2 end end fallback_sampler = ::OpenTelemetry::SDK::Trace::Samplers.trace_id_ratio_based(configurator.sampling_ratio) # TODO: how to properly extract the data from resource? _, service_name = ::OpenTelemetry.tracer_provider.resource.attribute_enumerator.detect { |elem| elem[0] == ::OpenTelemetry::SemanticConventions::Resource::SERVICE_NAME } _, env = ::OpenTelemetry.tracer_provider.resource.attribute_enumerator.detect { |elem| elem[0] == ::OpenTelemetry::SemanticConventions::Resource::DEPLOYMENT_ENVIRONMENT } @remote_config_service = Config::RemoteConfig.new configurator.aspecto_auth, service_name, env, fallback_sampler rescue StandardError => e warn "Failed to initialize Aspecto tracing." warn e end def shutdown ::OpenTelemetry.logger.info("Aspecto is shuting down. tracing is now stopped") ::OpenTelemetry.tracer_provider.shutdown @remote_config_service&.shutdown end def validate_configurator_options(configurator) # aspecto_auth unless configurator.aspecto_auth.instance_of?(String) && !configurator.aspecto_auth.empty? raise " Unable to retrieve Aspecto token. In order for the Aspecto service to work, it requires an auth token. Please provide it through ASPECTO_AUTH env param OR aspecto_auth configure option. You can get the token from: https://app.aspecto.io/app/integration/api-key For more details please check: https://docs.aspecto.io " end # sampling_ratio unless (configurator.sampling_ratio.is_a?(Numeric) || configurator.sampling_ratio.is_a?(String)) && Float(configurator.sampling_ratio).between?(0, 1) # rubocop:disable Style/GuardClause raise " sampling_ratio should be number in range [0.0, 1.0], received #{configurator.sampling_ratio} " end end end end