gitlab-labkit 0.0.1
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/lib/labkit.rb +15 -0
- data/lib/labkit/correlation.rb +7 -0
- data/lib/labkit/correlation/correlation_id.rb +42 -0
- data/lib/labkit/logging.rb +7 -0
- data/lib/labkit/logging/sanitizer.rb +26 -0
- data/lib/labkit/tracing.rb +53 -0
- data/lib/labkit/tracing/common.rb +59 -0
- data/lib/labkit/tracing/factory.rb +55 -0
- data/lib/labkit/tracing/grpc_interceptor.rb +41 -0
- data/lib/labkit/tracing/jaeger_factory.rb +82 -0
- data/lib/labkit/tracing/rack_middleware.rb +40 -0
- data/lib/labkit/tracing/rails/action_view_subscriber.rb +68 -0
- data/lib/labkit/tracing/rails/active_record_subscriber.rb +50 -0
- data/lib/labkit/tracing/rails/rails_common.rb +26 -0
- data/lib/labkit/tracing/sidekiq/client_middleware.rb +25 -0
- data/lib/labkit/tracing/sidekiq/server_middleware.rb +21 -0
- data/lib/labkit/tracing/sidekiq/sidekiq_common.rb +22 -0
- metadata +229 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: b0e98b5b882ecd840a8fd9cc638c6c6c8802249ac7a8c501b6fba56deb1170dc
         | 
| 4 | 
            +
              data.tar.gz: 46c8894fc4cd432bc07a08c64f3090976fa74476d21b1bf482ead09c93cb27f8
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: d8c5fd9a8309b00b3c62652b62fbac2112dc023a5061d85731127c519ba6cb77c475ff405142cac74fb9d5eb30239b17c0d23978998ca8dca79f4f352b950897
         | 
| 7 | 
            +
              data.tar.gz: b7fb1d3b8c683295a58054192f1326ce985714698991c7e9f304631cb3ba4b0fd92e49b282f587ad9396b7f1846e0550cd9278fa992e254dc447bb898b81d208
         | 
    
        data/lib/labkit.rb
    ADDED
    
    | @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Labkit
         | 
| 4 | 
            +
              autoload :Correlation, 'labkit/correlation'
         | 
| 5 | 
            +
              autoload :Tracing, 'labkit/tracing'
         | 
| 6 | 
            +
              autoload :Logging, 'labkit/logging'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def self.process_name
         | 
| 9 | 
            +
                return 'sidekiq' if Sidekiq.server?
         | 
| 10 | 
            +
                return 'console' if defined?(Rails::Console)
         | 
| 11 | 
            +
                return 'test' if Rails.env.test?
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                'web'
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| @@ -0,0 +1,42 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Labkit
         | 
| 4 | 
            +
              module Correlation
         | 
| 5 | 
            +
                module CorrelationId
         | 
| 6 | 
            +
                  LOG_KEY = 'correlation_id'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  class << self
         | 
| 9 | 
            +
                    def use_id(correlation_id, &blk)
         | 
| 10 | 
            +
                      # always generate a id if null is passed
         | 
| 11 | 
            +
                      correlation_id ||= new_id
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                      ids.push(correlation_id || new_id)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                      begin
         | 
| 16 | 
            +
                        yield(current_id)
         | 
| 17 | 
            +
                      ensure
         | 
| 18 | 
            +
                        ids.pop
         | 
| 19 | 
            +
                      end
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    def current_id
         | 
| 23 | 
            +
                      ids.last
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    def current_or_new_id
         | 
| 27 | 
            +
                      current_id || new_id
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    private
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    def ids
         | 
| 33 | 
            +
                      Thread.current[:correlation_id] ||= []
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    def new_id
         | 
| 37 | 
            +
                      SecureRandom.uuid
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
            end
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Labkit
         | 
| 4 | 
            +
              module Logging
         | 
| 5 | 
            +
                class Sanitizer
         | 
| 6 | 
            +
                  ALLOWED_SCHEMES = %w[http https ssh git].freeze
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def self.sanitize_field(content)
         | 
| 9 | 
            +
                    regexp = URI::DEFAULT_PARSER.make_regexp(ALLOWED_SCHEMES)
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    content.gsub(regexp) { |url| masked_url(url) }
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def self.masked_url(url)
         | 
| 15 | 
            +
                    url = url.to_s.strip
         | 
| 16 | 
            +
                    url = Addressable::URI.parse(url)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    url.password = '*****' if url.password.present?
         | 
| 19 | 
            +
                    url.user = '*****' if url.user.present?
         | 
