Module: RailsOtelContext::FrameContext

Defined in:
lib/rails_otel_context/frame_context.rb

Overview

Thread-local storage for explicitly pushed call-frame context.

The default code.namespace / code.function attributes are extracted by walking the Ruby call stack on every span start. That works, but costs O(stack depth) object allocations per span. FrameContext eliminates the walk by letting call sites push their class+method once at entry:

RailsOtelContext::FrameContext.with_frame(class_name: 'OrdersController',
                                          method_name: 'create') do
  # every span created inside here reads the pushed frame — no stack walk
end

The Railtie automatically installs an around_action that pushes the controller frame for all controller actions. For jobs, service objects, or any other code that creates spans, use with_frame directly or include RailsOtelContext::Frameable.

The pushed frame is a fallback for the span processor: the stack walk still runs when no frame is pushed, so existing behavior is preserved.

Class Method Summary collapse

Class Method Details

.currentObject

Returns the currently pushed frame hash, or nil.



51
52
53
# File 'lib/rails_otel_context/frame_context.rb', line 51

def current
  Thread.current[FRAME_KEY]
end

.popObject Also known as: clear!

Clears the pushed frame. Pair with push in an ensure block.



46
47
48
# File 'lib/rails_otel_context/frame_context.rb', line 46

def pop
  Thread.current[FRAME_KEY] = nil
end

.push(class_name:, method_name:, filepath: nil, lineno: nil) ⇒ Object

Manual push without a block. Caller must call pop in an ensure.



41
42
43
# File 'lib/rails_otel_context/frame_context.rb', line 41

def push(class_name:, method_name:, filepath: nil, lineno: nil)
  Thread.current[FRAME_KEY] = build_frame(class_name, method_name, filepath, lineno)
end

.with_frame(class_name:, method_name:, filepath: nil, lineno: nil) ⇒ Object

Pushes class_name/method_name for the duration of the block, restoring whatever was pushed before (supports nesting). Optional filepath: and lineno: are carried through to the span processor so DB adapter call-site info survives the span lifecycle.



32
33
34
35
36
37
38
# File 'lib/rails_otel_context/frame_context.rb', line 32

def with_frame(class_name:, method_name:, filepath: nil, lineno: nil)
  prev = Thread.current[FRAME_KEY]
  Thread.current[FRAME_KEY] = build_frame(class_name, method_name, filepath, lineno)
  yield
ensure
  Thread.current[FRAME_KEY] = prev
end