jaeger-client-with-ruby-32-support 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +33 -0
- data/.gitignore +12 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +61 -0
- data/.rubocop_todo.yml +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/Makefile +1 -0
- data/README.md +210 -0
- data/Rakefile +9 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/crossdock/Dockerfile +31 -0
- data/crossdock/Gemfile +6 -0
- data/crossdock/Gemfile.lock +37 -0
- data/crossdock/docker-compose.yml +68 -0
- data/crossdock/jaeger-docker-compose.yml +53 -0
- data/crossdock/rules.mk +35 -0
- data/crossdock/server +175 -0
- data/jaeger-client.gemspec +37 -0
- data/lib/jaeger/client/version.rb +7 -0
- data/lib/jaeger/client.rb +77 -0
- data/lib/jaeger/encoders/thrift_encoder.rb +173 -0
- data/lib/jaeger/extractors.rb +173 -0
- data/lib/jaeger/http_sender.rb +28 -0
- data/lib/jaeger/injectors.rb +83 -0
- data/lib/jaeger/rate_limiter.rb +61 -0
- data/lib/jaeger/recurring_executor.rb +35 -0
- data/lib/jaeger/reporters/composite_reporter.rb +17 -0
- data/lib/jaeger/reporters/in_memory_reporter.rb +30 -0
- data/lib/jaeger/reporters/logging_reporter.rb +22 -0
- data/lib/jaeger/reporters/null_reporter.rb +11 -0
- data/lib/jaeger/reporters/remote_reporter/buffer.rb +29 -0
- data/lib/jaeger/reporters/remote_reporter.rb +42 -0
- data/lib/jaeger/reporters.rb +7 -0
- data/lib/jaeger/samplers/const.rb +24 -0
- data/lib/jaeger/samplers/guaranteed_throughput_probabilistic.rb +47 -0
- data/lib/jaeger/samplers/per_operation.rb +77 -0
- data/lib/jaeger/samplers/probabilistic.rb +40 -0
- data/lib/jaeger/samplers/rate_limiting.rb +51 -0
- data/lib/jaeger/samplers/remote_controlled/instructions_fetcher.rb +34 -0
- data/lib/jaeger/samplers/remote_controlled.rb +119 -0
- data/lib/jaeger/samplers.rb +8 -0
- data/lib/jaeger/scope.rb +39 -0
- data/lib/jaeger/scope_manager/scope_identifier.rb +13 -0
- data/lib/jaeger/scope_manager/scope_stack.rb +33 -0
- data/lib/jaeger/scope_manager.rb +48 -0
- data/lib/jaeger/span/thrift_log_builder.rb +18 -0
- data/lib/jaeger/span.rb +97 -0
- data/lib/jaeger/span_context.rb +57 -0
- data/lib/jaeger/thrift_tag_builder.rb +42 -0
- data/lib/jaeger/trace_id.rb +48 -0
- data/lib/jaeger/tracer.rb +214 -0
- data/lib/jaeger/udp_sender/transport.rb +41 -0
- data/lib/jaeger/udp_sender.rb +26 -0
- data/script/create_follows_from_trace +51 -0
- data/script/create_trace +52 -0
- data/thrift/agent.thrift +32 -0
- data/thrift/gen-rb/jaeger/thrift/agent/agent.rb +118 -0
- data/thrift/gen-rb/jaeger/thrift/agent/agent_constants.rb +15 -0
- data/thrift/gen-rb/jaeger/thrift/agent/agent_types.rb +17 -0
- data/thrift/gen-rb/jaeger/thrift/agent.rb +116 -0
- data/thrift/gen-rb/jaeger/thrift/agent_constants.rb +13 -0
- data/thrift/gen-rb/jaeger/thrift/agent_types.rb +15 -0
- data/thrift/gen-rb/jaeger/thrift/collector.rb +82 -0
- data/thrift/gen-rb/jaeger/thrift/jaeger_constants.rb +13 -0
- data/thrift/gen-rb/jaeger/thrift/jaeger_types.rb +211 -0
- data/thrift/gen-rb/jaeger/thrift/zipkin/zipkin_collector.rb +84 -0
- data/thrift/gen-rb/jaeger/thrift/zipkin/zipkincore_constants.rb +41 -0
- data/thrift/gen-rb/jaeger/thrift/zipkin/zipkincore_types.rb +220 -0
- data/thrift/jaeger.thrift +88 -0
- data/thrift/zipkincore.thrift +300 -0
- metadata +260 -0
| @@ -0,0 +1,119 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'remote_controlled/instructions_fetcher'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Jaeger
         | 
