Class: AwesomeExplain::Subscribers::ActiveRecordSubscriber
- Inherits:
-
ActiveSupport::LogSubscriber
- Object
- ActiveSupport::LogSubscriber
- AwesomeExplain::Subscribers::ActiveRecordSubscriber
- Defined in:
- lib/awesome_explain/subscribers/active_record_subscriber.rb
Instance Method Summary collapse
- #controller_data ⇒ Object
- #db_explain_enabled?(sql) ⇒ Boolean
- #db_logging_enbled? ⇒ Boolean
- #ddm_query?(sql) ⇒ Boolean
- #extract_delayed_job_jid(args) ⇒ Object
- #extract_sidekiq_jid(args) ⇒ Object
- #extract_sql_operation(sql) ⇒ Object
- #extract_table_name_and_schema(sql) ⇒ Object
- #reduce_table_and_schema(matched) ⇒ Object
- #resolve_controller_id ⇒ Object
- #resolve_delayed_job_class_id ⇒ Object
- #resolve_sidekiq_class_id ⇒ Object
- #resolve_source_name ⇒ Object
- #resolve_stracktrace_id(stacktrace) ⇒ Object
- #sql(event) ⇒ Object
- #track_sql(event) ⇒ Object
Instance Method Details
#controller_data ⇒ Object
81 82 83 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 81 def controller_data Thread.current['ae_controller_data'] end |
#db_explain_enabled?(sql) ⇒ Boolean
145 146 147 148 149 150 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 145 def db_explain_enabled?(sql) # return true if Thread.current['ae_analyze'] # return false if Rails.const_defined?('Console') return false if ddm_query?(sql) true end |
#db_logging_enbled? ⇒ Boolean
152 153 154 155 156 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 152 def db_logging_enbled? # return true if Thread.current['ae_analyze'] return false if Rails.const_defined?('Console') true end |
#ddm_query?(sql) ⇒ Boolean
72 73 74 75 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 72 def ddm_query?(sql) matched = sql.match(/INSERT|DELETE|UPDATE/) matched.present? && matched[0].present? end |
#extract_delayed_job_jid(args) ⇒ Object
89 90 91 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 89 def extract_delayed_job_jid(args) Thread.current[:delayed_job]&.id end |
#extract_sidekiq_jid(args) ⇒ Object
85 86 87 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 85 def extract_sidekiq_jid(args) Thread.current[:sidekiq_job].dig('jid') end |
#extract_sql_operation(sql) ⇒ Object
158 159 160 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 158 def extract_sql_operation(sql) sql.match(/SELECT|INSERT|DELETE|UPDATE/)[0] end |
#extract_table_name_and_schema(sql) ⇒ Object
162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 162 def extract_table_name_and_schema(sql) matched = sql.match(/FROM\s+(\"\w+\")\.?(\"\w+\")?/) return reduce_table_and_schema(matched) if matched && matched[1].present? matched = sql.match(/INSERT INTO\s+(\"\w+\")\.?(\"\w+\")?/) return reduce_table_and_schema(matched) if matched && matched[1].present? matched = sql.match(/UPDATE\s+(\"\w+\")\.?(\"\w+\")?/) return reduce_table_and_schema(matched) if matched && matched[1].present? # matched = sql.match(/DELETE FROM\s+(\"\w+\")\.?(\"\w+\")?/) # return reduce_table_and_schema(matched) if matched && matched[1].present? end |
#reduce_table_and_schema(matched) ⇒ Object
176 177 178 179 180 181 182 183 184 185 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 176 def reduce_table_and_schema(matched) if matched[1].present? table_name = matched[2].nil? ? matched[1] : matched[2] table_name = table_name.gsub(/\"/, '') schema_name = matched[2].nil? ? 'public' : matched[1] schema_name = schema_name.gsub(/\"/, '') return [table_name, schema_name] end end |
#resolve_controller_id ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 106 def resolve_controller_id data = controller_data return nil unless data.present? ::AwesomeExplain::Controller.find_or_create_by({ name: controller_data[:controller], action: controller_data[:action], path: controller_data[:path], params: controller_data[:params].to_json, session_id: Thread.current['ae_session_id'] }).id end |
#resolve_delayed_job_class_id ⇒ Object
132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 132 def resolve_delayed_job_class_id return unless Thread.current[:delayed_worker_class].present? delayed_worker_class_str = Thread.current[:delayed_worker_class] delayed_job_queue_str = Thread.current[:delayed_job_queue].to_s delayed_job_worker = ::AwesomeExplain::DelayedJob.find_or_create_by({ job: delayed_worker_class_str, jid: extract_delayed_job_jid(Thread.current[:delayed_job]), params: Thread.current[:delayed_job].present? ? Thread.current[:delayed_job].to_json : {} }) delayed_job_worker.id end |
#resolve_sidekiq_class_id ⇒ Object
118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 118 def resolve_sidekiq_class_id return unless Thread.current[:sidekiq_worker_class].present? sidekiq_worker_class_str = Thread.current[:sidekiq_worker_class] sidekiq_queue_str = Thread.current[:sidekiq_queue].to_s sidekiq_worker = ::AwesomeExplain::SidekiqWorker.find_or_create_by({ worker: sidekiq_worker_class_str, queue: sidekiq_queue_str, jid: extract_sidekiq_jid(Thread.current[:sidekiq_job]), params: Thread.current[:sidekiq_job].present? ? Thread.current[:sidekiq_job].to_json : {} }) sidekiq_worker.id end |
#resolve_source_name ⇒ Object
77 78 79 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 77 def resolve_source_name Thread.current['ae_source'] || DEFAULT_SOURCE_NAME end |
#resolve_stracktrace_id(stacktrace) ⇒ Object
93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 93 def resolve_stracktrace_id(stacktrace) stacktrace_str = stacktrace .select {|c| c =~ /^#{::AwesomeExplain::Config.instance.rails_path + '\/(lib|app|db)\/'}/ } .map {|c| c.gsub Rails.root.to_s, ''} .to_json stacktrace = ::AwesomeExplain::Stacktrace.find_or_create_by({ stacktrace: stacktrace_str }) stacktrace.id end |
#sql(event) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 3 def sql(event) if track_sql(event) && db_logging_enbled? sql = event.payload[:sql] begin table_name_and_schema = extract_table_name_and_schema(sql) table_name = table_name_and_schema.first schema_name = table_name_and_schema.last request_id = event.payload[:connection_id] binds = event.payload[:binds] cached = event.payload[:name] == 'CACHE' operation = extract_sql_operation(sql) name = event.payload[:name] stacktrace = caller ActiveRecord::Base.transaction do explain = nil if db_explain_enabled?(sql)# && !cached connection_id = event.payload[:connection_id] connection = ::AwesomeExplain::Config.instance.connection explain_uuid = SecureRandom.uuid explain = connection.raw_connection.exec("EXPLAIN (ANALYZE true, COSTS true, FORMAT json) #{sql}") #explain = connection.raw_connection.exec_prepared("ae_#{explain_uuid}", binds).to_a explain = explain.map { |h| h.values.first }.join("\n") explain = ::AwesomeExplain::SqlExplain.new({ explain_output: explain, stacktrace_id: resolve_stracktrace_id(stacktrace), controller_id: resolve_controller_id, }) explain.save end sql_query = { table_name: table_name, schema_name: schema_name, app_name: ::AwesomeExplain::Config.instance.app_name, source_name: resolve_source_name, duration: event.duration, query: sql, binds: binds.to_json, cached: cached, name: name, operation: operation, session_id: Thread.current[:ae_session_id], stacktrace_id: resolve_stracktrace_id(stacktrace), sql_explain_id: explain&.id, controller_id: resolve_controller_id, sidekiq_worker_id: resolve_sidekiq_class_id, delayed_job_id: resolve_delayed_job_class_id, } ::AwesomeExplain::SqlQuery.create(sql_query) end rescue => exception logger.warn sql logger.warn exception.to_s logger.warn exception.backtrace[0..5] end end end |
#track_sql(event) ⇒ Object
64 65 66 67 68 69 70 |
# File 'lib/awesome_explain/subscribers/active_record_subscriber.rb', line 64 def track_sql(event) return false if event.payload[:connection].class.name == 'ActiveRecord::ConnectionAdapters::SQLite3Adapter' sql = event.payload[:sql] !sql.match(/EXPLAIN|SAVEPOINT|nextval|CREATE|BEGIN|COMMIT|ROLLBACK|begin|commit|rollback|ar_|sql_|pg_|explain|logs|controllers|stacktraces|schema_migrations|delayed_jobs/) && sql.strip == sql && event.payload[:name] != 'SCHEMA' end |