| 20 | 
            +
                    url.to_s
         | 
| 21 | 
            +
                  rescue Addressable::URI::InvalidURIError
         | 
| 22 | 
            +
                    ''
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
| @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'active_support/all'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Labkit
         | 
| 6 | 
            +
              module Tracing
         | 
| 7 | 
            +
                autoload :Common, 'labkit/tracing/common'
         | 
| 8 | 
            +
                autoload :Factory, 'labkit/tracing/factory'
         | 
| 9 | 
            +
                autoload :GRPCInterceptor, 'labkit/tracing/grpc_interceptor'
         | 
| 10 | 
            +
                autoload :JaegerFactory, 'labkit/tracing/jaeger_factory'
         | 
| 11 | 
            +
                autoload :RackMiddleware, 'labkit/tracing/rack_middleware'
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                module Rails
         | 
| 14 | 
            +
                  autoload :ActionViewSubscriber, 'labkit/tracing/rails/action_view_subscriber.rb'
         | 
| 15 | 
            +
                  autoload :ActiveRecordSubscriber, 'labkit/tracing/rails/active_record_subscriber.rb'
         | 
| 16 | 
            +
                  autoload :RailsCommon, 'labkit/tracing/rails/rails_common'
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                module Sidekiq
         | 
| 20 | 
            +
                  autoload :ClientMiddleware, 'labkit/tracing/sidekiq/client_middleware'
         | 
| 21 | 
            +
                  autoload :ServerMiddleware, 'labkit/tracing/sidekiq/server_middleware'
         | 
| 22 | 
            +
                  autoload :SidekiqCommon, 'labkit/tracing/sidekiq/sidekiq_common'
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                # Only enable tracing when the `GITLAB_TRACING` env var is configured. Note that we avoid using ApplicationSettings since
         | 
| 26 | 
            +
                # the same environment variable needs to be configured for Workhorse, Gitaly and any other components which
         | 
| 27 | 
            +
                # emit tracing. Since other components may start before Rails, and may not have access to ApplicationSettings,
         | 
| 28 | 
            +
                # an env var makes more sense.
         | 
| 29 | 
            +
                def self.enabled?
         | 
| 30 | 
            +
                  connection_string.present?
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def self.connection_string
         | 
| 34 | 
            +
                  ENV['GITLAB_TRACING']
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def self.tracing_url_template
         | 
| 38 | 
            +
                  ENV['GITLAB_TRACING_URL']
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def self.tracing_url_enabled?
         | 
| 42 | 
            +
                  enabled? && tracing_url_template.present?
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                # This will provide a link into the distributed tracing for the current trace,
         | 
| 46 | 
            +
                # if it has been captured.
         | 
| 47 | 
            +
                def self.tracing_url
         | 
| 48 | 
            +
                  return unless tracing_url_enabled?
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  tracing_url_template.to_s % { correlation_id: Labkit::Correlation::CorrelationId.current_id.to_s, service: Labkit.process_name }
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
| @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'opentracing'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Labkit
         | 
| 6 | 
            +
              module Tracing
         | 
| 7 | 
            +
                module Common
         | 
| 8 | 
            +
                  def tracer
         | 
| 9 | 
            +
                    OpenTracing.global_tracer
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # Convience method for running a block with a span
         | 
| 13 | 
            +
                  def in_tracing_span(operation_name:, tags:, child_of: nil)
         | 
| 14 | 
            +
                    scope = tracer.start_active_span(operation_name, child_of: child_of, tags: tags)
         | 
| 15 | 
            +
                    span = scope.span
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    # Add correlation details to the span if we have them
         | 
| 18 | 
            +
                    correlation_id = Labkit::Correlation::CorrelationId.current_id
         | 
| 19 | 
            +
                    span.set_tag('correlation_id', correlation_id) if correlation_id
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    begin
         | 
| 22 | 
            +
                      yield span
         | 
| 23 | 
            +
                    rescue StandardError => e
         | 
| 24 | 
            +
                      log_exception_on_span(span, e)
         | 
| 25 | 
            +
                      raise e
         | 
| 26 | 
            +
                    ensure
         | 
| 27 | 
            +
                      scope.close
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def postnotify_span(operation_name, start_time, end_time, tags: nil, child_of: nil, exception: nil)
         | 
| 32 | 
            +
                    span = OpenTracing.start_span(operation_name, start_time: start_time, tags: tags, child_of: child_of)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    log_exception_on_span(span, exception) if exception
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    span.finish(end_time: end_time)
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def log_exception_on_span(span, exception)
         | 