| 6 | 
            +
              module Samplers
         | 
| 7 | 
            +
                class RemoteControlled
         | 
| 8 | 
            +
                  DEFAULT_REFRESH_INTERVAL = 60
         | 
| 9 | 
            +
                  DEFAULT_SAMPLING_HOST = 'localhost'
         | 
| 10 | 
            +
                  DEFAULT_SAMPLING_PORT = 5778
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  attr_reader :sampler
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def initialize(opts = {})
         | 
| 15 | 
            +
                    @sampler = opts.fetch(:sampler, Probabilistic.new)
         | 
| 16 | 
            +
                    @logger = opts.fetch(:logger, Logger.new($stdout))
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    @poll_executor = opts[:poll_executor] || begin
         | 
| 19 | 
            +
                      refresh_interval = opts.fetch(:refresh_interval, DEFAULT_REFRESH_INTERVAL)
         | 
| 20 | 
            +
                      RecurringExecutor.new(interval: refresh_interval)
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    @instructions_fetcher = opts[:instructions_fetcher] || begin
         | 
| 24 | 
            +
                      service_name = opts.fetch(:service_name)
         | 
| 25 | 
            +
                      host = opts.fetch(:host, DEFAULT_SAMPLING_HOST)
         | 
| 26 | 
            +
                      port = opts.fetch(:port, DEFAULT_SAMPLING_PORT)
         | 
| 27 | 
            +
                      InstructionsFetcher.new(host: host, port: port, service_name: service_name)
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def sample(*args)
         | 
| 32 | 
            +
                    @poll_executor.start(&method(:poll)) unless @poll_executor.running?
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    @sampler.sample(*args)
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def poll
         | 
| 38 | 
            +
                    @logger.debug 'Fetching sampling strategy'
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    instructions = @instructions_fetcher.fetch
         | 
| 41 | 
            +
                    handle_instructions(instructions)
         | 
| 42 | 
            +
                  rescue InstructionsFetcher::FetchFailed => e
         | 
| 43 | 
            +
                    @logger.warn "Fetching sampling strategy failed: #{e.message}"
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  private
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def handle_instructions(instructions)
         | 
| 49 | 
            +
                    if instructions['operationSampling']
         | 
| 50 | 
            +
                      update_per_operation_sampler(instructions['operationSampling'])
         | 
| 51 | 
            +
                    else
         | 
| 52 | 
            +
                      update_rate_limiting_or_probabilistic_sampler(instructions['strategyType'], instructions)
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  def update_per_operation_sampler(instructions)
         | 
| 57 | 
            +
                    strategies = normalize(instructions)
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    if @sampler.is_a?(PerOperation)
         | 
| 60 | 
            +
                      @sampler.update(strategies: strategies)
         | 
| 61 | 
            +
                    else
         | 
| 62 | 
            +
                      @sampler = PerOperation.new(strategies: strategies, max_operations: 2000)
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  def normalize(instructions)
         | 
| 67 | 
            +
                    {
         | 
| 68 | 
            +
                      default_sampling_probability: instructions['defaultSamplingProbability'],
         | 
| 69 | 
            +
                      default_lower_bound_traces_per_second: instructions['defaultLowerBoundTracesPerSecond'],
         | 
| 70 | 
            +
                      per_operation_strategies: instructions['perOperationStrategies'].map do |strategy|
         | 
| 71 | 
            +
                        {
         | 
| 72 | 
            +
                          operation: strategy['operation'],
         | 
| 73 | 
            +
                          probabilistic_sampling: {
         | 
| 74 | 
            +
                            sampling_rate: strategy['probabilisticSampling']['samplingRate']
         | 
| 75 | 
            +
                          }
         | 
| 76 | 
            +
                        }
         | 
| 77 | 
            +
                      end
         | 
| 78 | 
            +
                    }
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  def update_rate_limiting_or_probabilistic_sampler(strategy, instructions)
         | 
