Module: MetrixWire::Instrument::ActiveSupportSubscriber
- Defined in:
- lib/metrixwire/instrument/active_support.rb
Overview
Subscribes to ActiveSupport::Notifications for the idiomatic, reliable Rails path: SQL queries, controller actions (route refinement + errors), and cache reads/writes. Each subscriber is guarded and never throws.
Constant Summary collapse
- SCHEMA_SQL =
/\A\s*(BEGIN|COMMIT|ROLLBACK|SAVEPOINT|RELEASE|SET|SHOW|PRAGMA)\b/i.freeze
Class Method Summary collapse
- .cache_op(name, payload) ⇒ Object
-
.event_of(args) ⇒ Object
Build an ActiveSupport::Notifications::Event from the raw subscribe args.
- .install ⇒ Object
- .row_count_from_payload(payload) ⇒ Object
-
.subscribe_cache ⇒ Object
Cache reads/writes → custom cache spans.
-
.subscribe_process_action ⇒ Object
Refine the route to controller#action, capture 5xx + exceptions.
-
.subscribe_sql ⇒ Object
db_query spans from sql.active_record.
-
.subscribe_transaction ⇒ Object
ActiveRecord transaction notifications (Rails 7.2+) → transaction spans.
Class Method Details
.cache_op(name, payload) ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/metrixwire/instrument/active_support.rb', line 122 def cache_op(name, payload) case name when "cache_read.active_support" ["read", payload.key?(:hit) ? (payload[:hit] ? true : false) : nil] when "cache_fetch_hit.active_support" ["read", true] when "cache_write.active_support" ["write", nil] when "cache_delete.active_support" ["delete", nil] else [name.split(".").first.sub("cache_", ""), nil] end end |
.event_of(args) ⇒ Object
Build an ActiveSupport::Notifications::Event from the raw subscribe args.
149 150 151 |
# File 'lib/metrixwire/instrument/active_support.rb', line 149 def event_of(args) ::ActiveSupport::Notifications::Event.new(*args) end |
.install ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/metrixwire/instrument/active_support.rb', line 13 def install return unless defined?(::ActiveSupport::Notifications) return if @installed subscribe_sql subscribe_process_action subscribe_cache subscribe_transaction @installed = true rescue StandardError nil end |
.row_count_from_payload(payload) ⇒ Object
137 138 139 140 141 142 143 144 145 146 |
# File 'lib/metrixwire/instrument/active_support.rb', line 137 def row_count_from_payload(payload) row_count = payload[:row_count] return { rowCount: row_count.to_i } if row_count result = payload[:result] n = Helpers.extract_row_count(result) n.nil? ? nil : { rowCount: n } rescue StandardError nil end |
.subscribe_cache ⇒ Object
Cache reads/writes → custom cache spans. hit is known for cache_read.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/metrixwire/instrument/active_support.rb', line 85 def subscribe_cache ::ActiveSupport::Notifications.subscribe(/\Acache_(read|fetch_hit|write|delete)\.active_support\z/) do |*args| begin trace = Context.current next if trace.nil? event = event_of(args) name = event.name.to_s payload = event.payload op, hit = cache_op(name, payload) = { kind: "cache", op: op } [:hit] = hit unless hit.nil? Helpers.add_span("custom", "cache #{op}", event.duration, meta: , source_location: nil) rescue StandardError nil end end end |
.subscribe_process_action ⇒ Object
Refine the route to controller#action, capture 5xx + exceptions.
51 52 53 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 79 80 81 82 |
# File 'lib/metrixwire/instrument/active_support.rb', line 51 def subscribe_process_action ::ActiveSupport::Notifications.subscribe("process_action.action_controller") do |*args| begin trace = Context.current next if trace.nil? payload = event_of(args).payload controller = payload[:controller] action = payload[:action] method = payload[:method] || trace.method if controller && action trace.route = "#{method} #{controller}##{action}" end status = payload[:status].to_i trace.status = "error" if status >= 500 if (ex = payload[:exception_object]) trace.capture_exception(ex) elsif (ex_arr = payload[:exception]) # [class_name, message] trace.(:exception, { type: ex_arr[0].to_s, message: ex_arr[1].to_s }) trace.status = "error" end rescue StandardError nil end end end |
.subscribe_sql ⇒ Object
db_query spans from sql.active_record. rowCount from the result payload when ActiveRecord exposes it.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/metrixwire/instrument/active_support.rb', line 28 def subscribe_sql ::ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| begin trace = Context.current next if trace.nil? event = event_of(args) payload = event.payload name = payload[:name].to_s sql = payload[:sql].to_s # Skip schema/transaction control + AR's own housekeeping queries. next if sql.empty? || SCHEMA_SQL.match?(sql) next if name == "SCHEMA" || name == "TRANSACTION" = row_count_from_payload(payload) Helpers.add_span("db_query", sql, event.duration, meta: , source_location: nil) rescue StandardError nil end end end |
.subscribe_transaction ⇒ Object
ActiveRecord transaction notifications (Rails 7.2+) → transaction spans.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/metrixwire/instrument/active_support.rb', line 105 def subscribe_transaction ::ActiveSupport::Notifications.subscribe("transaction.active_record") do |*args| begin trace = Context.current next if trace.nil? event = event_of(args) Helpers.add_span("custom", "DB transaction", event.duration, meta: { kind: "transaction" }, source_location: nil) rescue StandardError nil end end rescue StandardError nil end |