Module: Bitfab::Otel::Encoder

Defined in:
lib/bitfab/otel.rb

Overview

Turns finished carrier spans into the OTLP/JSON shapes Bitfab ingests.

Class Method Summary collapse

Class Method Details

.encode_request(envelope, spans) ⇒ Object



628
629
630
# File 'lib/bitfab/otel.rb', line 628

def encode_request(envelope, spans)
  envelope.head + spans.map(&:encoded).join(",") + envelope.tail
end

.encode_span(span) ⇒ Object



614
615
616
617
# File 'lib/bitfab/otel.rb', line 614

def encode_span(span)
  encoded = JSON.generate(span_to_otlp(span))
  EncodedSpan.new(encoded, encoded.bytesize)
end

.error?(payload) ⇒ Boolean

Returns:

  • (Boolean)


679
680
681
682
683
684
685
# File 'lib/bitfab/otel.rb', line 679

def error?(payload)
  raw_span = payload["rawSpan"]
  return true if raw_span.is_a?(Hash) && !raw_span.dig("span_data", "error").nil?

  errors = payload["errors"]
  errors.is_a?(Array) && !errors.empty?
end

.request_envelope(first) ⇒ Object



619
620
621
622
623
624
625
626
# File 'lib/bitfab/otel.rb', line 619

def request_envelope(first)
  scope = first.instrumentation_scope
  resource = JSON.generate({"attributes" => attributes(first.resource.attribute_enumerator.to_h)})
  scope_json = JSON.generate({"name" => scope.name, "version" => scope.version || ""})
  head = %({"resourceSpans":[{"resource":#{resource},"scopeSpans":[{"scope":#{scope_json},"spans":[)
  tail = "]}]}]}"
  RequestEnvelope.new(head, tail, (head + tail).bytesize)
end

.span_name(operation, payload) ⇒ Object



653
654
655
656
657
658
659
660
661
662
# File 'lib/bitfab/otel.rb', line 653

def span_name(operation, payload)
  raw_span = payload["rawSpan"]
  if operation == "external_span" && raw_span.is_a?(Hash)
    name = raw_span.dig("span_data", "name")
    return name if name.is_a?(String)
  end
  return payload["traceFunctionKey"] if payload["traceFunctionKey"].is_a?(String)

  "bitfab.#{operation}"
end

.span_to_otlp(span) ⇒ Object



632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
# File 'lib/bitfab/otel.rb', line 632

def span_to_otlp(span)
  result = {
    "traceId" => span.hex_trace_id,
    "spanId" => span.hex_span_id,
    "name" => span.name,
    "kind" => SPAN_KINDS.fetch(span.kind, 1),
    "startTimeUnixNano" => (span.start_timestamp || 0).to_s,
    "endTimeUnixNano" => (span.end_timestamp || 0).to_s,
    "attributes" => attributes(span.attributes),
    "droppedAttributesCount" => dropped_count(span.total_recorded_attributes, span.attributes),
    "droppedEventsCount" => dropped_count(span.total_recorded_events, span.events),
    "droppedLinksCount" => dropped_count(span.total_recorded_links, span.links),
    "status" => status(span.status),
    "flags" => span.trace_flags.sampled? ? 1 : 0
  }
  result["parentSpanId"] = span.hex_parent_span_id unless span.parent_span_id == INVALID_SPAN_ID
  tracestate = span.tracestate&.to_s
  result["traceState"] = tracestate unless tracestate.nil? || tracestate.empty?
  result
end

.timestamp(payload, field) ⇒ Object



664
665
666
667
668
669
670
671
672
673
674
675
676
677
# File 'lib/bitfab/otel.rb', line 664

def timestamp(payload, field)
  raw = payload.dig("rawSpan", field) if payload["rawSpan"].is_a?(Hash)
  if raw.nil?
    raw_trace = payload["externalTrace"] || payload["rawTrace"]
    raw = raw_trace[field] if raw_trace.is_a?(Hash)
  end
  return Time.now unless raw.is_a?(String)

  begin
    Time.iso8601(raw)
  rescue ArgumentError
    Time.now
  end
end