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", assembly_id: "little_ghost.assembly.id", assembly_kind: "little_ghost.assembly.kind", 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", assembly_step: "execute_assembly_step", tool: "execute_tool", workflow: "invoke_workflow", swarm: "invoke_swarm", graph: "invoke_graph", assembly: "invoke_assembly" }.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.
81 82 83 84 85 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 81 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.
98 99 100 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 98 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.
93 94 95 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 93 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.
135 136 137 138 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 135 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.
141 142 143 144 145 146 147 148 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 141 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.
88 89 90 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 88 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.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 103 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.
120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/little_ghost/tracing/open_telemetry.rb', line 120 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 |