Class: RailsOtelContext::ActiveRecordContext::Subscriber

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_otel_context/activerecord_context.rb

Overview

Subscriber for sql.active_record notifications.

Instance Method Summary collapse

Constructor Details

#initializeSubscriber

Returns a new instance of Subscriber.



84
85
86
# File 'lib/rails_otel_context/activerecord_context.rb', line 84

def initialize
  @threshold = RailsOtelContext.configuration.slow_query_threshold_ms
end

Instance Method Details

#finish(_name, id, _payload) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/rails_otel_context/activerecord_context.rb', line 126

def finish(_name, id, _payload)
  if @threshold && Thread.current[TIMING_ID_KEY].equal?(id)
    elapsed_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - Thread.current[TIMING_START_KEY]) * 1000
    Thread.current[TIMING_ID_KEY] = nil
    if elapsed_ms >= @threshold
      span = OpenTelemetry::Trace.current_span
      span.set_attribute(DB_SLOW_ATTR, true) if span.context.valid?
    end
  end
ensure
  Thread.current[THREAD_KEY] = nil
end

#start(_name, id, payload) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/rails_otel_context/activerecord_context.rb', line 88

def start(_name, id, payload)
  ar_name = payload[:name]
  return unless ar_name
  return if ar_name == 'SCHEMA' || ar_name.start_with?('CACHE')

  ctx = if ar_name == 'SQL'
          ActiveRecordContext.parse_sql_context(payload[:sql])
        else
          ActiveRecordContext.parse_ar_name(ar_name)
        end
  return unless ctx

  # Include scope name if one was captured by RelationScopeCapture
  scope = Thread.current[SCOPE_THREAD_KEY]
  ctx[:scope_name] = scope if scope

  query_key = ctx[:query_key]
  counts = (Thread.current[RequestContext::QUERY_COUNT_KEY] ||= {})
  count = (counts[query_key] = (counts[query_key] || 0) + 1)
  ctx[:query_count] = count if count > 1

  ctx[:async] = true if payload[:async]
  Thread.current[THREAD_KEY] = ctx

  if @threshold
    Thread.current[TIMING_ID_KEY]    = id
    Thread.current[TIMING_START_KEY] = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  end

  # Enrich the current span directly. When OTel instruments via driver-level
  # prepend (Trilogy, PG, Mysql2), the span is created BEFORE this notification
  # fires, so CallContextProcessor#on_start sees nil AR context. Applying here
  # fixes those spans after the fact.
  return unless defined?(OpenTelemetry::Trace)

  ActiveRecordContext.apply_to_span(OpenTelemetry::Trace.current_span, ctx)
end