rorvswild 0.6.1 → 1.0.0.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,7 +23,6 @@ development:
23
23
 
24
24
  production:
25
25
  api_key: #{api_key}
26
- # explain_sql_threshold: 500 # Execute EXPLAIN for queries above the specified time in ms.
27
26
  # ignored_exceptions:
28
27
  # - ActionController::RoutingError
29
28
  # - UncommentToIgnoreAnyExceptionNameListedHere
@@ -1,26 +1,13 @@
1
1
  module RorVsWild
2
2
  module Location
3
- def self.cleanup_method_name(method)
4
- method.sub!("block in ".freeze, "".freeze)
5
- method.sub!("in `".freeze, "".freeze)
6
- method.sub!("'".freeze, "".freeze)
7
- method.index("_app_views_".freeze) == 0 ? nil : method
3
+ def find_most_relevant_location(locations)
4
+ result = locations.find { |l| l.path.index(app_root) == 0 && !(l.path =~ gem_home_regex) } if app_root
5
+ result || locations.find { |l| !(l.path =~ gem_home_regex) } || locations.first
8
6
  end
9
7
 
10
- def self.split_file_location(location)
11
- file, line, method = location.split(":")
12
- method = cleanup_method_name(method) if method
13
- [file, line, method]
14
- end
15
-
16
- def extract_most_relevant_location(stack)
17
- location = stack.find { |str| str =~ app_root_regex && !(str =~ gem_home_regex) } if app_root_regex
18
- location ||= stack.find { |str| !(str =~ gem_home_regex) } if gem_home_regex
19
- RorVsWild::Location.split_file_location(relative_path(location || stack.first))
20
- end
21
-
22
- def app_root_regex
23
- @app_root_regex ||= RorVsWild.default_client.app_root ? /\A#{RorVsWild.default_client.app_root}/ : nil
8
+ def extract_most_relevant_file_and_line(stack)
9
+ location = find_most_relevant_location(stack)
10
+ [relative_path(location.path), location.lineno]
24
11
  end
25
12
 
26
13
  def gem_home_regex
@@ -28,6 +15,10 @@ module RorVsWild
28
15
  end
29
16
 
30
17
  def gem_home
18
+ @gem_home ||= guess_gem_home
19
+ end
20
+
21
+ def guess_gem_home
31
22
  if ENV["GEM_HOME"] && !ENV["GEM_HOME"].empty?
32
23
  ENV["GEM_HOME"]
33
24
  elsif ENV["GEM_PATH"] && !(first_gem_path = ENV["GEM_PATH"].split(":").first)
