Class: Brut::FrontEnd::Handlers::InstrumentationHandler

Inherits:
Brut::FrontEnd::Handler show all
Defined in:
lib/brut/front_end/handlers/instrumentation_handler.rb

Defined Under Namespace

Classes: TraceParent

Constant Summary collapse

Event =
Data.define(:name, :timestamp, :attributes) do
  def self.from_json(json)
    name       = json["name"]
    timestamp  = Time.at(json["timestamp"].to_i / 1000.0)
    attributes = json["attributes"] || {}
    self.new(name:,timestamp:,attributes:)
  end
end
Span =
Data.define(:name,:start_timestamp,:end_timestamp,:attributes,:events,:spans) do
  def self.from_json(json)
    name            = json["name"]
    start_timestamp = Time.at(json["start_timestamp"].to_i / 1000.0)
    end_timestamp   = Time.at(json["end_timestamp"].to_i / 1000.0)
    attributes      = json["attributes"] || {}
    events          = (json["events"] || []).map { Event.from_json(it) }
    spans           = (json["spans"] || []).map { Span.from_json(it) }
    self.new(name:,start_timestamp:,end_timestamp:,attributes:,events:,spans:)
  end

  def self.from_header(header_value)
    if header_value.nil?
      return nil
    end
    if header_value.kind_of?(self)
      return header_value
    end

    # This header can have info for several vendors, delimited by commas. We pick
    # out ours, which has a vendor name 'brut'
    brut_state = header_value.split(/\s*,\s*/).map { it.split(/\s*=\s*/) }.detect { |vendor,_|
      vendor == "brut"
    }[1]

    # Our state is a base-64 encoded JSON blob
    # each key/value separated by a colon.
    json = Base64.decode64(brut_state)

    hash = JSON.parse(json)
    if !hash.kind_of?(Hash)
      SemanticLogger[self.class].info "Got a #{hash.class} and not a Hash"
      return nil
    end
    self.from_json(hash)
  end
end

Instance Method Summary collapse

Methods inherited from Brut::FrontEnd::Handler

#before_handle, #handle!

Methods included from Brut::Framework::Errors

#abstract_method!, #bug!

Methods included from Brut::FrontEnd::HandlingResults

#http_status, #redirect_to

Constructor Details

#initialize(http_tracestate:, http_traceparent:) ⇒ InstrumentationHandler

Returns a new instance of InstrumentationHandler.



68
69
70
71
# File 'lib/brut/front_end/handlers/instrumentation_handler.rb', line 68

def initialize(http_tracestate:, http_traceparent:)
  @http_tracestate  = http_tracestate
  @http_traceparent = http_traceparent
end

Instance Method Details

#handleObject



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/brut/front_end/handlers/instrumentation_handler.rb', line 72

def handle
  traceparent = TraceParent.from_header(@http_traceparent)
  span        = Span.from_header(@http_tracestate)

  if span.nil? || traceparent.nil?
    SemanticLogger[self.class].info "Missing traceparent or span: #{@http_tracestate}, #{@http_traceparent}"
    return http_status(400)
  end

  carrier = traceparent.as_carrier
  propagator = OpenTelemetry::Trace::Propagation::TraceContext::TextMapPropagator.new
  extracted_context = propagator.extract(carrier)
  OpenTelemetry::Context.with_current(extracted_context) do
    record_span(span)
  end
  http_status(200)
end