rorvswild 0.6.1 → 1.0.0.pre.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|