Class: RailsOtelContext::CallContextProcessor

Inherits:
Object
  • Object
show all
Includes:
SourceLocation
Defined in:
lib/rails_otel_context/call_context_processor.rb

Overview

SpanProcessor that enriches ALL spans with:

- code.namespace / code.function / code.filepath / code.lineno
  (nearest app-code frame from the call stack — automatic, no manual setup)
- rails.controller / rails.action   (when inside a controller action)
- rails.job                         (when inside a job)

Call-context resolution:

1. Explicit override — O(1). If app code calls FrameContext.with_frame (or
   includes Frameable), that frame wins. Use this to intentionally override
   the automatic nearest-frame (e.g., to expose a service boundary rather
   than the inner repo it delegates to).
2. Stack walk — O(stack depth). Default path when no override is active.
   DB adapters (Trilogy, PG, MySQL2, Redis, ClickHouse) additionally overwrite
   code.* post-query from a shallower position, giving exact call-site precision.

rails.* attributes come from RequestContext (thread-local set by Railtie hooks) and are applied unconditionally — no config gate.

Custom attributes (configured via custom_span_attributes) are also applied. The callable must return a Hash (or nil) and must be fast — hot path per span.

Constant Summary collapse

SPAN_CONTROLLER_ATTR =
'rails.controller'
SPAN_ACTION_ATTR =
'rails.action'
SPAN_JOB_ATTR =
'rails.job'
AR_MODEL_ATTR =
'code.activerecord.model'
AR_METHOD_ATTR =
'code.activerecord.method'
AR_SCOPE_ATTR =
'code.activerecord.scope'
AR_QUERY_COUNT_ATTR =
'db.query_count'
AR_ASYNC_ATTR =
'db.async'
ORIG_NAME_ATTR =
'l9.orig.name'

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from SourceLocation

#apply_call_site_to_span, #apply_source_to_span, #call_site_for_app, #source_location_for_app, #with_call_site_frame

Constructor Details

#initialize(app_root:, config: RailsOtelContext.configuration) ⇒ CallContextProcessor

Returns a new instance of CallContextProcessor.



40
41
42
43
44
45
# File 'lib/rails_otel_context/call_context_processor.rb', line 40

def initialize(app_root:, config: RailsOtelContext.configuration)
  @app_root                = app_root.to_s
  @custom_span_attributes  = config.custom_span_attributes
  @span_name_formatter     = config.span_name_formatter
  @slow_query_threshold_ms = config.slow_query_threshold_ms
end

Instance Attribute Details

#app_rootObject (readonly)

Exposed so SourceLocation mixin can use it for the stack-walk path.



38
39
40
# File 'lib/rails_otel_context/call_context_processor.rb', line 38

def app_root
  @app_root
end

Instance Method Details

#force_flushObject

Return Export::SUCCESS (0) so the SDK’s tracer_provider.force_flush/shutdown can safely call results.max across all registered processors without raising ArgumentError when mixing our return value with integer status codes.



83
# File 'lib/rails_otel_context/call_context_processor.rb', line 83

def force_flush(**) = 0

#on_finish(span) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/rails_otel_context/call_context_processor.rb', line 54

def on_finish(span)
  return unless span.respond_to?(:attributes)

  attrs = span.attributes
  return unless attrs&.key?('db.system')

  stash_if_prepare_span(span, attrs)

  return unless @slow_query_threshold_ms

  start_ns = span.start_timestamp
  end_ns   = span.end_timestamp
  return unless start_ns && end_ns

  duration_ms = (end_ns - start_ns) / 1_000_000.0
  return unless duration_ms >= @slow_query_threshold_ms

  # span.recording? is false here — the span has finished and current_span
  # has reverted to the HTTP parent. Write directly to the backing attributes
  # hash so db.slow lands on the actual DB span, not the HTTP parent.
  raw_attrs = span.instance_variable_get(:@attributes)
  raw_attrs.store(ActiveRecordContext::DB_SLOW_ATTR, true) if raw_attrs.respond_to?(:store)
rescue StandardError
  nil
end

#on_start(span, _parent_context) ⇒ Object



47
48
49
50
51
52
# File 'lib/rails_otel_context/call_context_processor.rb', line 47

def on_start(span, _parent_context)
  apply_call_context(span)
  apply_request_context(span)
  apply_db_context(span)
  apply_custom_attributes(span) if @custom_span_attributes
end

#shutdownObject



84
# File 'lib/rails_otel_context/call_context_processor.rb', line 84

def shutdown(**) = 0