Class: RorVsWild::Client
- Inherits:
-
Object
- Object
- RorVsWild::Client
- Includes:
- Location
- Defined in:
- lib/rorvswild/client.rb
Constant Summary collapse
- IGNORED_QUERIES =
%w[EXPLAIN SCHEMA].freeze
Instance Attribute Summary collapse
-
#api_key ⇒ Object
readonly
Returns the value of attribute api_key.
-
#api_url ⇒ Object
readonly
Returns the value of attribute api_url.
-
#app_id ⇒ Object
readonly
Returns the value of attribute app_id.
-
#app_root ⇒ Object
readonly
Returns the value of attribute app_root.
-
#app_root_regex ⇒ Object
readonly
Returns the value of attribute app_root_regex.
-
#explain_sql_threshold ⇒ Object
readonly
Returns the value of attribute explain_sql_threshold.
-
#ignored_exceptions ⇒ Object
readonly
Returns the value of attribute ignored_exceptions.
-
#threads ⇒ Object
readonly
Returns the value of attribute threads.
Class Method Summary collapse
Instance Method Summary collapse
- #after_exception(exception, controller) ⇒ Object
- #after_http_request(name, start, finish, id, payload) ⇒ Object
- #after_sql_query(name, start, finish, id, payload) ⇒ Object
- #after_view_rendering(name, start, finish, id, payload) ⇒ Object
- #around_active_job(job, block) ⇒ Object
- #around_delayed_job(job, &block) ⇒ Object
- #before_http_request(name, start, finish, id, payload) ⇒ Object
- #catch_error(extra_details = nil, &block) ⇒ Object
- #cpu_time ⇒ Object
-
#initialize(config) ⇒ Client
constructor
A new instance of Client.
- #measure_block(name, &block) ⇒ Object
- #measure_code(code) ⇒ Object
- #record_error(exception, extra_details = nil) ⇒ Object
- #setup_callbacks ⇒ Object
Methods included from Location
cleanup_method_name, #extract_most_relevant_location, #gem_home, #gem_home_regex, #relative_path, split_file_location
Constructor Details
#initialize(config) ⇒ Client
Returns a new instance of Client.
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 |
# File 'lib/rorvswild/client.rb', line 23 def initialize(config) config = self.class.default_config.merge(config) @explain_sql_threshold = config[:explain_sql_threshold] @ignored_exceptions = config[:ignored_exceptions] @app_root = config[:app_root] @api_url = config[:api_url] @api_key = config[:api_key] @app_id = config[:app_id] @logger = config[:logger] @threads = Set.new @data = {} if defined?(Rails) @logger ||= Rails.logger @app_root ||= Rails.root.to_s config = Rails.application.config @parameter_filter = ActionDispatch::Http::ParameterFilter.new(config.filter_parameters) @ignored_exceptions ||= %w[ActionController::RoutingError] + config.action_dispatch.rescue_responses.map { |(key,value)| key } end @logger ||= Logger.new(STDERR) @app_root_regex = app_root ? /\A#{app_root}/ : nil setup_callbacks RorVsWild.register_client(self) end |
Instance Attribute Details
#api_key ⇒ Object (readonly)
Returns the value of attribute api_key.
19 20 21 |
# File 'lib/rorvswild/client.rb', line 19 def api_key @api_key end |
#api_url ⇒ Object (readonly)
Returns the value of attribute api_url.
19 20 21 |
# File 'lib/rorvswild/client.rb', line 19 def api_url @api_url end |
#app_id ⇒ Object (readonly)
Returns the value of attribute app_id.
19 20 21 |
# File 'lib/rorvswild/client.rb', line 19 def app_id @app_id end |
#app_root ⇒ Object (readonly)
Returns the value of attribute app_root.
19 20 21 |
# File 'lib/rorvswild/client.rb', line 19 def app_root @app_root end |
#app_root_regex ⇒ Object (readonly)
Returns the value of attribute app_root_regex.
21 22 23 |
# File 'lib/rorvswild/client.rb', line 21 def app_root_regex @app_root_regex end |
#explain_sql_threshold ⇒ Object (readonly)
Returns the value of attribute explain_sql_threshold.
19 20 21 |
# File 'lib/rorvswild/client.rb', line 19 def explain_sql_threshold @explain_sql_threshold end |
#ignored_exceptions ⇒ Object (readonly)
Returns the value of attribute ignored_exceptions.
19 20 21 |
# File 'lib/rorvswild/client.rb', line 19 def ignored_exceptions @ignored_exceptions end |
#threads ⇒ Object (readonly)
Returns the value of attribute threads.
21 22 23 |
# File 'lib/rorvswild/client.rb', line 21 def threads @threads end |
Class Method Details
.default_config ⇒ Object
11 12 13 14 15 16 17 |
# File 'lib/rorvswild/client.rb', line 11 def self.default_config { api_url: "https://www.rorvswild.com/api", explain_sql_threshold: 500, ignored_exceptions: [], } end |
Instance Method Details
#after_exception(exception, controller) ⇒ Object
105 106 107 108 109 110 111 112 113 114 |
# File 'lib/rorvswild/client.rb', line 105 def after_exception(exception, controller) if !ignored_exception?(exception) file, line = exception.backtrace.first.split(":") request[:error] = exception_to_hash(exception).merge( session: controller.session.to_hash, environment_variables: filter_sensitive_data(filter_environment_variables(controller.request.env)) ) end raise exception end |
#after_http_request(name, start, finish, id, payload) ⇒ Object
72 73 74 75 76 77 78 79 80 |
# File 'lib/rorvswild/client.rb', line 72 def after_http_request(name, start, finish, id, payload) request[:db_runtime] = (payload[:db_runtime] || 0).round request[:view_runtime] = (payload[:view_runtime] || 0).round request[:other_runtime] = compute_duration(start, finish) - request[:db_runtime] - request[:view_runtime] request[:error][:parameters] = filter_sensitive_data(payload[:params]) if request[:error] post_request rescue => exception log_error(exception) end |
#after_sql_query(name, start, finish, id, payload) ⇒ Object
84 85 86 87 88 89 90 91 92 |
# File 'lib/rorvswild/client.rb', line 84 def after_sql_query(name, start, finish, id, payload) return if !queries || IGNORED_QUERIES.include?(payload[:name]) file, line, method = extract_most_relevant_location(caller) runtime, sql = compute_duration(start, finish), payload[:sql] plan = runtime >= explain_sql_threshold ? explain(payload[:sql], payload[:binds]) : nil push_query(file: file, line: line, method: method, sql: sql, plan: plan, runtime: runtime) rescue => exception log_error(exception) end |
#after_view_rendering(name, start, finish, id, payload) ⇒ Object
94 95 96 97 98 99 100 101 102 103 |
# File 'lib/rorvswild/client.rb', line 94 def after_view_rendering(name, start, finish, id, payload) if views if view = views[file = relative_path(payload[:identifier])] view[:runtime] += compute_duration(start, finish) view[:times] += 1 else views[file] = {file: file, runtime: compute_duration(start, finish), times: 1} end end end |
#around_active_job(job, block) ⇒ Object
116 117 118 |
# File 'lib/rorvswild/client.rb', line 116 def around_active_job(job, block) measure_block(job.class.name, &block) end |
#around_delayed_job(job, &block) ⇒ Object
120 121 122 |
# File 'lib/rorvswild/client.rb', line 120 def around_delayed_job(job, &block) measure_block(job.name) { block.call(job) } end |
#before_http_request(name, start, finish, id, payload) ⇒ Object
68 69 70 |
# File 'lib/rorvswild/client.rb', line 68 def before_http_request(name, start, finish, id, payload) request.merge!(controller: payload[:controller], action: payload[:action], path: payload[:path], queries: [], views: {}) end |
#catch_error(extra_details = nil, &block) ⇒ Object
146 147 148 149 150 151 152 153 |
# File 'lib/rorvswild/client.rb', line 146 def catch_error(extra_details = nil, &block) begin block.call rescue Exception => ex record_error(ex, extra_details) if !ignored_exception?(ex) ex end end |
#cpu_time ⇒ Object
159 160 161 162 |
# File 'lib/rorvswild/client.rb', line 159 def cpu_time time = Process.times time.utime + time.stime + time.cutime + time.cstime end |
#measure_block(name, &block) ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/rorvswild/client.rb', line 128 def measure_block(name, &block) return block.call if job[:name] # Prevent from recursive jobs job[:name] = name job[:queries] = [] started_at = Time.now cpu_time_offset = cpu_time begin block.call rescue Exception => ex job[:error] = exception_to_hash(ex) if !ignored_exception?(ex) raise ensure job[:runtime] = (Time.now - started_at) * 1000 job[:cpu_runtime] = (cpu_time - cpu_time_offset) * 1000 post_job end end |
#measure_code(code) ⇒ Object
124 125 126 |
# File 'lib/rorvswild/client.rb', line 124 def measure_code(code) measure_block(code) { eval(code) } end |
#record_error(exception, extra_details = nil) ⇒ Object
155 156 157 |
# File 'lib/rorvswild/client.rb', line 155 def record_error(exception, extra_details = nil) post_error(exception_to_hash(exception, extra_details)) end |
#setup_callbacks ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/rorvswild/client.rb', line 50 def setup_callbacks client = self if defined?(ActiveSupport::Notifications) ActiveSupport::Notifications.subscribe("sql.active_record", &method(:after_sql_query)) ActiveSupport::Notifications.subscribe("render_partial.action_view", &method(:after_view_rendering)) ActiveSupport::Notifications.subscribe("render_template.action_view", &method(:after_view_rendering)) ActiveSupport::Notifications.subscribe("process_action.action_controller", &method(:after_http_request)) ActiveSupport::Notifications.subscribe("start_processing.action_controller", &method(:before_http_request)) ActionController::Base.rescue_from(StandardError) { |exception| client.after_exception(exception, self) } end Plugin::Resque.setup Plugin::Sidekiq.setup Kernel.at_exit(&method(:at_exit)) ActiveJob::Base.around_perform(&method(:around_active_job)) if defined?(ActiveJob::Base) Delayed::Worker.lifecycle.around(:invoke_job, &method(:around_delayed_job)) if defined?(Delayed::Worker) end |