| 82 | 
            +
                    case strategy
         | 
| 83 | 
            +
                    when 'PROBABILISTIC'
         | 
| 84 | 
            +
                      update_probabilistic_strategy(instructions['probabilisticSampling'])
         | 
| 85 | 
            +
                    when 'RATE_LIMITING'
         | 
| 86 | 
            +
                      update_rate_limiting_strategy(instructions['rateLimitingSampling'])
         | 
| 87 | 
            +
                    else
         | 
| 88 | 
            +
                      @logger.warn "Unknown sampling strategy #{strategy}"
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  def update_probabilistic_strategy(instructions)
         | 
| 93 | 
            +
                    rate = instructions['samplingRate']
         | 
| 94 | 
            +
                    return unless rate
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                    if @sampler.is_a?(Probabilistic)
         | 
| 97 | 
            +
                      @sampler.update(rate: rate)
         | 
| 98 | 
            +
                      @logger.info "Updated Probabilistic sampler (rate=#{rate})"
         | 
| 99 | 
            +
                    else
         | 
| 100 | 
            +
                      @sampler = Probabilistic.new(rate: rate)
         | 
| 101 | 
            +
                      @logger.info "Updated sampler to Probabilistic (rate=#{rate})"
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  def update_rate_limiting_strategy(instructions)
         | 
| 106 | 
            +
                    max_traces_per_second = instructions['maxTracesPerSecond']
         | 
| 107 | 
            +
                    return unless max_traces_per_second
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    if @sampler.is_a?(RateLimiting)
         | 
| 110 | 
            +
                      @sampler.update(max_traces_per_second: max_traces_per_second)
         | 
| 111 | 
            +
                      @logger.info "Updated Ratelimiting sampler (max_traces_per_second=#{max_traces_per_second})"
         | 
| 112 | 
            +
                    else
         | 
| 113 | 
            +
                      @sampler = RateLimiting.new(max_traces_per_second: max_traces_per_second)
         | 
| 114 | 
            +
                      @logger.info "Updated sampler to Ratelimiting (max_traces_per_second=#{max_traces_per_second})"
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
                end
         | 
| 118 | 
            +
              end
         | 
| 119 | 
            +
            end
         | 
| @@ -0,0 +1,8 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'samplers/const'
         | 
| 4 | 
            +
            require_relative 'samplers/guaranteed_throughput_probabilistic'
         | 
| 5 | 
            +
            require_relative 'samplers/per_operation'
         | 
| 6 | 
            +
            require_relative 'samplers/probabilistic'
         | 
| 7 | 
            +
            require_relative 'samplers/rate_limiting'
         | 
| 8 | 
            +
            require_relative 'samplers/remote_controlled'
         | 
    
        data/lib/jaeger/scope.rb
    ADDED
    
    | @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Jaeger
         | 
| 4 | 
            +
              # Scope represents an OpenTracing Scope
         | 
| 5 | 
            +
              #
         | 
| 6 | 
            +
              # See http://www.opentracing.io for more information.
         | 
| 7 | 
            +
              class Scope
         | 
| 8 | 
            +
                def initialize(span, scope_stack, finish_on_close:)
         | 
| 9 | 
            +
                  @span = span
         | 
| 10 | 
            +
                  @scope_stack = scope_stack
         | 
| 11 | 
            +
                  @finish_on_close = finish_on_close
         | 
| 12 | 
            +
                  @closed = false
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                # Return the Span scoped by this Scope
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # @return [Span]
         | 
| 18 | 
            +
                attr_reader :span
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                # Close scope
         | 
| 21 | 
            +
                #
         | 
| 22 | 
            +
                # Mark the end of the active period for the current thread and Scope,
         | 
| 23 | 
            +
                # updating the ScopeManager#active in the process.
         | 
| 24 | 
            +
                def close
         | 
| 25 | 
            +
                  raise "Tried to close already closed span: #{inspect}" if @closed
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  @closed = true
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  @span.finish if @finish_on_close
         | 
| 30 | 
            +
                  removed_scope = @scope_stack.pop
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  if removed_scope != self # rubocop:disable Style/GuardClause
         | 
| 33 | 
            +
                    raise 'Removed non-active scope, ' \
         | 
| 34 | 
            +
                          "removed: #{removed_scope.inspect}, "\
         | 