@@ -0,0 +1,46 @@
1
+ module RorVsWild
2
+ module Plugin
3
+ class ActionController
4
+ def self.setup
5
+ return if @installed
6
+ return unless defined?(::ActionController::Base)
7
+ ActiveSupport::Notifications.subscribe("process_action.action_controller", new)
8
+ ::ActionController::Base.around_action(&method(:around_action))
9
+ ::ActionController::Base.rescue_from(StandardError) { |ex| RorVsWild::Plugin::ActionController.after_exception(ex, self) }
10
+ @installed = true
11
+ end
12
+
13
+ def self.after_exception(exception, controller)
14
+ if hash = RorVsWild.agent.push_exception(exception)
15
+ hash[:session] = controller.session.to_hash
16
+ hash[:environment_variables] = controller.request.filtered_env
17
+ end
18
+ raise exception
19
+ end
20
+
21
+ def self.around_action(controller, block)
22
+ begin
23
+ RorVsWild::Section.start do |section|
24
+ method_name = controller.method_for_action(controller.action_name)
25
+ section.file, section.line = controller.method(method_name).source_location
26
+ section.command = "#{controller.class}##{method_name}"
27
+ section.kind = "code".freeze
28
+ end
29
+ block.call
30
+ ensure
31
+ RorVsWild::Section.stop
32
+ end
33
+ end
34
+
35
+ # Payload: controller, action, params, format, method, path
36
+ def start(name, id, payload)
37
+ name = "#{payload[:controller]}##{payload[:action]}"
38
+ RorVsWild.agent.start_request(name: name, path: payload[:path])
39
+ end
40
+
41
+ def finish(name, id, payload)
42
+ RorVsWild.agent.stop_request
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,23 @@
1
+ module RorVsWild
2
+ module Plugin
3
+ class ActionMailer
4
+ def self.setup
5
+ return if @installed
6
+ return unless defined?(::ActiveSupport::Notifications.subscribe)
7
+ ActiveSupport::Notifications.subscribe("deliver.action_mailer", new)
8
+ @installed = true
9
+ end
10
+
11
+ def start(name, id, payload)
12
+ RorVsWild::Section.start
13
+ end
14
+
15
+ def finish(name, id, payload)
16
+ RorVsWild::Section.stop do |section|
17
+ section.command = payload[:mailer]
18
+ section.kind = "mail".freeze
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ module RorVsWild
2
+ module Plugin
3
+ class ActionView
4
+ def self.setup
5
+ return if @installed
6
+ return unless defined?(::ActiveSupport::Notifications.subscribe)
7
+ ActiveSupport::Notifications.subscribe("render_partial.action_view", new)
8
+ ActiveSupport::Notifications.subscribe("render_template.action_view", new)
9
+ @installed = true
10
+ end
11
+
12
+ def start(name, id, payload)
13
+ RorVsWild::Section.start
14
+ end
15
+
16
+ def finish(name, id, payload)
17
+ RorVsWild::Section.stop do |section|
18
+ section.kind = "view".freeze
19
+ section.command = RorVsWild.agent.relative_path(payload[:identifier])
20
+ section.file = section.command
21
+ section.line = 1
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module RorVsWild
2
+ module Plugin
3
+ module ActiveJob
4
+ def self.setup
5
+ return if @installed
6
+ return unless defined?(::ActiveJob::Base)
7
+ ::ActiveJob::Base.around_perform(&method(:around_perform))
8
+ @installed = true
9
+ end
10
+
11
+ def self.around_perform(job, block)
12
+ RorVsWild.measure_block(job.class.name, &block)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ module RorVsWild
2
+ module Plugin
3
+ class ActiveRecord
4
+ def self.setup
5
+ return if @installed
6
+ setup_callback
7
+ @installed = true
8
+ end
9
+
10
+ def self.setup_callback
11
+ return unless defined?(::ActiveSupport::Notifications.subscribe)
12
+ ActiveSupport::Notifications.subscribe("sql.active_record", new)
13
+ end
14
+
15
+ IGNORED_QUERIES = %w[EXPLAIN SCHEMA].freeze
16
+
17
+ def start(name, id, payload)
18
+ return if IGNORED_QUERIES.include?(payload[:name])
19
+ RorVsWild::Section.start do |section|
20
+ section.command = payload[:sql]
21
+ section.kind = "sql".freeze
22
+ end
23
+ end
24
+
25
+ def finish(name, id, payload)
26
+ return if IGNORED_QUERIES.include?(payload[:name])
27
+ RorVsWild::Section.stop
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ module RorVsWild
2
+ module Plugin
3
+ module DelayedJob
4
+ def self.setup
5
+ return if @installed
6
+ return unless defined?(Delayed::Worker)
7
+ Delayed::Worker.lifecycle.around(:invoke_job, &method(:around_perform))
8
+ @installed = true
9
+ end
10
+
11
+ def self.around_perform(job, &block)
12
+ RorVsWild.measure_block(job.name) { block.call(job) }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -15,6 +15,7 @@ module RorVsWild
15
15
  end
16
16
 
17
17
  def started(event)
18
+ RorVsWild::Section.start
18
19
  commands[event.request_id] = event.command
19
20
  end
20
21
 
@@ -27,10 +28,10 @@ module RorVsWild
27
28
  end
28
29
 
29
30
  def after_query(event)
30
- runtime = event.duration * 1000
31
- command = commands.delete(event.request_id).to_s
32
- file, line, method = RorVsWild.client.extract_most_relevant_location(caller)
33
- RorVsWild.client.send(:push_query, kind: "mongo", command: command, file: file, line: line, method: method, runtime: runtime)
31
+ RorVsWild::Section.stop do |section|
32
+ section.kind = "mongo".freeze
33
+ section.command = commands.delete(event.request_id).to_s
34
+ end
34
35
  end
35
36
  end
36
37
  end
@@ -1,6 +1,9 @@
1
1
  module RorVsWild
2
2
  module Plugin
3
3
  class NetHttp
4
+ HTTP = "http".freeze
5
+ HTTPS = "https".freeze
6
+
4
7
  def self.setup
5
8
  return if !defined?(Net::HTTP)