| 40 | 
            +
                    span.set_tag('error', true)
         | 
| 41 | 
            +
                    span.log_kv(kv_tags_for_exception(exception))
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def kv_tags_for_exception(exception)
         | 
| 45 | 
            +
                    case exception
         | 
| 46 | 
            +
                    when Exception
         | 
| 47 | 
            +
                      {
         | 
| 48 | 
            +
                        :"event" => 'error',
         | 
| 49 | 
            +
                        :"error.kind" => exception.class.to_s,
         | 
| 50 | 
            +
                        :"message" => Labkit::Logging::Sanitizer.sanitize_field(exception.message),
         | 
| 51 | 
            +
                        :"stack" => exception.backtrace&.join('\n')
         | 
| 52 | 
            +
                      }
         | 
| 53 | 
            +
                    else
         | 
| 54 | 
            +
                      { :"event" => 'error', :"error.kind" => exception.class.to_s, :"error.object" => Labkit::Logging::Sanitizer.sanitize_field(exception.to_s) }
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
            end
         | 
| @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'cgi'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Labkit
         | 
| 6 | 
            +
              module Tracing
         | 
| 7 | 
            +
                class Factory
         | 
| 8 | 
            +
                  OPENTRACING_SCHEME = 'opentracing'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def self.create_tracer(service_name, connection_string)
         | 
| 11 | 
            +
                    return unless connection_string.present?
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    begin
         | 
| 14 | 
            +
                      opentracing_details = parse_connection_string(connection_string)
         | 
| 15 | 
            +
                      driver_name = opentracing_details[:driver_name]
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                      case driver_name
         | 
| 18 | 
            +
                      when 'jaeger'
         | 
| 19 | 
            +
                        JaegerFactory.create_tracer(service_name, opentracing_details[:options])
         | 
| 20 | 
            +
                      else
         | 
| 21 | 
            +
                        raise "Unknown driver: #{driver_name}"
         | 
| 22 | 
            +
                      end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                      # Can't create the tracer? Warn and continue sans tracer
         | 
| 25 | 
            +
                    rescue StandardError => e
         | 
| 26 | 
            +
                      warn "Unable to instantiate tracer: #{e}"
         | 
| 27 | 
            +
                      nil
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def self.parse_connection_string(connection_string)
         | 
| 32 | 
            +
                    parsed = URI.parse(connection_string)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    raise 'Invalid tracing connection string' unless valid_uri?(parsed)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    { driver_name: parsed.host, options: parse_query(parsed.query) }
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                  private_class_method :parse_connection_string
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  def self.parse_query(query)
         | 
| 41 | 
            +
                    return {} unless query
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    CGI.parse(query).symbolize_keys.transform_values(&:first)
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                  private_class_method :parse_query
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  def self.valid_uri?(uri)
         | 
| 48 | 
            +
                    return false unless uri
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    uri.scheme == OPENTRACING_SCHEME && uri.host.to_s =~ /^[a-z0-9_]+$/ && uri.path.empty?
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                  private_class_method :valid_uri?
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
            end
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'opentracing'
         | 
| 4 | 
            +
            require 'grpc'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Labkit
         | 
| 7 | 
            +
              module Tracing
         | 
| 8 | 
            +
                class GRPCInterceptor < GRPC::ClientInterceptor
         | 
| 9 | 
            +
                  include Common
         | 
| 10 | 
            +
                  include Singleton
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def request_response(request:, call:, method:, metadata:)
         | 
| 13 | 
            +
                    wrap_with_tracing(method, 'unary', metadata) { yield }
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def client_streamer(requests:, call:, method:, metadata:)
         | 
| 17 | 
            +
                    wrap_with_tracing(method, 'client_stream', metadata) { yield }
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def server_streamer(request:, call:, method:, metadata:)
         | 
| 21 | 
            +
                    wrap_with_tracing(method, 'server_stream', metadata) { yield }
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def bidi_streamer(requests:, call:, method:, metadata:)
         | 
| 25 | 
            +
                    wrap_with_tracing(method, 'bidi_stream', metadata) { yield }
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  private
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def wrap_with_tracing(method, grpc_type, metadata)
         | 
| 31 | 
            +
                    tags = { 'component' => 'grpc', 'span.kind' => 'client', 'grpc.method' => method, 'grpc.type' => grpc_type }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    in_tracing_span(operation_name: "grpc:#{method}", tags: tags) do |span|
         | 
| 34 | 
            +
                      OpenTracing.inject(span.context, OpenTracing::FORMAT_TEXT_MAP, metadata)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                      yield
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| @@ -0,0 +1,82 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'jaeger/client'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Labkit
         | 