| 35 | 
            +
                          "expected: #{inspect}"
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Jaeger
         | 
| 4 | 
            +
              class ScopeManager
         | 
| 5 | 
            +
                # @api private
         | 
| 6 | 
            +
                class ScopeIdentifier
         | 
| 7 | 
            +
                  def self.generate
         | 
| 8 | 
            +
                    # 65..90.chr are characters between A and Z
         | 
| 9 | 
            +
                    "opentracing_#{(0...8).map { rand(65..90).chr }.join}".to_sym
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Jaeger
         | 
| 4 | 
            +
              class ScopeManager
         | 
| 5 | 
            +
                # @api private
         | 
| 6 | 
            +
                class ScopeStack
         | 
| 7 | 
            +
                  def initialize
         | 
| 8 | 
            +
                    # Generate a random identifier to use as the Thread.current key. This is
         | 
| 9 | 
            +
                    # needed so that it would be possible to create multiple tracers in one
         | 
| 10 | 
            +
                    # thread (mostly useful for testing purposes)
         | 
| 11 | 
            +
                    @scope_identifier = ScopeIdentifier.generate
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def push(scope)
         | 
| 15 | 
            +
                    store << scope
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def pop
         | 
| 19 | 
            +
                    store.pop
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def peek
         | 
| 23 | 
            +
                    store.last
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  private
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def store
         | 
| 29 | 
            +
                    Thread.current[@scope_identifier] ||= []
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,48 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'scope_manager/scope_stack'
         | 
| 4 | 
            +
            require_relative 'scope_manager/scope_identifier'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Jaeger
         | 
| 7 | 
            +
              # ScopeManager represents an OpenTracing ScopeManager
         | 
| 8 | 
            +
              #
         | 
| 9 | 
            +
              # See http://www.opentracing.io for more information.
         | 
| 10 | 
            +
              #
         | 
| 11 | 
            +
              # The ScopeManager interface abstracts both the activation of Span instances
         | 
| 12 | 
            +
              # via ScopeManager#activate and access to an active Span/Scope via
         | 
| 13 | 
            +
              # ScopeManager#active
         | 
| 14 | 
            +
              #
         | 
| 15 | 
            +
              class ScopeManager
         | 
| 16 | 
            +
                def initialize
         | 
| 17 | 
            +
                  @scope_stack = ScopeStack.new
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                # Make a span instance active
         | 
| 21 | 
            +
                #
         | 
| 22 | 
            +
                # @param span [Span] the Span that should become active
         | 
| 23 | 
            +
                # @param finish_on_close [Boolean] whether the Span should automatically be
         | 
| 24 | 
            +
                #   finished when Scope#close is called
         | 
| 25 | 
            +
                # @return [Scope] instance to control the end of the active period for the
         | 
| 26 | 
            +
                #  Span. It is a programming error to neglect to call Scope#close on the
         | 
| 27 | 
            +
                #  returned instance.
         | 
| 28 | 
            +
                def activate(span, finish_on_close: true)
         | 
| 29 | 
            +
                  return active if active && active.span == span
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  scope = Scope.new(span, @scope_stack, finish_on_close: finish_on_close)
         | 
| 32 | 
            +
                  @scope_stack.push(scope)
         | 
| 33 | 
            +
                  scope
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                # Return active scope
         | 
| 37 | 
            +
                #
         | 
| 38 | 
            +
                # If there is a non-null Scope, its wrapped Span becomes an implicit parent
         | 
| 39 | 
            +
                # (as Reference#CHILD_OF) of any newly-created Span at
         | 
| 40 | 
            +
                # Tracer#start_active_span or Tracer#start_span time.
         | 
| 41 | 
            +
                #
         | 
| 42 | 
            +
                # @return [Scope] the currently active Scope which can be used to access the
         | 
| 43 | 
            +
                #   currently active Span.
         | 
| 44 | 
            +
                def active
         | 
| 45 | 
            +
                  @scope_stack.peek
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Jaeger
         | 
| 4 | 
            +
              class Span
         | 
| 5 | 
            +
                class ThriftLogBuilder
         | 
| 6 | 
            +
                  FIELDS = Jaeger::Thrift::Log::FIELDS
         | 
