# frozen_string_literal: true module OpenTracing module Instrumentation module Bunny # ConsumeTracer extract parent span from message headers and # create span around passed block # # Usage: # consumer_tracer = \ # OpenTracing::Instrumentation::Bunny::ConsumeTracer.new # # consumer_tracer = \ # OpenTracing::Instrumentation::Bunny::ConsumeTracer.new do |config| # config.tracer = custom_tracer # end # # queue.subscribe(block: true) do |delivery_info, properties, payload| # consume_tracer.consume(delivery_info, properties) do # end # end class ConsumeTracer extend Forwardable # @param config [ConsumeTracerConfig] def initialize(config: ConsumeTracerConfig.new) yield config if block_given? @config = config end # Extract tracing parent from headers. Create span with tags. # If block passed, then span closed after block exit, otherwise # return active scope. # # @param delivery_info [Bunny::DeliveryInfo] # @param properties [Bunny::MessageProperties] # @yield if block passed, then it called and after scope closed # @yieldparam scope [OpenTracing::Scope] # @return [OpenTracing::Scope, nil] return active scope if called # without block, otherwise return block result def consume(delivery_info, properties) span_scope = safe_start_active_span(delivery_info, properties) return span_scope unless block_given? handle_error(span_scope) do yield span_scope end end private def_delegators( :@config, :tracer, :operation_name_builder, :tags_builder, :error_writer, :logger, ) def handle_error(span_scope) yield rescue StandardError => e error_writer.write_error(span_scope.span, e) raise e ensure # Close span if exists span_scope&.close end def safe_start_active_span(delivery_info, properties) start_active_span(delivery_info, properties) rescue StandardError => e logger&.error(e) nil end def start_active_span(delivery_info, properties) operation_name = build_operation_name(delivery_info) tags = build_tags(delivery_info, properties) references = build_references(properties[:headers]) tracer.start_active_span( operation_name, tags: tags, references: references, ) end def build_operation_name(delivery_info) operation_name_builder.build_operation_name(delivery_info) end def build_tags(delivery_info, properties) tags_builder.build_tags(delivery_info, properties) end def build_references(headers) return [] if headers.nil? span_context = tracer.extract( OpenTracing::FORMAT_TEXT_MAP, headers, ) [OpenTracing::Reference.follows_from(span_context)] end end end end end