Module: Pigeon::Tracing::Core

Defined in:
lib/pigeon/tracing/core.rb

Overview

Core tracing functionality

Class Method Summary collapse

Class Method Details

.available?Boolean

Check if OpenTelemetry is available

Returns:

  • (Boolean)

    Whether OpenTelemetry is available



9
10
11
# File 'lib/pigeon/tracing/core.rb', line 9

def self.available?
  defined?(OpenTelemetry)
end

.extract_context(headers) ⇒ OpenTelemetry::Context?

Extract trace context from headers

Parameters:

  • headers (Hash)

    Headers containing trace context

Returns:

  • (OpenTelemetry::Context, nil)

    Extracted context or nil if not available



70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/pigeon/tracing/core.rb', line 70

def self.extract_context(headers)
  return nil unless available?
  return nil unless headers

  # Convert header keys to lowercase for case-insensitive matching
  normalized_headers = {}
  headers.each do |k, v|
    normalized_headers[k.to_s.downcase] = v
  end

  carrier = OpenTelemetry::Context::Propagation::Rack::HeadersGetter.new(normalized_headers)
  OpenTelemetry.propagation.extract(carrier)
end

.init(service_name: "pigeon", exporter: nil) ⇒ Boolean

Initialize OpenTelemetry tracing

Parameters:

  • service_name (String) (defaults to: "pigeon")

    Service name for traces

  • exporter (OpenTelemetry::Exporter::OTLP::Exporter, nil) (defaults to: nil)

    Optional custom exporter

Returns:

  • (Boolean)

    Whether initialization was successful



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/pigeon/tracing/core.rb', line 17

def self.init(service_name: "pigeon", exporter: nil)
  return false unless available?

  begin
    # Configure the SDK with default exporters
    OpenTelemetry::SDK.configure do |c|
      c.service_name = service_name
      c.use_all # Use all available instrumentation
      c.add_span_processor(
        OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
          exporter || OpenTelemetry::Exporter::OTLP::Exporter.new
        )
      )
    end

    true
  rescue StandardError => e
    Pigeon.config.logger.error("Failed to initialize OpenTelemetry: #{e.message}")
    false
  end
end

.inject_context(headers = {}, context = nil) ⇒ Hash

Inject trace context into headers

Parameters:

  • headers (Hash) (defaults to: {})

    Headers to inject trace context into

  • context (OpenTelemetry::Context, nil) (defaults to: nil)

    Context to inject (defaults to current)

Returns:

  • (Hash)

    Headers with injected trace context



88
89
90
91
92
93
94
95
96
# File 'lib/pigeon/tracing/core.rb', line 88

def self.inject_context(headers = {}, context = nil)
  return headers unless available?

  headers ||= {}
  context ||= OpenTelemetry::Context.current
  carrier = OpenTelemetry::Context::Propagation::Rack::HeadersSetter.new(headers)
  OpenTelemetry.propagation.inject(carrier, context: context)
  headers
end

.span_kind_from_symbol(kind) ⇒ Integer

Convert a symbol to an OpenTelemetry span kind

Parameters:

  • kind (Symbol)

    Span kind symbol

Returns:

  • (Integer)

    OpenTelemetry span kind



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/pigeon/tracing/core.rb', line 101

def self.span_kind_from_symbol(kind)
  return OpenTelemetry::Trace::SpanKind::INTERNAL unless available?

  case kind
  when :server
    OpenTelemetry::Trace::SpanKind::SERVER
  when :client
    OpenTelemetry::Trace::SpanKind::CLIENT
  when :producer
    OpenTelemetry::Trace::SpanKind::PRODUCER
  when :consumer
    OpenTelemetry::Trace::SpanKind::CONSUMER
  else
    OpenTelemetry::Trace::SpanKind::INTERNAL
  end
end

.tracer(name = "pigeon") ⇒ OpenTelemetry::Tracer?

Get the OpenTelemetry tracer

Parameters:

  • name (String) (defaults to: "pigeon")

    Tracer name

Returns:

  • (OpenTelemetry::Tracer, nil)

    Tracer or nil if OpenTelemetry is not available



42
43
44
45
46
# File 'lib/pigeon/tracing/core.rb', line 42

def self.tracer(name = "pigeon")
  return nil unless available?

  OpenTelemetry.tracer_provider.tracer(name)
end

.with_span(name, attributes: {}, kind: :internal, parent_context: nil) { ... } ⇒ Object

Create a span for a block of code

Parameters:

  • name (String)

    Span name

  • attributes (Hash) (defaults to: {})

    Span attributes

  • kind (Symbol) (defaults to: :internal)

    Span kind (:internal, :server, :client, :producer, :consumer)

  • parent_context (OpenTelemetry::Context, nil) (defaults to: nil)

    Optional parent context

Yields:

  • Block to execute within the span

Returns:

  • (Object)

    Result of the block



55
56
57
58
59
60
61
62
63
64
65
# File 'lib/pigeon/tracing/core.rb', line 55

def self.with_span(name, attributes: {}, kind: :internal, parent_context: nil, &)
  return yield unless available?

  tracer = self.tracer
  return yield unless tracer

  parent_context ||= OpenTelemetry::Context.current
  span_kind = span_kind_from_symbol(kind)

  tracer.in_span(name, attributes: attributes, kind: span_kind, with_parent: parent_context, &)
end