| 7 | 
            +
                  TIMESTAMP = FIELDS[Jaeger::Thrift::Log::TIMESTAMP].fetch(:name)
         | 
| 8 | 
            +
                  LOG_FIELDS = FIELDS[Jaeger::Thrift::Log::LOG_FIELDS].fetch(:name)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def self.build(timestamp, fields)
         | 
| 11 | 
            +
                    Jaeger::Thrift::Log.new(
         | 
| 12 | 
            +
                      TIMESTAMP => (timestamp.to_f * 1_000_000).to_i,
         | 
| 13 | 
            +
                      LOG_FIELDS => fields.map { |key, value| ThriftTagBuilder.build(key, value) }
         | 
| 14 | 
            +
                    )
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
    
        data/lib/jaeger/span.rb
    ADDED
    
    | @@ -0,0 +1,97 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'span/thrift_log_builder'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Jaeger
         | 
| 6 | 
            +
              class Span
         | 
| 7 | 
            +
                attr_accessor :operation_name
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                attr_reader :context, :start_time, :end_time, :references, :tags, :logs
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                # Creates a new {Span}
         | 
| 12 | 
            +
                #
         | 
| 13 | 
            +
                # @param context [SpanContext] the context of the span
         | 
| 14 | 
            +
                # @param operation_name [String] the operation name
         | 
| 15 | 
            +
                # @param reporter [#report] span reporter
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # @return [Span] a new Span
         | 
| 18 | 
            +
                def initialize(context, operation_name, reporter, start_time: Time.now, references: [], tags: {})
         | 
| 19 | 
            +
                  @context = context
         | 
| 20 | 
            +
                  @operation_name = operation_name
         | 
| 21 | 
            +
                  @reporter = reporter
         | 
| 22 | 
            +
                  @start_time = start_time
         | 
| 23 | 
            +
                  @references = references
         | 
| 24 | 
            +
                  @tags = []
         | 
| 25 | 
            +
                  @logs = []
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  tags.each { |key, value| set_tag(key, value) }
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                # Set a tag value on this span
         | 
| 31 | 
            +
                #
         | 
| 32 | 
            +
                # @param key [String] the key of the tag
         | 
| 33 | 
            +
                # @param value [String, Numeric, Boolean] the value of the tag. If it's not
         | 
| 34 | 
            +
                # a String, Numeric, or Boolean it will be encoded with to_s
         | 
| 35 | 
            +
                def set_tag(key, value)
         | 
| 36 | 
            +
                  if key == 'sampling.priority'
         | 
| 37 | 
            +
                    if value.to_i.positive?
         | 
| 38 | 
            +
                      return self if @context.debug?
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                      @context.flags = @context.flags | SpanContext::Flags::SAMPLED | SpanContext::Flags::DEBUG
         | 
| 41 | 
            +
                    else
         | 
| 42 | 
            +
                      @context.flags = @context.flags & ~SpanContext::Flags::SAMPLED
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
                    return self
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  # Using Thrift::Tag to avoid unnecessary memory allocations
         | 
| 48 | 
            +
                  @tags << ThriftTagBuilder.build(key, value)
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  self
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                # Set a baggage item on the span
         | 
| 54 | 
            +
                #
         | 
| 55 | 
            +
                # @param key [String] the key of the baggage item
         | 
| 56 | 
            +
                # @param value [String] the value of the baggage item
         | 
| 57 | 
            +
                def set_baggage_item(key, value)
         | 
| 58 | 
            +
                  @context.set_baggage_item(key, value)
         | 
| 59 | 
            +
                  self
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                # Get a baggage item
         | 
| 63 | 
            +
                #
         | 
| 64 | 
            +
                # @param key [String] the key of the baggage item
         | 
| 65 | 
            +
                #
         | 
| 66 | 
            +
                # @return Value of the baggage item
         | 
| 67 | 
            +
                def get_baggage_item(key)
         | 
| 68 | 
            +
                  @context.get_baggage_item(key)
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                # Add a log entry to this span
         | 
| 72 | 
            +
                #
         | 
| 73 | 
            +
                # @deprecated Use {#log_kv} instead.
         | 
| 74 | 
            +
                def log(...)
         | 
| 75 | 
            +
                  warn 'Span#log is deprecated. Please use Span#log_kv instead.'
         | 
