Module: Foam::Otel::Errors

Defined in:
lib/foam/otel/errors.rb

Overview

Backs the record_exception helper: records onto the active span a standard OTel exception event + ERROR status, deduped per span by exception object identity so recording the same exception twice produces ONE event. Recording an exception must never raise (BASE_PACKAGE_SPEC rule 9); recordException accepts customer error classes and never notifies any vendor tracker (section 0 traces row).

The seen-set lives ON the span object (an instance variable), not in a thread-local: a request span can be recorded on one thread and finished on another (ActionController::Live streams from a separate thread), so a thread-local would miss the dedupe across threads and leak entries. Span-carried state is GC'd with the span and travels with it.

Constant Summary collapse

IVAR =
:@__foam_otel_recorded

Class Method Summary collapse

Class Method Details

.record_once(span, error) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/foam/otel/errors.rb', line 22

def record_once(span, error)
  return false unless span.respond_to?(:context) && span.context.valid?
  return false unless span.respond_to?(:recording?) && span.recording?
  return false unless error.is_a?(Exception)

  seen = span.instance_variable_get(IVAR)
  unless seen
    seen = []
    span.instance_variable_set(IVAR, seen)
  end
  return false if seen.include?(error.object_id)

  seen << error.object_id
  span.record_exception(error)
  span.status = OpenTelemetry::Trace::Status.error(error.message.to_s)
  true
rescue StandardError, SystemStackError
  # SystemStackError is not a StandardError: a customer error whose
  # #message/#to_s recurses unboundedly must never turn "recording an
  # exception" into a host-thread crash (rule 9).
  false
end