lib/rorvswild.rb in rorvswild-0.0.1 vs lib/rorvswild.rb in rorvswild-0.0.2

- old
+ new

@@ -1,139 +1,145 @@ require "rorvswild/version" -class RorVsWild - def self.default_config - { - api_url: "http://www.rorvswild.com/api", - sql_threshold: 500 - } +module RorVsWild + def self.new(*args) + Client.new(*args) # Compatibility with 0.0.1 end - attr_reader :api_url, :api_key, :app_id, :sql_threshold, :error, :request + class Client + def self.default_config + { + api_url: "http://www.rorvswild.com/api", + sql_threshold: 500 + } + end - def initialize(config) - config = RorVsWild.default_config.merge(config) - @sql_threshold = config[:sql_threshold] - @api_url = config[:api_url] - @api_key = config[:api_key] - @app_id = config[:app_id] - setup_callbacks - end + attr_reader :api_url, :api_key, :app_id, :sql_threshold, :error, :request - def setup_callbacks - ApplicationController.rescue_from(StandardError, &method(:after_exception)) - ActiveSupport::Notifications.subscribe("sql.active_record", &method(:after_sql_query)) - 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)) - end + def initialize(config) + config = self.class.default_config.merge(config) + @sql_threshold = config[:sql_threshold] + @api_url = config[:api_url] + @api_key = config[:api_key] + @app_id = config[:app_id] + setup_callbacks + end - def before_http_request(name, start, finish, id, payload) - @request = {controller: payload[:controller], action: payload[:action], path: payload[:path]} - @queries = [] - @views = [] - @error = nil - end + def setup_callbacks + ApplicationController.rescue_from(StandardError, &method(:after_exception)) + ActiveSupport::Notifications.subscribe("sql.active_record", &method(:after_sql_query)) + 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)) + end - 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[:params] = params_filter.filter(payload[:params]) if error - Thread.new { post_request } - rescue => ex - Rails.logger.error("[RorVsWild] " + ex.inspect) - Rails.logger.error("[RorVsWild] " + ex.backtrace.join("\n[RorVsWild] ")) - end + def before_http_request(name, start, finish, id, payload) + @request = {controller: payload[:controller], action: payload[:action], path: payload[:path]} + @queries = [] + @views = [] + @error = nil + end - def after_sql_query(name, start, finish, id, payload) - return if !queries || payload[:name] == "EXPLAIN".freeze - runtime, sql, plan = compute_duration(start, finish), nil, nil - file, line, method = extract_file_and_line_from_call_stack(caller) - plan = explain(sql = payload[:sql], payload[:binds]) if runtime > sql_threshold - queries << {file: file, line: line, sql: sql, plan: plan, runtime: runtime} - rescue => ex - Rails.logger.error("[RorVsWild] " + ex.inspect) - Rails.logger.error("[RorVsWild] " + ex.backtrace.join("\n[RorVsWild] ")) - end + 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[:params] = params_filter.filter(payload[:params]) if error + Thread.new { post_request } + rescue => ex + Rails.logger.error("[RorVsWild] " + ex.inspect) + Rails.logger.error("[RorVsWild] " + ex.backtrace.join("\n[RorVsWild] ")) + end - def after_view_rendering(name, start, finish, id, payload) - views << {file: relative_path(payload[:identifier]), runtime: compute_duration(start, finish)} if views - end + def after_sql_query(name, start, finish, id, payload) + return if !queries || payload[:name] == "EXPLAIN".freeze + runtime, sql, plan = compute_duration(start, finish), nil, nil + file, line, method = extract_file_and_line_from_call_stack(caller) + plan = explain(sql = payload[:sql], payload[:binds]) if runtime > sql_threshold + queries << {file: file, line: line, sql: sql, plan: plan, runtime: runtime} + rescue => ex + Rails.logger.error("[RorVsWild] " + ex.inspect) + Rails.logger.error("[RorVsWild] " + ex.backtrace.join("\n[RorVsWild] ")) + end - def after_exception(exception) - file, line = exception.backtrace.first.split(":") - @error = { - exception: exception.class.to_s, - backtrace: exception.backtrace, - message: exception.message, - file: relative_path(file), - line: line.to_i - } - raise exception - end + def after_view_rendering(name, start, finish, id, payload) + views << {file: relative_path(payload[:identifier]), runtime: compute_duration(start, finish)} if views + end - ####################### - ### Private methods ### - ####################### + def after_exception(exception) + file, line = exception.backtrace.first.split(":") + @error = { + exception: exception.class.to_s, + backtrace: exception.backtrace, + message: exception.message, + file: relative_path(file), + line: line.to_i + } + raise exception + end - private + ####################### + ### Private methods ### + ####################### - def queries - @queries - end + private - def views - @views - end + def queries + @queries + end - def slowest_views - views.sort { |h1, h2| h2[:runtime] <=> h1[:runtime] }[0, 25] - end + def views + @views + end - def slowest_queries - queries.sort { |h1, h2| h2[:runtime] <=> h1[:runtime] }[0, 25] - end + def slowest_views + views.sort { |h1, h2| h2[:runtime] <=> h1[:runtime] }[0, 25] + end - def explain(sql, binds) - rows = ActiveRecord::Base.connection.exec_query("EXPLAIN " + sql, "EXPLAIN", binds) - rows.map { |row| row["QUERY PLAN"] }.join("\n") - rescue => ex - end + def slowest_queries + queries.sort { |h1, h2| h2[:runtime] <=> h1[:runtime] }[0, 25] + end - def post_request - post("/requests", request: request.merge(queries: slowest_queries, views: slowest_views, error: error)) - end + def explain(sql, binds) + rows = ActiveRecord::Base.connection.exec_query("EXPLAIN " + sql, "EXPLAIN", binds) + rows.map { |row| row["QUERY PLAN"] }.join("\n") + rescue => ex + end - def extract_file_and_line_from_call_stack(stack) - file, line, method = (stack.find { |str| str.include?(Rails.root.to_s) } || - stack.find { |str| str.include?("(irb):") }).split(":") - file.sub!(Rails.root.to_s, "") - method.sub!("block in ", "") - method.sub!("in `", "") - method.sub!("'", "") - [file, line, method] - end + def post_request + post("/requests", request: request.merge(queries: slowest_queries, views: slowest_views, error: error)) + end - def compute_duration(start, finish) - ((finish - start) * 1000).round - end + def extract_file_and_line_from_call_stack(stack) + file, line, method = (stack.find { |str| str.include?(Rails.root.to_s) } || + stack.find { |str| str.include?("(irb):") }).split(":") + file.sub!(Rails.root.to_s, "") + method.sub!("block in ", "") + method.sub!("in `", "") + method.sub!("'", "") + [file, line, method] + end - def relative_path(path) - path.sub(Rails.root.to_s, "") - end + def compute_duration(start, finish) + ((finish - start) * 1000).round + end - def post(path, data) - uri = URI(api_url + path) - Net::HTTP.start(uri.host, uri.port) do |http| - post = Net::HTTP::Post.new(uri) - post.content_type = "application/json" - post.basic_auth(app_id, api_key) - post.body = data.to_json - http.request(post) + def relative_path(path) + path.sub(Rails.root.to_s, "") end - end - def params_filter - @params_filter ||= ActionDispatch::Http::ParameterFilter.new(Rails.application.config.filter_parameters) + def post(path, data) + uri = URI(api_url + path) + Net::HTTP.start(uri.host, uri.port) do |http| + post = Net::HTTP::Post.new(uri) + post.content_type = "application/json" + post.basic_auth(app_id, api_key) + post.body = data.to_json + http.request(post) + end + end + + def params_filter + @params_filter ||= ActionDispatch::Http::ParameterFilter.new(Rails.application.config.filter_parameters) + end end end