| 76 | 
            +
                  log_kv(...)
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                # Add a log entry to this span
         | 
| 80 | 
            +
                #
         | 
| 81 | 
            +
                # @param timestamp [Time] time of the log
         | 
| 82 | 
            +
                # @param fields [Hash] Additional information to log
         | 
| 83 | 
            +
                def log_kv(timestamp: Time.now, **fields)
         | 
| 84 | 
            +
                  # Using Thrift::Log to avoid unnecessary memory allocations
         | 
| 85 | 
            +
                  @logs << ThriftLogBuilder.build(timestamp, fields)
         | 
| 86 | 
            +
                  nil
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                # Finish the {Span}
         | 
| 90 | 
            +
                #
         | 
| 91 | 
            +
                # @param end_time [Time] custom end time, if not now
         | 
| 92 | 
            +
                def finish(end_time: Time.now)
         | 
| 93 | 
            +
                  @end_time = end_time
         | 
| 94 | 
            +
                  @reporter.report(self)
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
              end
         | 
| 97 | 
            +
            end
         | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Jaeger
         | 
| 4 | 
            +
              # SpanContext holds the data for a span that gets inherited to child spans
         | 
| 5 | 
            +
              class SpanContext
         | 
| 6 | 
            +
                module Flags
         | 
| 7 | 
            +
                  NONE = 0x00
         | 
| 8 | 
            +
                  SAMPLED = 0x01
         | 
| 9 | 
            +
                  DEBUG = 0x02
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def self.create_from_parent_context(span_context)
         | 
| 13 | 
            +
                  new(
         | 
| 14 | 
            +
                    trace_id: span_context.trace_id,
         | 
| 15 | 
            +
                    parent_id: span_context.span_id,
         | 
| 16 | 
            +
                    span_id: TraceId.generate,
         | 
| 17 | 
            +
                    flags: span_context.flags,
         | 
| 18 | 
            +
                    baggage: span_context.baggage.dup
         | 
| 19 | 
            +
                  )
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                attr_accessor :flags
         | 
| 23 | 
            +
                attr_reader :span_id, :parent_id, :trace_id, :baggage
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def initialize(span_id:, trace_id:, flags:, parent_id: 0, baggage: {})
         | 
| 26 | 
            +
                  @span_id = span_id
         | 
| 27 | 
            +
                  @parent_id = parent_id
         | 
| 28 | 
            +
                  @trace_id = trace_id
         | 
| 29 | 
            +
                  @baggage = baggage
         | 
| 30 | 
            +
                  @flags = flags
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def sampled?
         | 
| 34 | 
            +
                  @flags & Flags::SAMPLED == Flags::SAMPLED
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def debug?
         | 
| 38 | 
            +
                  @flags & Flags::DEBUG == Flags::DEBUG
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def to_trace_id
         | 
| 42 | 
            +
                  @to_trace_id ||= @trace_id.to_s(16)
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def to_span_id
         | 
| 46 | 
            +
                  @to_span_id ||= @span_id.to_s(16)
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def set_baggage_item(key, value)
         | 
| 50 | 
            +
                  @baggage[key.to_s] = value.to_s
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def get_baggage_item(key)
         | 
| 54 | 
            +
                  @baggage[key.to_s]
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
| @@ -0,0 +1,42 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Jaeger
         | 
| 4 | 
            +
              class ThriftTagBuilder
         | 
| 5 | 
            +
                FIELDS = Jaeger::Thrift::Tag::FIELDS
         | 
| 6 | 
            +
                KEY = FIELDS[Jaeger::Thrift::Tag::KEY].fetch(:name)
         | 
| 7 | 
            +
                VTYPE = FIELDS[Jaeger::Thrift::Tag::VTYPE].fetch(:name)
         | 
| 8 | 
            +
                VLONG = FIELDS[Jaeger::Thrift::Tag::VLONG].fetch(:name)
         | 
| 9 | 
            +
                VDOUBLE = FIELDS[Jaeger::Thrift::Tag::VDOUBLE].fetch(:name)
         | 
| 10 | 
            +
                VBOOL = FIELDS[Jaeger::Thrift::Tag::VBOOL].fetch(:name)
         | 