| 6 | 
            +
              module Tracing
         | 
| 7 | 
            +
                class JaegerFactory
         | 
| 8 | 
            +
                  # When the probabilistic sampler is used, by default 0.1% of requests will be traced
         | 
| 9 | 
            +
                  DEFAULT_PROBABILISTIC_RATE = 0.001
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  # The default port for the Jaeger agent UDP listener
         | 
| 12 | 
            +
                  DEFAULT_UDP_PORT = 6831
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  # Reduce this from default of 10 seconds as the Ruby jaeger
         | 
| 15 | 
            +
                  # client doesn't have overflow control, leading to very large
         | 
| 16 | 
            +
                  # messages which fail to send over UDP (max packet = 64k)
         | 
| 17 | 
            +
                  # Flush more often, with smaller packets
         | 
| 18 | 
            +
                  FLUSH_INTERVAL = 5
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def self.create_tracer(service_name, options)
         | 
| 21 | 
            +
                    kwargs = {
         | 
| 22 | 
            +
                      service_name: service_name,
         | 
| 23 | 
            +
                      sampler: get_sampler(options[:sampler], options[:sampler_param]),
         | 
| 24 | 
            +
                      reporter: get_reporter(service_name, options[:http_endpoint], options[:udp_endpoint])
         | 
| 25 | 
            +
                    }
         | 
| 26 | 
            +
                      .compact
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    extra_params = options.except(:sampler, :sampler_param, :http_endpoint, :udp_endpoint, :strict_parsing, :debug)
         | 
| 29 | 
            +
                    if extra_params.present?
         | 
| 30 | 
            +
                      message = "jaeger tracer: invalid option: #{extra_params.keys.join(', ')}"
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                      raise message if options[:strict_parsing]
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                      warn message
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    Jaeger::Client.build(kwargs)
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  def self.get_sampler(sampler_type, sampler_param)
         | 
| 41 | 
            +
                    case sampler_type
         | 
| 42 | 
            +
                    when 'probabilistic'
         | 
| 43 | 
            +
                      sampler_rate = sampler_param ? sampler_param.to_f : DEFAULT_PROBABILISTIC_RATE
         | 
| 44 | 
            +
                      Jaeger::Samplers::Probabilistic.new(rate: sampler_rate)
         | 
| 45 | 
            +
                    when 'const'
         | 
| 46 | 
            +
                      const_value = sampler_param == '1'
         | 
| 47 | 
            +
                      Jaeger::Samplers::Const.new(const_value)
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
                  private_class_method :get_sampler
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  def self.get_reporter(service_name, http_endpoint, udp_endpoint)
         | 
| 53 | 
            +
                    encoder = Jaeger::Encoders::ThriftEncoder.new(service_name: service_name)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    if http_endpoint.present?
         | 
| 56 | 
            +
                      sender = get_http_sender(encoder, http_endpoint)
         | 
| 57 | 
            +
                    elsif udp_endpoint.present?
         | 
| 58 | 
            +
                      sender = get_udp_sender(encoder, udp_endpoint)
         | 
| 59 | 
            +
                    else
         | 
| 60 | 
            +
                      return nil
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    Jaeger::Reporters::RemoteReporter.new(sender: sender, flush_interval: FLUSH_INTERVAL)
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                  private_class_method :get_reporter
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  def self.get_http_sender(encoder, address)
         | 
| 68 | 
            +
                    Jaeger::HttpSender.new(url: address, encoder: encoder, logger: Logger.new(STDOUT))
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                  private_class_method :get_http_sender
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  def self.get_udp_sender(encoder, address)
         | 
| 73 | 
            +
                    pair = address.split(':', 2)
         | 
| 74 | 
            +
                    host = pair[0]
         | 
| 75 | 
            +
                    port = pair[1] ? pair[1].to_i : DEFAULT_UDP_PORT
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    Jaeger::UdpSender.new(host: host, port: port, encoder: encoder, logger: Logger.new(STDOUT))
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
                  private_class_method :get_udp_sender
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
            end
         | 
| @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'opentracing'
         | 
| 4 | 
            +
            require 'rails'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Labkit
         | 
| 7 | 
            +
              module Tracing
         | 
| 8 | 
            +
                class RackMiddleware
         | 
| 9 | 
            +
                  include Common
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  REQUEST_METHOD = 'REQUEST_METHOD'
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def initialize(app)
         | 
| 14 | 
            +
                    @app = app
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def call(env)
         | 
