Class: Phronomy::Tracing::OpenTelemetryTracer

Inherits:
Base
  • Object
show all
Defined in:
lib/phronomy/tracing/open_telemetry_tracer.rb

Overview

OpenTelemetry tracer adapter.

Requires the +opentelemetry-api+ gem (or +opentelemetry-sdk+ for testing). The caller is responsible for configuring the OpenTelemetry SDK before using this tracer — phronomy does not configure an exporter or propagator.

Examples:

Configure globally

require "opentelemetry-sdk"
OpenTelemetry::SDK.configure { |c| c.use_all }

Phronomy.configure do |c|
  c.tracer = Phronomy::Tracing::OpenTelemetryTracer.new
end

Instance Method Summary collapse

Constructor Details

#initialize(tracer_name: "phronomy") ⇒ OpenTelemetryTracer

Returns a new instance of OpenTelemetryTracer.

Parameters:

  • tracer_name (String) (defaults to: "phronomy")

    name passed to the OTel TracerProvider



21
22
23
24
# File 'lib/phronomy/tracing/open_telemetry_tracer.rb', line 21

def initialize(tracer_name: "phronomy")
  require "opentelemetry"
  @otel_tracer = OpenTelemetry.tracer_provider.tracer(tracer_name, Phronomy::VERSION)
end

Instance Method Details

#finish_span(span, output: nil, usage: nil, error: nil) ⇒ Object

Finishes the OTel span, recording output, token usage, or error.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/phronomy/tracing/open_telemetry_tracer.rb', line 40

def finish_span(span, output: nil, usage: nil, error: nil)
  if error
    span.record_exception(error)
    span.status = OpenTelemetry::Trace::Status.error(error.message)
  else
    span.set_attribute("phronomy.output", output.to_s) if output
    if usage
      span.set_attribute("llm.usage.input_tokens", usage.input)
      span.set_attribute("llm.usage.output_tokens", usage.output)
      total = (usage.input || 0) + (usage.output || 0)
      span.set_attribute("llm.usage.total_tokens", total)
    end
  end
  span.finish
end

#start_span(name, input: nil, **attributes) ⇒ OpenTelemetry::Trace::Span

Starts an OTel span. Input and extra metadata are stored as span attributes prefixed with +phronomy.+.

Returns:

  • (OpenTelemetry::Trace::Span)


32
33
34
35
36
37
# File 'lib/phronomy/tracing/open_telemetry_tracer.rb', line 32

def start_span(name, input: nil, **attributes)
  attrs = {}
  attrs["phronomy.input"] = input.to_s if input
  attributes.each { |k, v| attrs["phronomy.#{k}"] = v.to_s }
  @otel_tracer.start_span(name, attributes: attrs)
end

#trace(name, input: nil, **meta) {|span| ... } ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Overrides Base#trace to use OTel +in_span+, which pushes the span onto the OTel Context stack while the block runs. Nested +trace+ calls automatically inherit the current span as their parent, establishing the correct parent/child hierarchy in the trace tree.

Parameters:

  • name (String)

    span name

  • input (Object, nil) (defaults to: nil)

    input to record as a span attribute

  • meta (Hash)

    additional metadata (e.g. task_id, user_id)

Yields:

  • (span)

    the active OTel span

Returns:

  • (Object)

    the block's return value



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/phronomy/tracing/open_telemetry_tracer.rb', line 67

def trace(name, input: nil, **meta)
  attrs = {}
  attrs["phronomy.input"] = input.to_s if input
  meta.each { |k, v| attrs["phronomy.#{k}"] = v.to_s unless v.nil? }

  result = nil
  @otel_tracer.in_span(name, attributes: attrs) do |span|
    result, usage = yield span
    span.set_attribute("phronomy.output", result.to_s) if result
    if usage
      span.set_attribute("llm.usage.input_tokens", usage.input)
      span.set_attribute("llm.usage.output_tokens", usage.output)
      total = (usage.input || 0) + (usage.output || 0)
      span.set_attribute("llm.usage.total_tokens", total)
    end
  rescue => e
    span.record_exception(e)
    span.status = OpenTelemetry::Trace::Status.error(e.message)
    raise
  end
  result
end