lib/hypertrace/ruby_agent.rb in hypertrace-agent-0.2.2 vs lib/hypertrace/ruby_agent.rb in hypertrace-agent-0.3.0

- old
+ new

@@ -30,76 +30,127 @@ # RubyAgent is repetitve, but want to remain somewhat consistent compared to python/node class Hypertrace::RubyAgent include Hypertrace::Logging include Singleton + SUPPORTED_INSTRUMENTATIONS = [ + 'OpenTelemetry::Instrumentation::ActionPack', + 'OpenTelemetry::Instrumentation::ActionView', + 'OpenTelemetry::Instrumentation::ActiveRecord', + 'OpenTelemetry::Instrumentation::ActiveSupport', + 'OpenTelemetry::Instrumentation::Faraday', + 'OpenTelemetry::Instrumentation::Mongo', + 'OpenTelemetry::Instrumentation::Mysql2', + 'OpenTelemetry::Instrumentation::PG', + 'OpenTelemetry::Instrumentation::Rack', + 'OpenTelemetry::Instrumentation::Sinatra', + 'OpenTelemetry::Instrumentation::Net::HTTP', + 'OpenTelemetry::Instrumentation::HTTP', + 'OpenTelemetry::Instrumentation::RestClient' + ].freeze + + SUPPORTED_INSTRUMENTATIONS_ADDITIONAL_PATCH = { + 'OpenTelemetry::Instrumentation::Faraday' => ['./instrumentation/faraday_patch'], + 'OpenTelemetry::Instrumentation::Rack' => ['./instrumentation/rack', './instrumentation/rack_env_getter'], + 'OpenTelemetry::Instrumentation::Net::HTTP' => ['./instrumentation/net_http_patch'], + 'OpenTelemetry::Instrumentation::HTTP' => ['./instrumentation/http_patch'], + 'OpenTelemetry::Instrumentation::RestClient' => ['./instrumentation/rest_client_patch'], + } + def self.instrument! self.instance.instrument! end + def self.instrument_as_additional! + self.instance.instrument_as_additional! + end + def self.config self.instance.config end def initialize(version = Hypertrace::VERSION) - log.info {"Initializing Hypertrace"} - configure_otel_logger! + log.info { "Initializing Hypertrace" } @config = Hypertrace::Config::Config.new @version = version - log.info {"Hypertrace version: #{Hypertrace::VERSION}"} - log.info {"Ruby version: #{RUBY_VERSION}"} + log.info { "Hypertrace version: #{Hypertrace::VERSION}" } + log.info { "Ruby version: #{RUBY_VERSION}" } end def config @config.config end def instrument! initalize_tracer end - def initalize_tracer + def instrument_as_additional! resource = OpenTelemetry::SDK::Resources::Resource.create(create_resource_attributes) + exporter = create_resource_customized_exporter resource + span_processor = create_span_processor exporter - # TODO: Extra resource Attributes From Config - exporter = create_exporter - OpenTelemetry::SDK.configure do |c| - if ENV['HT_CI_TEST'] != nil - span_processor = OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(EXPORTER) - c.add_span_processor span_processor - else - c.add_span_processor OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(exporter) - end - c.resource = resource - - c.use 'OpenTelemetry::Instrumentation::ActionPack' - c.use 'OpenTelemetry::Instrumentation::ActionView' - c.use 'OpenTelemetry::Instrumentation::ActiveRecord' - c.use 'OpenTelemetry::Instrumentation::ActiveSupport' - c.use 'OpenTelemetry::Instrumentation::Faraday' - c.use 'OpenTelemetry::Instrumentation::Mongo' - c.use 'OpenTelemetry::Instrumentation::Mysql2' - c.use 'OpenTelemetry::Instrumentation::PG' - c.use 'OpenTelemetry::Instrumentation::Rack' - c.use 'OpenTelemetry::Instrumentation::Sinatra' - c.use 'OpenTelemetry::Instrumentation::Net::HTTP' - c.use 'OpenTelemetry::Instrumentation::HTTP' - c.use 'OpenTelemetry::Instrumentation::RestClient' + existing_processors = OpenTelemetry.tracer_provider.instance_variable_get(:"@span_processors") + if existing_processors.nil? || existing_processors&.empty? + log.error("No existing tracer_provider found, continuing without Hypertrace instrumentation") + return end + existing_processors << span_processor + OpenTelemetry.tracer_provider.instance_variable_set(:'@span_processors', existing_processors) + # We don't pass a span_processor or resource because we have to add it to the already configured tracer provider + # The resource will be added at the export phase + configure_instrumentation nil, nil + configure_propagators + end - apply_custom_patch './instrumentation/rack' - apply_custom_patch './instrumentation/net_http_patch' - apply_custom_patch './instrumentation/http_patch' - apply_custom_patch './instrumentation/faraday_patch' - apply_custom_patch './instrumentation/rest_client_patch' - apply_custom_patch './instrumentation/rack_env_getter' + def create_span_processor exporter + return OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(EXPORTER) if ENV['HT_CI_TEST'] != nil + return OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(exporter) + end + def initalize_tracer + resource = OpenTelemetry::SDK::Resources::Resource.create(create_resource_attributes) + exporter = create_exporter + span_processor = create_span_processor(exporter) + # TODO: Extra resource Attributes From Config + configure_instrumentation span_processor, resource configure_propagators end + def configure_instrumentation span_processor, resource + OpenTelemetry::SDK.configure do |c| + c.add_span_processor span_processor if span_processor + c.resource = resource if resource + + SUPPORTED_INSTRUMENTATIONS.each do |instrumentation_string| + c.use instrumentation_string + additional_patches = SUPPORTED_INSTRUMENTATIONS_ADDITIONAL_PATCH[instrumentation_string] + if additional_patches + additional_patches.each do |patch_file| + apply_custom_patch patch_file + end + end + end + end + end + private + def create_resource_customized_exporter resource + exporter = nil + if config.reporting.trace_reporter_type == :OTLP + verify_mode = config.reporting.secure.value ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE + exporter = Hypertrace::OtlpHttpExporter.new(endpoint: config.reporting.endpoint.value, + ssl_verify_mode: verify_mode, + is_hypertrace: true, + supported_instrumentation_libraries: SUPPORTED_INSTRUMENTATIONS, + custom_resource: resource) + return exporter + end + log.error "Resource customized exporter not supported for zipkin, only OTLP HTTP" + end + def create_exporter exporter = nil if config.reporting.trace_reporter_type == :OTLP verify_mode = config.reporting.secure.value ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE exporter = OpenTelemetry::Exporter::OTLP::Exporter.new(endpoint: config.reporting.endpoint.value, @@ -121,23 +172,23 @@ elsif format[0] == :B3 propagator_list << OpenTelemetry::Propagator::B3::Single.text_map_propagator end end if propagator_list.empty? - log.warn{"No propagators were added!"} + log.warn { "No propagators were added!" } end OpenTelemetry.propagation = OpenTelemetry::Context::Propagation::CompositeTextMapPropagator.compose_propagators(propagator_list.compact) end private def apply_custom_patch file begin require_relative file - log.debug{"Applied patch for #{file}"} + log.debug { "Applied patch for #{file}" } rescue => _e - log.debug{"Unable to apply patch for #{file} this is most likely because the library is unavailable or an unsupported version"} + log.debug { "Unable to apply patch for #{file} this is most likely because the library is unavailable or an unsupported version" } end end def create_resource_attributes { @@ -145,13 +196,7 @@ 'service.instance.id': Process.pid, 'telemetry.sdk.version': @version, 'telemetry.sdk.name': 'hypertrace', 'telemetry.sdk.language': 'ruby' }.transform_keys(&:to_s) - end - - NULL_LOGGER = Logger.new(File::NULL) - - def configure_otel_logger! - OpenTelemetry.logger = NULL_LOGGER end end \ No newline at end of file