| 18 | 
            +
                    method = env[REQUEST_METHOD]
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    context = tracer.extract(OpenTracing::FORMAT_RACK, env)
         | 
| 21 | 
            +
                    tags = { 'component' => 'rack', 'span.kind' => 'server', 'http.method' => method, 'http.url' => self.class.build_sanitized_url_from_env(env) }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    in_tracing_span(operation_name: "http:#{method}", child_of: context, tags: tags) do |span|
         | 
| 24 | 
            +
                      @app.call(env).tap { |status_code, _headers, _body| span.set_tag('http.status_code', status_code) }
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  # Generate a sanitized (safe) request URL from the rack environment
         | 
| 29 | 
            +
                  def self.build_sanitized_url_from_env(env)
         | 
| 30 | 
            +
                    request = ::ActionDispatch::Request.new(env)
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    original_url = request.original_url
         | 
| 33 | 
            +
                    uri = URI.parse(original_url)
         | 
| 34 | 
            +
                    uri.query = request.filtered_parameters.to_query if uri.query.present?
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    uri.to_s
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Labkit
         | 
| 4 | 
            +
              module Tracing
         | 
| 5 | 
            +
                module Rails
         | 
| 6 | 
            +
                  class ActionViewSubscriber
         | 
| 7 | 
            +
                    include RailsCommon
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    COMPONENT_TAG = 'ActionView'
         | 
| 10 | 
            +
                    RENDER_TEMPLATE_NOTIFICATION_TOPIC = 'render_template.action_view'
         | 
| 11 | 
            +
                    RENDER_COLLECTION_NOTIFICATION_TOPIC = 'render_collection.action_view'
         | 
| 12 | 
            +
                    RENDER_PARTIAL_NOTIFICATION_TOPIC = 'render_partial.action_view'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    # Instruments Rails ActionView events for opentracing.
         | 
| 15 | 
            +
                    # Returns a lambda, which, when called will unsubscribe from the notifications
         | 
| 16 | 
            +
                    def self.instrument
         | 
| 17 | 
            +
                      subscriber = new
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      subscriptions = [
         | 
| 20 | 
            +
                        ActiveSupport::Notifications.subscribe(RENDER_TEMPLATE_NOTIFICATION_TOPIC) do |_, start, finish, _, payload|
         | 
| 21 | 
            +
                          subscriber.notify_render_template(start, finish, payload)
         | 
| 22 | 
            +
                        end,
         | 
| 23 | 
            +
                        ActiveSupport::Notifications.subscribe(RENDER_COLLECTION_NOTIFICATION_TOPIC) do |_, start, finish, _, payload|
         | 
| 24 | 
            +
                          subscriber.notify_render_collection(start, finish, payload)
         | 
| 25 | 
            +
                        end,
         | 
| 26 | 
            +
                        ActiveSupport::Notifications.subscribe(RENDER_PARTIAL_NOTIFICATION_TOPIC) do |_, start, finish, _, payload|
         | 
| 27 | 
            +
                          subscriber.notify_render_partial(start, finish, payload)
         | 
| 28 | 
            +
                        end
         | 
| 29 | 
            +
                      ]
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                      create_unsubscriber subscriptions
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    # For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
         | 
| 35 | 
            +
                    def notify_render_template(start, finish, payload)
         | 
| 36 | 
            +
                      generate_span_for_notification('render_template', start, finish, payload, tags_for_render_template(payload))
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    def notify_render_collection(start, finish, payload)
         | 
| 40 | 
            +
                      generate_span_for_notification('render_collection', start, finish, payload, tags_for_render_collection(payload))
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    def notify_render_partial(start, finish, payload)
         | 
| 44 | 
            +
                      generate_span_for_notification('render_partial', start, finish, payload, tags_for_render_partial(payload))
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    private
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    def tags_for_render_template(payload)
         | 
| 50 | 
            +
                      { 'component' => COMPONENT_TAG, 'template.id' => payload[:identifier], 'template.layout' => payload[:layout] }
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    def tags_for_render_collection(payload)
         | 
| 54 | 
            +
                      {
         | 
| 55 | 
            +
                        'component' => COMPONENT_TAG,
         | 
| 56 | 
            +
                        'template.id' => payload[:identifier],
         | 
| 57 | 
            +
                        'template.count' => payload[:count] || 0,
         | 
| 58 | 
            +
                        'template.cache.hits' => payload[:cache_hits] || 0
         | 
| 59 | 
            +
                      }
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    def tags_for_render_partial(payload)
         | 