6
9
  return if Net::HTTP.method_defined?(:request_without_rorvswild)
@@ -9,12 +12,19 @@ module RorVsWild
9
12
  alias_method :request_without_rorvswild, :request
10
13
 
11
14
  def request(req, body = nil, &block)
12
- scheme = use_ssl? ? "https".freeze : "http".freeze
15
+ return request_without_rorvswild(req, body, &block) if request_called_twice?
16
+ scheme = use_ssl? ? HTTPS : HTTP
13
17
  url = "#{req.method} #{scheme}://#{address}#{req.path}"
14
- RorVsWild.client.measure_query("http".freeze, url) do
18
+ RorVsWild.agent.measure_section(url, HTTP) do
15
19
  request_without_rorvswild(req, body, &block)
16
20
  end
17
21
  end
22
+
23
+ def request_called_twice?
24
+ # Net::HTTP#request calls itself when connection is not started.
25
+ # This condition prevents from counting twice the request.
26
+ (current_section = RorVsWild::Section.current) && current_section.kind == HTTP
27
+ end
18
28
  end
19
29
  end
20
30
  end
@@ -9,7 +9,7 @@ module RorVsWild
9
9
 
10
10
  def process(commands, &block)
11
11
  string = RorVsWild::Plugin::Redis.commands_to_string(commands)
12
- RorVsWild.client.measure_query("redis".freeze, string) do
12
+ RorVsWild.agent.measure_block(string, "redis".freeze) do
13
13
  process_without_rorvswild(commands, &block)
14
14
  end
15
15
  end
@@ -0,0 +1,14 @@
1
+ require "rorvswild/plugin/net_http"
2
+
3
+ require "rorvswild/plugin/redis"
4
+ require "rorvswild/plugin/mongo"
5
+
6
+ require "rorvswild/plugin/resque"
7
+ require "rorvswild/plugin/sidekiq"
8
+ require "rorvswild/plugin/active_job"
9
+ require "rorvswild/plugin/delayed_job"
10
+
11
+ require "rorvswild/plugin/action_view"
12
+ require "rorvswild/plugin/active_record"
13
+ require "rorvswild/plugin/action_mailer"
14
+ require "rorvswild/plugin/action_controller"
@@ -13,7 +13,7 @@ module RorVsWild
13
13
  return if @started
14
14
  if (path = Rails.root.join("config/rorvswild.yml")).exist?
15
15
  if config = RorVsWild::RailsLoader.load_config_file(path)[Rails.env]
16
- RorVsWild::Client.new(config.symbolize_keys)
16
+ RorVsWild.start(config.symbolize_keys)
17
17
  @started = true
18
18
  end
19
19
  end
@@ -0,0 +1,54 @@
1
+ module RorVsWild
2
+ class Section
3
+ attr_reader :started_at
4
+ attr_accessor :kind, :file, :line, :calls, :command, :children_runtime, :total_runtime
5
+
6
+ def self.start(&block)
7
+ section = Section.new
8
+ block.call(section) if block_given?
9
+ stack.push(section)
10
+ section
11
+ end
12
+
13
+ def self.stop(&block)
14
+ section = stack.pop
15
+ block.call(section) if block_given?
16
+ section.total_runtime = (Time.now.utc - section.started_at) * 1000
17
+ current.children_runtime += section.total_runtime if current
18
+ RorVsWild.agent.add_section(section)
19
+ end
20
+
21
+ def self.stack
22
+ RorVsWild.agent.data[:section_stack] ||= []
23
+ end
24
+
25
+ def self.current
26
+ stack.last
27
+ end
28
+
29
+ def initialize
30
+ @calls = 1
31
+ @total_runtime = 0
32
+ @children_runtime = 0
33
+ @started_at = Time.now.utc
34
+ location = RorVsWild.agent.find_most_relevant_location(caller_locations)
35
+ @file = RorVsWild.agent.relative_path(location.path)
36
+ @line = location.lineno
37
+ end
38
+
39
+ def sibling?(section)
40
+ kind == section.kind && line == section.line && file == section.file
41
+ end
42
+
43
+ def merge(section)
44
+ self.calls += section.calls
45
+ self.total_runtime += section.total_runtime
46
+ self.children_runtime += section.children_runtime
47
+ self.command ||= section.command
48
+ end
49
+
50
+ def self_runtime
51
+ total_runtime - children_runtime
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,3 @@
1
1
  module RorVsWild
