# frozen_string_literal: true require "faraday" require "faraday_middleware" require "rufus/scheduler" require_relative "../sampler/rules_sampler" require_relative "../sampler/message_process_sampler" module Aspecto # Aspecto's OpenTelemetry distribution module OpenTelemetry module Config # Handle fetching of remote configuration from aspecto class RemoteConfig def initialize(aspecto_auth, service_name, env, fallback_sampler) @service_name = service_name @env = env @fallback_sampler = fallback_sampler aspecto_config_url = ENV.fetch("ASPECTO_CONFIG_HOST", "https://config.aspecto.io") @http_client = Faraday.new "#{aspecto_config_url}/config/#{aspecto_auth}" do |f| f.response :json # decode response bodies as JSON end @http_client.options.timeout = 10 @scheduler = Rufus::Scheduler.new @remote_config_poll_frequency = ENV.fetch("ASPECTO_REMOTE_CONFIG_POLL_FREQUENCY", "30s") @scheduler.interval @remote_config_poll_frequency, first: :now do update_config end ::OpenTelemetry.logger.info "[Aspecto] SDK client initialized" end def shutdown @scheduler.shutdown end private def update_config # rubocop:disable Metrics/AbcSize ::OpenTelemetry::Common::Utilities.untraced do response = @http_client.get do |req| req.headers["If-None-Match"] = @latest_config_etag unless @latest_config_etag.nil? end @latest_config_etag = response.headers["etag"] return if response.status == 304 if response.status >= 400 ::OpenTelemetry.logger.error("[Aspecto] error when trying to get remote config. will try again in #{@remote_config_poll_frequency}") return end handle_new_config response.body if response.status < 300 end rescue StandardError => e ::OpenTelemetry.logger.error "[Aspecto] updating remote config failed. using previous remote config" ::OpenTelemetry.logger.debug e end def handle_new_config(config) ::OpenTelemetry.logger.info("[Aspecto] successfully received remote configuration") update_sampler config["samplingRules"] end def update_sampler(sampler_config) rules_sampler = Sampler::RulesSampler.new sampler_config, @fallback_sampler, @service_name, @env service_sampler = ::OpenTelemetry::SDK::Trace::Samplers.parent_based(root: rules_sampler) message_process_sampler = Sampler::MessageProcessSampler.new rules_sampler, service_sampler # updating the sampler should be thread safe as it's an atomic setter on a global value ::OpenTelemetry.tracer_provider.sampler = message_process_sampler end end end end end