| 63 | 
            +
                      { 'component' => COMPONENT_TAG, 'template.id' => payload[:identifier] }
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
            end
         | 
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Labkit
         | 
| 4 | 
            +
              module Tracing
         | 
| 5 | 
            +
                module Rails
         | 
| 6 | 
            +
                  class ActiveRecordSubscriber
         | 
| 7 | 
            +
                    include RailsCommon
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    ACTIVE_RECORD_NOTIFICATION_TOPIC = 'sql.active_record'
         | 
| 10 | 
            +
                    OPERATION_NAME_PREFIX = 'active_record:'
         | 
| 11 | 
            +
                    DEFAULT_OPERATION_NAME = 'sqlquery'
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    # Instruments Rails ActiveRecord events for opentracing.
         | 
| 14 | 
            +
                    # Returns a lambda, which, when called will unsubscribe from the notifications
         | 
| 15 | 
            +
                    def self.instrument
         | 
| 16 | 
            +
                      subscriber = new
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      subscription =
         | 
| 19 | 
            +
                        ActiveSupport::Notifications.subscribe(ACTIVE_RECORD_NOTIFICATION_TOPIC) do |_, start, finish, _, payload|
         | 
| 20 | 
            +
                          subscriber.notify(start, finish, payload)
         | 
| 21 | 
            +
                        end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                      create_unsubscriber [subscription]
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    # For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
         | 
| 27 | 
            +
                    def notify(start, finish, payload)
         | 
| 28 | 
            +
                      generate_span_for_notification(notification_name(payload), start, finish, payload, tags_for_notification(payload))
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    private
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    def notification_name(payload)
         | 
| 34 | 
            +
                      OPERATION_NAME_PREFIX + (payload[:name].presence || DEFAULT_OPERATION_NAME)
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    def tags_for_notification(payload)
         | 
| 38 | 
            +
                      {
         | 
| 39 | 
            +
                        'component' => 'ActiveRecord',
         | 
| 40 | 
            +
                        'span.kind' => 'client',
         | 
| 41 | 
            +
                        'db.type' => 'sql',
         | 
| 42 | 
            +
                        'db.connection_id' => payload[:connection_id],
         | 
| 43 | 
            +
                        'db.cached' => payload[:cached] || false,
         | 
| 44 | 
            +
                        'db.statement' => payload[:sql]
         | 
| 45 | 
            +
                      }
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'active_support/all'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Labkit
         | 
| 6 | 
            +
              module Tracing
         | 
| 7 | 
            +
                module Rails
         | 
| 8 | 
            +
                  module RailsCommon
         | 
| 9 | 
            +
                    extend ActiveSupport::Concern
         | 
| 10 | 
            +
                    include Labkit::Tracing::Common
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    class_methods do
         | 
| 13 | 
            +
                      def create_unsubscriber(subscriptions)
         | 
| 14 | 
            +
                        -> { subscriptions.each { |subscriber| ActiveSupport::Notifications.unsubscribe(subscriber) } }
         | 
| 15 | 
            +
                      end
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    def generate_span_for_notification(operation_name, start, finish, payload, tags)
         | 
| 19 | 
            +
                      exception = payload[:exception]
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      postnotify_span(operation_name, start, finish, tags: tags, exception: exception)
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'opentracing'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Labkit
         | 
| 6 | 
            +
              module Tracing
         | 
| 7 | 
            +
                module Sidekiq
         | 
| 8 | 
            +
                  class ClientMiddleware
         | 
| 9 | 
            +
                    include SidekiqCommon
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    SPAN_KIND = 'client'
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def call(worker_class, job, queue, redis_pool)
         | 
| 14 | 
            +
                      in_tracing_span(operation_name: "sidekiq:#{job['class']}", tags: tags_from_job(job, SPAN_KIND)) do |span|
         | 
| 15 | 
            +
                        # Inject the details directly into the job
         | 
| 16 | 
            +
                        tracer
         | 
| 17 | 
            +
                          .inject(span.context, OpenTracing::FORMAT_TEXT_MAP, job)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                        yield
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'opentracing'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Labkit
         | 
| 6 | 
            +
              module Tracing
         | 
| 7 | 
            +
                module Sidekiq
         | 
| 8 | 
            +
                  class ServerMiddleware
         | 
| 9 | 
            +
                    include SidekiqCommon
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    SPAN_KIND = 'server'
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def call(worker, job, queue)
         | 
| 14 | 
            +
                      context = tracer.extract(OpenTracing::FORMAT_TEXT_MAP, job)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                      in_tracing_span(operation_name: "sidekiq:#{job['class']}", child_of: context, tags: tags_from_job(job, SPAN_KIND)) { |span| yield }
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Labkit
         | 
