Class: Tracelit::ErrorSpanProcessor
- Inherits:
-
Object
- Object
- Tracelit::ErrorSpanProcessor
- Defined in:
- lib/tracelit/error_span_processor.rb
Overview
ErrorSpanProcessor ensures error spans are always exported regardless of the sampling decision made at span creation time.
How it works:
-
ErrorAlwaysOnSampler returns RECORD_ONLY (not DROP) for unsampled spans, which ensures this processor’s on_finish is called for every span
-
On span finish, if the span has status ERROR, this processor forces it through the exporter directly, bypassing the BatchSpanProcessor
-
BatchSpanProcessor ignores RECORD_ONLY spans (trace_flags.sampled? false) so there is no double-export for sampled error spans
NOTE: opentelemetry-sdk 1.x uses on_finish (not on_end) as the hook name.
Important: this processor must never block application threads. Exporting an unsampled error span synchronously in on_finish can block request / console threads when the ingest endpoint is slow or retrying. We enqueue span data into a bounded in-memory queue and export on a background worker thread.
Constant Summary collapse
- QUEUE_CAPACITY =
512- SHUTDOWN_SENTINEL =
Object.new
Instance Method Summary collapse
- #force_flush(timeout: nil) ⇒ Object
-
#initialize(exporter) ⇒ ErrorSpanProcessor
constructor
A new instance of ErrorSpanProcessor.
- #on_finish(span) ⇒ Object
- #on_start(_span, _parent_context) ⇒ Object
- #shutdown(timeout: nil) ⇒ Object
Constructor Details
#initialize(exporter) ⇒ ErrorSpanProcessor
Returns a new instance of ErrorSpanProcessor.
25 26 27 28 29 30 31 32 33 34 |
# File 'lib/tracelit/error_span_processor.rb', line 25 def initialize(exporter) @exporter = exporter @queue = SizedQueue.new(QUEUE_CAPACITY) @shutdown = false @worker = Thread.new do Thread.current[:tracelit_error_export_worker] = true worker_loop end @worker.abort_on_exception = false end |
Instance Method Details
#force_flush(timeout: nil) ⇒ Object
54 55 56 57 |
# File 'lib/tracelit/error_span_processor.rb', line 54 def force_flush(timeout: nil) wait_for_queue_drain(timeout) @exporter.force_flush(timeout: timeout) end |
#on_finish(span) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/tracelit/error_span_processor.rb', line 40 def on_finish(span) # Skip spans that are not in error — only intervene for errors return if span.status.ok? # Skip spans that were fully sampled — BatchSpanProcessor handles those. # This prevents double-export of error spans on traces that were sampled. return if span.context.trace_flags.sampled? # Queue for background export; never block the caller. enqueue(span.to_span_data) rescue StandardError # Never let processor errors propagate to the application end |
#on_start(_span, _parent_context) ⇒ Object
36 37 38 |
# File 'lib/tracelit/error_span_processor.rb', line 36 def on_start(_span, _parent_context) # nothing to do at start end |
#shutdown(timeout: nil) ⇒ Object
59 60 61 62 63 64 65 66 |
# File 'lib/tracelit/error_span_processor.rb', line 59 def shutdown(timeout: nil) return if @shutdown @shutdown = true enqueue_shutdown_signal @worker&.join(timeout || 1) # Do not shut down the shared exporter here — # the BatchSpanProcessor owns its lifecycle end |