Class: LittleGhost::Tracing::OpenTelemetry
- Inherits:
-
Instrumentation::Subscriber
- Object
- Instrumentation::Subscriber
- LittleGhost::Tracing::OpenTelemetry
- Defined in:
- lib/little_ghost/tracing/open_telemetry.rb
Overview
OpenTelemetry turns LittleGhost lifecycle notifications into GenAI spans and events. It brings agents, model calls, tools, workflows, and token usage into the same traces as the rest of an application.
LittleGhost.configure do |config|
config.instrument LittleGhost::Tracing::OpenTelemetry.new
end
LittleGhost depends only on opentelemetry-api. Install and configure the
desired SDK, processors, and exporters before registering this subscriber.
Content and trust
Prompts, responses, messages, tool arguments, and exception content are omitted by default. They appear only when Instrumentation has an explicit, scrubbed Support::ContentCapture policy.
Constant Summary collapse
- ATTRIBUTE_NAMES =
{ agent_id: "gen_ai.agent.id", agent_name: "gen_ai.agent.name", model_id: "gen_ai.request.model", model_provider: "gen_ai.provider.name", response_id: "gen_ai.response.id", response_model: "gen_ai.response.model", finish_reasons: "gen_ai.response.finish_reasons", input_tokens: "gen_ai.usage.input_tokens", output_tokens: "gen_ai.usage.output_tokens", cache_read_tokens: "gen_ai.usage.cache_read.input_tokens", cache_write_tokens: "gen_ai.usage.cache_creation.input_tokens", reasoning_tokens: "gen_ai.usage.reasoning.output_tokens", time_to_first_token: "gen_ai.response.time_to_first_chunk", session_id: "gen_ai.conversation.id", tool_name: "gen_ai.tool.name", tool_type: "gen_ai.tool.type", tool_call_id: "gen_ai.tool.call.id", workflow_name: "gen_ai.workflow.name", http_response_status_code: "http.response.status_code", error_class: "error.type", error_type: "error.type" }.freeze
- OPERATIONS =
:nodoc:
{ agent: "invoke_agent", agent_turn: "agent_turn", model: "chat", run: "invoke_agent", runtime: "runtime_startup", session_store: "session_store", subagent: "invoke_agent", tool: "execute_tool", workflow: "invoke_workflow" }.freeze
- REQUEST_SETTING_ATTRIBUTES =
:nodoc:
{ frequency_penalty: "gen_ai.request.frequency_penalty", max_tokens: "gen_ai.request.max_tokens", max_output_tokens: "gen_ai.request.max_tokens", presence_penalty: "gen_ai.request.presence_penalty", seed: "gen_ai.request.seed", temperature: "gen_ai.request.temperature", top_k: "gen_ai.request.top_k", top_p: "gen_ai.request.top_p" }.freeze
- CONTENT_KEYS =
:nodoc:
%w[arguments content input input_text message output output_text prompt response text].freeze
- SENSITIVE_KEY =
:nodoc:
/(authorization|api[_-]?key|credential|password|secret|(?:^|[_-])token(?:$|[_-])|cookie|private[_-]?key)/i- MAX_ATTRIBUTE_LENGTH =
:nodoc:
1_024
Instance Method Summary collapse
-
#emit(name, attributes) ⇒ Object
Adds a structured event to the correlated active span.
-
#finish(name, attributes) ⇒ Object
Finishes the span correlated by operation ID.
-
#flush(timeout: nil) ⇒ Object
Flushes through the configured tracer provider when supported.
-
#initialize(tracer: nil) ⇒ OpenTelemetry
constructor
Uses
traceror the global tracer provider. -
#shutdown(timeout: nil) ⇒ Object
Finishes spans still owned by this subscriber.
-
#start(name, attributes) ⇒ Object
Starts a span for a lifecycle operation.
-
#trace_context(operation_id: nil) ⇒ Object
Supplies W3C propagation fields for an active operation when known.
-
#with_span(name, attributes:, parent_operation_id: nil) ⇒ Object
Wraps a block in a standalone internal span.
Constructor Details
#initialize(tracer: nil) ⇒ OpenTelemetry
Uses tracer or the global tracer provider.
75 76 77 78 79 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 75 def initialize(tracer: nil) @tracer = tracer @entries = {} @mutex = Mutex.new end |
Instance Method Details
#emit(name, attributes) ⇒ Object
Adds a structured event to the correlated active span.
92 93 94 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 92 def emit(name, attributes) add_event(name.to_sym, prepare_attributes(:emit, name.to_sym, attributes)) end |
#finish(name, attributes) ⇒ Object
Finishes the span correlated by operation ID.
87 88 89 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 87 def finish(name, attributes) finish_span(name.to_sym, prepare_attributes(:finish, name.to_sym, attributes)) end |
#flush(timeout: nil) ⇒ Object
Flushes through the configured tracer provider when supported.
129 130 131 132 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 129 def flush(timeout: nil) provider = ::OpenTelemetry.tracer_provider provider.force_flush(timeout:) if provider.respond_to?(:force_flush) end |
#shutdown(timeout: nil) ⇒ Object
Finishes spans still owned by this subscriber.
135 136 137 138 139 140 141 142 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 135 def shutdown(timeout: nil) spans = @mutex.synchronize do current = @entries.values.map { |entry| entry.fetch(:span) }.uniq @entries = {} current end spans.each(&:finish) end |
#start(name, attributes) ⇒ Object
Starts a span for a lifecycle operation.
82 83 84 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 82 def start(name, attributes) start_span(name.to_sym, prepare_attributes(:start, name.to_sym, attributes)) end |
#trace_context(operation_id: nil) ⇒ Object
Supplies W3C propagation fields for an active operation when known.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 97 def trace_context(operation_id: nil, **) span = @mutex.synchronize { @entries.dig(operation_id, :span) } context = span&.context return {} unless context&.valid? carrier = {} trace_context_propagator.inject( carrier, context: ::OpenTelemetry::Trace.context_with_span(span) ) carrier.slice("traceparent", "tracestate").merge(trace_id: context.hex_trace_id) rescue context&.valid? ? {trace_id: context.hex_trace_id} : {} end |
#with_span(name, attributes:, parent_operation_id: nil) ⇒ Object
Wraps a block in a standalone internal span. Prefer lifecycle Instrumentation methods for normal framework operations.
114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 114 def with_span(name, attributes:, parent_operation_id: nil) parent = @mutex.synchronize { @entries[parent_operation_id] } parent_context = parent && ::OpenTelemetry::Trace.context_with_span(parent.fetch(:span)) = {kind: :internal, attributes:} [:with_parent] = parent_context if parent_context span = tracer.start_span(name, **) yield span rescue => error span.status = ::OpenTelemetry::Trace::Status.error(error.class.name) if span raise ensure span&.finish end |