| 4 | 
            +
              module Tracing
         | 
| 5 | 
            +
                module Sidekiq
         | 
| 6 | 
            +
                  module SidekiqCommon
         | 
| 7 | 
            +
                    include Labkit::Tracing::Common
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    def tags_from_job(job, kind)
         | 
| 10 | 
            +
                      {
         | 
| 11 | 
            +
                        'component' => 'sidekiq',
         | 
| 12 | 
            +
                        'span.kind' => kind,
         | 
| 13 | 
            +
                        'sidekiq.queue' => job['queue'],
         | 
| 14 | 
            +
                        'sidekiq.jid' => job['jid'],
         | 
| 15 | 
            +
                        'sidekiq.retry' => job['retry'].to_s,
         | 
| 16 | 
            +
                        'sidekiq.args' => job['args']&.join(', ')
         | 
| 17 | 
            +
                      }
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,229 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: gitlab-labkit
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.0.1
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Andrew Newdigate
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2019-02-18 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: activesupport
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '5'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '5'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: grpc
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '1.16'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '1.16'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: jaeger-client
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - "~>"
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '0.10'
         | 
| 48 | 
            +
              type: :runtime
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - "~>"
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '0.10'
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: opentracing
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - "~>"
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '0.4'
         | 
| 62 | 
            +
              type: :runtime
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - "~>"
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: '0.4'
         | 
| 69 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 70 | 
            +
              name: rack
         | 
| 71 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 72 | 
            +
                requirements:
         | 
| 73 | 
            +
                - - "~>"
         | 
| 74 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 75 | 
            +
                    version: '2.0'
         | 
| 76 | 
            +
              type: :runtime
         | 
| 77 | 
            +
              prerelease: false
         | 
| 78 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
                requirements:
         | 
| 80 | 
            +
                - - "~>"
         | 
| 81 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                    version: '2.0'
         | 
| 83 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 84 | 
            +
              name: rails
         | 
| 85 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 86 | 
            +
                requirements:
         | 
| 87 | 
            +
                - - "~>"
         | 
| 88 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 89 | 
            +
                    version: '5.0'
         | 
| 90 | 
            +
              type: :runtime
         | 
| 91 | 
            +
              prerelease: false
         | 
| 92 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 93 | 
            +
                requirements:
         | 
| 94 | 
            +
                - - "~>"
         | 
| 95 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 96 | 
            +
                    version: '5.0'
         | 
| 97 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 98 | 
            +
              name: gitlab-styles
         | 
| 99 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 100 | 
            +
                requirements:
         | 
| 101 | 
            +
                - - "~>"
         | 
| 102 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 103 | 
            +
                    version: '2.4'
         | 
| 104 | 
            +
              type: :development
         | 
| 105 | 
            +
              prerelease: false
         | 
| 106 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 107 | 
            +
                requirements:
         | 
| 108 | 
            +
                - - "~>"
         | 
| 109 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 110 | 
            +
                    version: '2.4'
         | 
| 111 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 112 | 
            +
              name: rake
         | 
| 113 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 114 | 
            +
                requirements:
         | 
| 115 | 
            +
                - - "~>"
         | 
| 116 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 117 | 
            +
                    version: '12.3'
         | 
| 118 | 
            +
              type: :development
         | 
| 119 | 
            +
              prerelease: false
         | 
| 120 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 121 | 
            +
                requirements:
         | 
| 122 | 
            +
                - - "~>"
         | 
| 123 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 124 | 
            +
                    version: '12.3'
         | 
| 125 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 126 | 
            +
              name: rspec
         | 
| 127 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 128 | 
            +
                requirements:
         | 
| 129 | 
            +
                - - "~>"
         | 
| 130 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 131 | 
            +
                    version: 3.6.0
         | 
| 132 | 
            +
              type: :development
         | 
| 133 | 
            +
              prerelease: false
         | 
| 134 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 135 | 
            +
                requirements:
         | 
| 136 | 
            +
                - - "~>"
         | 
| 137 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 138 | 
            +
                    version: 3.6.0
         | 
| 139 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 140 | 
            +
              name: rspec-parameterized
         | 
| 141 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 142 | 
            +
                requirements:
         | 
| 143 | 
            +
                - - "~>"
         | 
| 144 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 145 | 
            +
                    version: '0.4'
         | 
| 146 | 
            +
              type: :development
         | 
| 147 | 
            +
              prerelease: false
         | 
| 148 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 149 | 
            +
                requirements:
         | 