| 11 | 
            +
                VSTR = FIELDS[Jaeger::Thrift::Tag::VSTR].fetch(:name)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def self.build(key, value)
         | 
| 14 | 
            +
                  case value
         | 
| 15 | 
            +
                  when Integer
         | 
| 16 | 
            +
                    Jaeger::Thrift::Tag.new(
         | 
| 17 | 
            +
                      KEY => key.to_s,
         | 
| 18 | 
            +
                      VTYPE => Jaeger::Thrift::TagType::LONG,
         | 
| 19 | 
            +
                      VLONG => value
         | 
| 20 | 
            +
                    )
         | 
| 21 | 
            +
                  when Float
         | 
| 22 | 
            +
                    Jaeger::Thrift::Tag.new(
         | 
| 23 | 
            +
                      KEY => key.to_s,
         | 
| 24 | 
            +
                      VTYPE => Jaeger::Thrift::TagType::DOUBLE,
         | 
| 25 | 
            +
                      VDOUBLE => value
         | 
| 26 | 
            +
                    )
         | 
| 27 | 
            +
                  when TrueClass, FalseClass
         | 
| 28 | 
            +
                    Jaeger::Thrift::Tag.new(
         | 
| 29 | 
            +
                      KEY => key.to_s,
         | 
| 30 | 
            +
                      VTYPE => Jaeger::Thrift::TagType::BOOL,
         | 
| 31 | 
            +
                      VBOOL => value
         | 
| 32 | 
            +
                    )
         | 
| 33 | 
            +
                  else
         | 
| 34 | 
            +
                    Jaeger::Thrift::Tag.new(
         | 
| 35 | 
            +
                      KEY => key.to_s,
         | 
| 36 | 
            +
                      VTYPE => Jaeger::Thrift::TagType::STRING,
         | 
| 37 | 
            +
                      VSTR => value.to_s
         | 
| 38 | 
            +
                    )
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
            end
         | 
| @@ -0,0 +1,48 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Jaeger
         | 
| 4 | 
            +
              module TraceId
         | 
| 5 | 
            +
                MAX_64BIT_SIGNED_INT = (1 << 63) - 1
         | 
| 6 | 
            +
                MAX_64BIT_UNSIGNED_INT = (1 << 64) - 1
         | 
| 7 | 
            +
                MAX_128BIT_UNSIGNED_INT = (1 << 128) - 1
         | 
| 8 | 
            +
                TRACE_ID_UPPER_BOUND = MAX_64BIT_UNSIGNED_INT + 1
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def self.generate
         | 
| 11 | 
            +
                  rand(TRACE_ID_UPPER_BOUND)
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def self.base16_hex_id_to_uint64(id)
         | 
| 15 | 
            +
                  return nil unless id
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  value = id.to_i(16)
         | 
| 18 | 
            +
                  value > MAX_64BIT_UNSIGNED_INT || value.negative? ? 0 : value
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def self.base16_hex_id_to_uint128(id)
         | 
| 22 | 
            +
                  return nil unless id
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  value = id.to_i(16)
         | 
| 25 | 
            +
                  value > MAX_128BIT_UNSIGNED_INT || value.negative? ? 0 : value
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                # Thrift defines ID fields as i64, which is signed, therefore we convert
         | 
| 29 | 
            +
                # large IDs (> 2^63) to negative longs
         | 
| 30 | 
            +
                def self.uint64_id_to_int64(id)
         | 
| 31 | 
            +
                  id > MAX_64BIT_SIGNED_INT ? id - MAX_64BIT_UNSIGNED_INT - 1 : id
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                # Convert an integer id into a 0 padded hex string.
         | 
| 35 | 
            +
                # If the string is shorter than 16 characters, it will be padded to 16.
         | 
| 36 | 
            +
                # If it is longer than 16 characters, it is padded to 32.
         | 
| 37 | 
            +
                def self.to_hex(id)
         | 
| 38 | 
            +
                  hex_str = id.to_s(16)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  # pad the string with '0's to 16 or 32 characters
         | 
| 41 | 
            +
                  if hex_str.length > 16
         | 
| 42 | 
            +
                    hex_str.rjust(32, '0')
         | 
| 43 | 
            +
                  else
         | 
| 44 | 
            +
                    hex_str.rjust(16, '0')
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
            end
         |