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.
- checksums.yaml +4 -4
- data/Gemfile +8 -0
- data/README.md +70 -1
- data/lib/rorvswild.rb +15 -17
- data/lib/rorvswild/agent.rb +180 -0
- data/lib/rorvswild/client.rb +9 -285
- data/lib/rorvswild/installer.rb +0 -1
- data/lib/rorvswild/location.rb +10 -19
- data/lib/rorvswild/plugin/action_controller.rb +46 -0
- data/lib/rorvswild/plugin/action_mailer.rb +23 -0
- data/lib/rorvswild/plugin/action_view.rb +26 -0
- data/lib/rorvswild/plugin/active_job.rb +16 -0
- data/lib/rorvswild/plugin/active_record.rb +31 -0
- data/lib/rorvswild/plugin/delayed_job.rb +16 -0
- data/lib/rorvswild/plugin/mongo.rb +5 -4
- data/lib/rorvswild/plugin/net_http.rb +12 -2
- data/lib/rorvswild/plugin/redis.rb +1 -1
- data/lib/rorvswild/plugins.rb +14 -0
- data/lib/rorvswild/rails_loader.rb +1 -1
- data/lib/rorvswild/section.rb +54 -0
- data/lib/rorvswild/version.rb +1 -1
- data/test/helper.rb +13 -0
- data/test/measure_nested_sections_test.rb +38 -0
- data/test/plugin/action_controller_test.rb +58 -0
- data/test/plugin/action_mailer_test.rb +27 -0
- data/test/plugin/action_view_test.rb +40 -0
- data/test/plugin/active_job_test.rb +21 -0
- data/test/plugin/active_record_test.rb +39 -0
- data/test/plugin/delayed_job_test.rb +35 -0
- data/test/plugin/mongo_test.rb +9 -20
- data/test/plugin/net_http_test.rb +12 -23
- data/test/plugin/redis_test.rb +15 -27
- data/test/rorvswild_test.rb +141 -0
- data/test/run.rb +1 -1
- data/test/section_test.rb +53 -0
- metadata +32 -7
- data/test/ror_vs_wild_test.rb +0 -147
data/lib/rorvswild/installer.rb
CHANGED
data/lib/rorvswild/location.rb
CHANGED
@@ -1,26 +1,13 @@
|
|
1
1
|
module RorVsWild
|
2
2
|
module Location
|
3
|
-
def
|
4
|
-
|
5
|
-
|
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
|
11
|
-
|
12
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
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.
|
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.
|
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
|
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
|
data/lib/rorvswild/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -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
|
+
|