Class: Errorgap::SpanCollector

Inherits:
Object
  • Object
show all
Defined in:
lib/errorgap/span_collector.rb

Constant Summary collapse

THREAD_KEY =
:errorgap_spans
NORMALIZE_PATTERN =

Quoted strings and standalone numeric literals → ?

/
  '(?:[^'\\]|\\.)*'   # single-quoted string literals
  |
  \b\d+(?:\.\d+)?\b   # integer and float literals
/x
SKIP_NAMES =
%w[SCHEMA EXPLAIN CACHE].freeze

Class Method Summary collapse

Class Method Details

.flushObject



55
56
57
58
59
# File 'lib/errorgap/span_collector.rb', line 55

def flush
  spans = Thread.current[THREAD_KEY] || []
  Thread.current[THREAD_KEY] = nil
  spans
end

.installObject

Call once at boot (from Railtie). Subscribers only record while a transaction is being collected (the middleware starts one per request when APM is enabled), so installing unconditionally is safe — and required, since app initializers that enable APM run after railtie initializers.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/errorgap/span_collector.rb', line 22

def install
  return unless defined?(ActiveSupport::Notifications)
  return if @installed

  @installed = true

  ActiveSupport::Notifications.subscribe("sql.active_record") do |*, payload|
    next unless Thread.current[THREAD_KEY]
    next if SKIP_NAMES.any? { |n| payload[:name]&.start_with?(n) }
    next if payload[:cached]

    duration_ms = payload[:duration] || 0.0
    sql = normalize_sql(payload[:sql].to_s)

    store << Span.new(kind: "db", sql: sql, duration_ms: duration_ms.round(3))
  end

  # Rails reports view_runtime with database time already subtracted, so
  # this does not double count the db spans above.
  ActiveSupport::Notifications.subscribe("process_action.action_controller") do |*, payload|
    next unless Thread.current[THREAD_KEY]

    view_ms = payload[:view_runtime]
    next unless view_ms&.positive?

    store << Span.new(kind: "view", duration_ms: view_ms.round(3))
  end
end

.normalize_sql(sql) ⇒ Object



65
66
67
# File 'lib/errorgap/span_collector.rb', line 65

def normalize_sql(sql)
  sql.gsub(NORMALIZE_PATTERN, "?").gsub(/\s+/, " ").strip
end

.startObject



51
52
53
# File 'lib/errorgap/span_collector.rb', line 51

def start
  Thread.current[THREAD_KEY] = []
end

.storeObject



61
62
63
# File 'lib/errorgap/span_collector.rb', line 61

def store
  Thread.current[THREAD_KEY] ||= []
end