| 150 | 
            +
                - - "~>"
         | 
| 151 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 152 | 
            +
                    version: '0.4'
         | 
| 153 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 154 | 
            +
              name: rubocop
         | 
| 155 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 156 | 
            +
                requirements:
         | 
| 157 | 
            +
                - - "~>"
         | 
| 158 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 159 | 
            +
                    version: 0.54.0
         | 
| 160 | 
            +
              type: :development
         | 
| 161 | 
            +
              prerelease: false
         | 
| 162 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 163 | 
            +
                requirements:
         | 
| 164 | 
            +
                - - "~>"
         | 
| 165 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 166 | 
            +
                    version: 0.54.0
         | 
| 167 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 168 | 
            +
              name: rubocop-rspec
         | 
| 169 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 170 | 
            +
                requirements:
         | 
| 171 | 
            +
                - - "~>"
         | 
| 172 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 173 | 
            +
                    version: 1.22.1
         | 
| 174 | 
            +
              type: :development
         | 
| 175 | 
            +
              prerelease: false
         | 
| 176 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 177 | 
            +
                requirements:
         | 
| 178 | 
            +
                - - "~>"
         | 
| 179 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 180 | 
            +
                    version: 1.22.1
         | 
| 181 | 
            +
            description: 
         | 
| 182 | 
            +
            email:
         | 
| 183 | 
            +
            - andrew@gitlab.com
         | 
| 184 | 
            +
            executables: []
         | 
| 185 | 
            +
            extensions: []
         | 
| 186 | 
            +
            extra_rdoc_files: []
         | 
| 187 | 
            +
            files:
         | 
| 188 | 
            +
            - lib/labkit.rb
         | 
| 189 | 
            +
            - lib/labkit/correlation.rb
         | 
| 190 | 
            +
            - lib/labkit/correlation/correlation_id.rb
         | 
| 191 | 
            +
            - lib/labkit/logging.rb
         | 
| 192 | 
            +
            - lib/labkit/logging/sanitizer.rb
         | 
| 193 | 
            +
            - lib/labkit/tracing.rb
         | 
| 194 | 
            +
            - lib/labkit/tracing/common.rb
         | 
| 195 | 
            +
            - lib/labkit/tracing/factory.rb
         | 
| 196 | 
            +
            - lib/labkit/tracing/grpc_interceptor.rb
         | 
| 197 | 
            +
            - lib/labkit/tracing/jaeger_factory.rb
         | 
| 198 | 
            +
            - lib/labkit/tracing/rack_middleware.rb
         | 
| 199 | 
            +
            - lib/labkit/tracing/rails/action_view_subscriber.rb
         | 
| 200 | 
            +
            - lib/labkit/tracing/rails/active_record_subscriber.rb
         | 
| 201 | 
            +
            - lib/labkit/tracing/rails/rails_common.rb
         | 
| 202 | 
            +
            - lib/labkit/tracing/sidekiq/client_middleware.rb
         | 
| 203 | 
            +
            - lib/labkit/tracing/sidekiq/server_middleware.rb
         | 
| 204 | 
            +
            - lib/labkit/tracing/sidekiq/sidekiq_common.rb
         | 
| 205 | 
            +
            homepage: http://about.gitlab.com
         | 
| 206 | 
            +
            licenses:
         | 
| 207 | 
            +
            - MIT
         | 
| 208 | 
            +
            metadata: {}
         | 
| 209 | 
            +
            post_install_message: 
         | 
| 210 | 
            +
            rdoc_options: []
         | 
| 211 | 
            +
            require_paths:
         | 
| 212 | 
            +
            - lib
         | 
| 213 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 214 | 
            +
              requirements:
         | 
| 215 | 
            +
              - - ">="
         | 
| 216 | 
            +
                - !ruby/object:Gem::Version
         | 
| 217 | 
            +
                  version: '0'
         | 
| 218 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 219 | 
            +
              requirements:
         | 
| 220 | 
            +
              - - ">="
         | 
| 221 | 
            +
                - !ruby/object:Gem::Version
         | 
| 222 | 
            +
                  version: '0'
         | 
| 223 | 
            +
            requirements: []
         | 
| 224 | 
            +
            rubyforge_project: 
         | 
| 225 | 
            +
            rubygems_version: 2.7.8
         | 
| 226 | 
            +
            signing_key: 
         | 
| 227 | 
            +
            specification_version: 4
         | 
| 228 | 
            +
            summary: Instrumentation for GitLab
         | 
| 229 | 
            +
            test_files: []
         |