2
- VERSION = "0.6.1".freeze
2
+ VERSION = "1.0.0-alpha".freeze
3
3
  end
@@ -5,3 +5,16 @@ require "rorvswild"
5
5
  require "minitest/autorun"
6
6
  require "mocha/mini_test"
7
7
  require "top_tests"
8
+
9
+ module RorVsWildAgentHelper
10
+ def agent
11
+ @agent ||= initialize_agent(app_root: File.dirname(__FILE__))
12
+ end
13
+
14
+ def initialize_agent(options = {})
15
+ agent ||= RorVsWild.start(options)
16
+ agent.stubs(:post_request)
17
+ agent.stubs(:post_job)
18
+ agent
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/helper")
2
+
3
+ class RorVsWild::MeasureNestedSectionsTest < Minitest::Test
4
+ include RorVsWildAgentHelper
5
+ include TopTests
6
+
7
+ def test_measure_section
8
+ result = agent.measure_block("root") do
9
+ agent.measure_block("parent") do
10
+ sleep 0.01
11
+ agent.measure_block("child") do
12
+ sleep 0.02
13
+ 42
14
+ end
15
+ end
16
+ end
17
+ assert_equal(42, result)
18
+ sections = agent.data[:sections]
19
+ parent, child = sections[1], sections[0]
20
+ assert_equal("child", child.command)
21
+ assert_equal("parent", parent.command)
22
+ assert(child.self_runtime > 20)
23
+ assert(parent.self_runtime > 10)
24
+ assert(child.self_runtime > parent.self_runtime)
25
+ assert_equal(child.total_runtime + parent.self_runtime, parent.total_runtime)
26
+ end
27
+
28
+ def test_measure_section_with_exception
29
+ assert_raises(ZeroDivisionError) do
30
+ agent.measure_block("root") do
31
+ agent.measure_block("parent") do
32
+ agent.measure_block("child") { 1 / 0 }
33
+ end
34
+ end
35
+ end
36
+ assert_equal(2, agent.data[:sections].size)
37
+ end
38
+ end
@@ -0,0 +1,58 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ require "action_controller"
4
+
5
+ class RorVsWild::Plugin::ActionControllerTest < Minitest::Test
6
+ include RorVsWildAgentHelper
7
+
8
+ def test_callback
9
+ agent.expects(:post_request)
10
+ payload = {controller: "UsersController", action: "show"}
11
+ ActiveSupport::Notifications.instrument("process_action.action_controller", payload) do
12
+ sleep 0.01
13
+ end
14
+
15
+ data = agent.send(:data)
16
+ assert_equal(0, data[:sections].size)
17
+ assert_equal("UsersController#show", data[:name])
18
+ assert(data[:runtime] > 10)
19
+ end
20
+
21
+ def test_callback_when_exception_is_raised
22
+ agent.expects(:post_request)
23
+ controller = stub(session: {id: "session"}, request: stub(filtered_env: {header: "env"}))
24
+ payload = {controller: "UsersController", action: "show"}
25
+ assert_raises(ZeroDivisionError) do
26
+ ActiveSupport::Notifications.instrument("process_action.action_controller", payload) do
27
+ begin
28
+ 1 / 0
29
+ rescue => ex
30
+ RorVsWild::Plugin::ActionController.after_exception(ex, controller)
31
+ end
32
+ end
33
+ end
34
+
35
+ data = agent.send(:data)
36
+ assert_equal("UsersController#show", data[:name])
37
+ assert_equal("ZeroDivisionError", data[:error][:exception])
38
+ assert_equal({id: "session"}, data[:error][:session])
39
+ assert_equal({header: "env"}, data[:error][:environment_variables])
40
+ end
41
+
42
+ class SampleController
43
+ def index
44
+ end
45
+ end
46
+
47
+ def test_around_action
48
+ controller = SampleController.new
49
+ controller.stubs(action_name: "index", controller_name: "SampleController", method_for_action: "index")
50
+ agent.measure_block("test") do
51
+ RorVsWild::Plugin::ActionController.around_action(controller, controller.method(:index))
52
+ end
53
+ assert_equal(1, agent.data[:sections].size)
54
+ assert_equal(__FILE__, agent.data[:sections][0].file)
55
+ assert_equal("RorVsWild::Plugin::ActionControllerTest::SampleController#index", agent.data[:sections][0].command)
56
+ end
57
+ end
58
+