newrelic_rpm 2.8.11 → 2.9.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of newrelic_rpm might be problematic. Click here for more details.
- data/CHANGELOG +267 -0
- data/LICENSE +1 -1
- data/Manifest +142 -0
- data/README.md +138 -0
- data/Rakefile +10 -28
- data/bin/mongrel_rpm +33 -0
- data/cert/cacert.pem +34 -0
- data/init.rb +38 -0
- data/lib/new_relic/agent/agent.rb +160 -347
- data/lib/new_relic/agent/collection_helper.rb +13 -24
- data/lib/new_relic/agent/error_collector.rb +29 -15
- data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +63 -76
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +90 -48
- data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +72 -47
- data/lib/new_relic/agent/instrumentation/error_instrumentation.rb +14 -0
- data/lib/new_relic/agent/instrumentation/merb/controller.rb +10 -1
- data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +5 -7
- data/lib/new_relic/agent/instrumentation/merb/errors.rb +3 -1
- data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +7 -0
- data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +34 -7
- data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +20 -12
- data/lib/new_relic/agent/instrumentation/rails/errors.rb +5 -4
- data/lib/new_relic/agent/method_tracer.rb +159 -135
- data/lib/new_relic/agent/patch_const_missing.rb +46 -26
- data/lib/new_relic/agent/sampler.rb +12 -0
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +44 -0
- data/lib/new_relic/agent/samplers/memory_sampler.rb +126 -0
- data/lib/new_relic/agent/samplers/mongrel_sampler.rb +22 -0
- data/lib/new_relic/agent/shim_agent.rb +11 -0
- data/lib/new_relic/agent/stats_engine.rb +85 -46
- data/lib/new_relic/agent/transaction_sampler.rb +63 -38
- data/lib/new_relic/agent/worker_loop.rb +8 -18
- data/lib/new_relic/agent.rb +200 -25
- data/lib/new_relic/commands/deployments.rb +9 -9
- data/lib/new_relic/control/merb.rb +22 -0
- data/lib/new_relic/control/rails.rb +141 -0
- data/lib/new_relic/{config → control}/ruby.rb +13 -2
- data/lib/new_relic/control.rb +424 -0
- data/lib/new_relic/local_environment.rb +201 -79
- data/lib/new_relic/metric_data.rb +7 -0
- data/lib/new_relic/metric_parser/action_mailer.rb +9 -0
- data/lib/new_relic/metric_parser/active_merchant.rb +26 -0
- data/lib/new_relic/metric_parser/active_record.rb +11 -0
- data/lib/new_relic/metric_parser/controller.rb +51 -0
- data/lib/new_relic/metric_parser/controller_cpu.rb +38 -0
- data/lib/new_relic/metric_parser/database.rb +23 -0
- data/lib/new_relic/metric_parser/errors.rb +6 -0
- data/lib/new_relic/metric_parser/mem_cache.rb +12 -0
- data/lib/new_relic/metric_parser/view.rb +61 -0
- data/lib/new_relic/metric_parser/web_service.rb +9 -0
- data/lib/new_relic/metric_parser.rb +107 -0
- data/lib/new_relic/metric_spec.rb +5 -0
- data/lib/new_relic/noticed_error.rb +5 -1
- data/lib/new_relic/rack/metric_app.rb +57 -0
- data/lib/new_relic/rack/newrelic.ru +25 -0
- data/lib/new_relic/rack/newrelic.yml +25 -0
- data/lib/new_relic/rack.rb +5 -0
- data/lib/new_relic/recipes.rb +10 -3
- data/lib/new_relic/stats.rb +130 -144
- data/lib/new_relic/transaction_analysis.rb +7 -8
- data/lib/new_relic/transaction_sample.rb +86 -10
- data/lib/new_relic/version.rb +41 -160
- data/lib/new_relic_api.rb +7 -6
- data/lib/newrelic_rpm.rb +30 -17
- data/lib/tasks/{agent_tests.rake → tests.rake} +1 -1
- data/newrelic.yml +115 -62
- data/newrelic_rpm.gemspec +36 -0
- data/test/active_record_fixtures.rb +55 -0
- data/test/config/newrelic.yml +21 -3
- data/test/config/{test_config.rb → test_control.rb} +14 -10
- data/test/new_relic/agent/active_record_instrumentation_test.rb +189 -0
- data/test/new_relic/agent/agent_test.rb +104 -0
- data/test/new_relic/agent/agent_test_controller.rb +18 -1
- data/test/new_relic/agent/classloader_patch_test.rb +56 -0
- data/test/new_relic/agent/{tc_collection_helper.rb → collection_helper_test.rb} +28 -23
- data/test/new_relic/agent/controller_test.rb +107 -0
- data/test/new_relic/agent/dispatcher_instrumentation_test.rb +70 -0
- data/test/new_relic/agent/error_collector_test.rb +155 -0
- data/test/new_relic/agent/{tc_method_tracer.rb → method_tracer_test.rb} +6 -12
- data/test/new_relic/agent/metric_data_test.rb +56 -0
- data/test/new_relic/agent/stats_engine_test.rb +266 -0
- data/test/new_relic/agent/{tc_transaction_sample_builder.rb → transaction_sample_builder_test.rb} +6 -5
- data/test/new_relic/agent/{tc_transaction_sample.rb → transaction_sample_test.rb} +9 -13
- data/test/new_relic/agent/transaction_sampler_test.rb +317 -0
- data/test/new_relic/agent/{tc_worker_loop.rb → worker_loop_test.rb} +1 -1
- data/test/new_relic/control_test.rb +97 -0
- data/test/new_relic/{tc_deployments_api.rb → deployments_api_test.rb} +8 -4
- data/test/new_relic/environment_test.rb +75 -0
- data/test/new_relic/metric_parser_test.rb +142 -0
- data/test/new_relic/{tc_metric_spec.rb → metric_spec_test.rb} +28 -1
- data/test/new_relic/samplers_test.rb +71 -0
- data/test/new_relic/{tc_shim_agent.rb → shim_agent_test.rb} +1 -1
- data/test/new_relic/stats_test.rb +291 -0
- data/test/new_relic/version_number_test.rb +46 -0
- data/test/test_helper.rb +7 -30
- data/test/ui/newrelic_controller_test.rb +14 -0
- data/test/ui/{tc_newrelic_helper.rb → newrelic_helper_test.rb} +16 -7
- data/ui/controllers/newrelic_controller.rb +17 -3
- data/ui/helpers/newrelic_helper.rb +44 -15
- data/ui/views/layouts/newrelic_default.rhtml +7 -8
- data/ui/views/newrelic/_sample.rhtml +5 -2
- data/ui/views/newrelic/_segment.rhtml +1 -1
- data/ui/views/newrelic/_segment_limit_message.rhtml +1 -0
- data/ui/views/newrelic/_segment_row.rhtml +4 -4
- data/ui/views/newrelic/_show_sample_detail.rhtml +3 -1
- data/ui/views/newrelic/_show_sample_sql.rhtml +2 -1
- data/ui/views/newrelic/explain_sql.rhtml +2 -5
- data/ui/views/newrelic/images/file_icon.png +0 -0
- data/ui/views/newrelic/images/new_relic_rpm_desktop.gif +0 -0
- data/ui/views/newrelic/index.rhtml +21 -13
- data/ui/views/newrelic/javascript/prototype-scriptaculous.js +7288 -0
- data/ui/views/newrelic/show_sample.rhtml +18 -3
- data/ui/views/newrelic/stylesheets/style.css +39 -0
- data/ui/views/newrelic/threads.rhtml +52 -0
- metadata +192 -70
- data/README +0 -136
- data/lib/new_relic/agent/instrumentation/rails/rails.rb +0 -6
- data/lib/new_relic/agent/samplers/cpu.rb +0 -29
- data/lib/new_relic/agent/samplers/memory.rb +0 -53
- data/lib/new_relic/agent/samplers/mongrel.rb +0 -26
- data/lib/new_relic/agent/synchronize.rb +0 -40
- data/lib/new_relic/config/merb.rb +0 -35
- data/lib/new_relic/config/rails.rb +0 -114
- data/lib/new_relic/config.rb +0 -279
- data/lib/new_relic/shim_agent.rb +0 -96
- data/test/new_relic/agent/model_fixture.rb +0 -15
- data/test/new_relic/agent/tc_active_record.rb +0 -90
- data/test/new_relic/agent/tc_agent.rb +0 -148
- data/test/new_relic/agent/tc_controller.rb +0 -77
- data/test/new_relic/agent/tc_dispatcher_instrumentation.rb +0 -52
- data/test/new_relic/agent/tc_error_collector.rb +0 -127
- data/test/new_relic/agent/tc_stats_engine.rb +0 -218
- data/test/new_relic/agent/tc_synchronize.rb +0 -37
- data/test/new_relic/agent/tc_transaction_sampler.rb +0 -302
- data/test/new_relic/tc_config.rb +0 -36
- data/test/new_relic/tc_environment.rb +0 -94
- data/test/new_relic/tc_stats.rb +0 -141
@@ -1,24 +1,21 @@
|
|
1
1
|
# We have to patch the mongrel dispatcher live since the classes
|
2
2
|
# aren't defined when our instrumentation loads
|
3
|
+
# To use this module, you need to monkey patch a method newrelic_response_code
|
4
|
+
# which will return the response status code when the dispatcher finishes.
|
3
5
|
module NewRelic::Agent::Instrumentation
|
4
6
|
module DispatcherInstrumentation
|
5
7
|
|
6
|
-
@@newrelic_agent = NewRelic::Agent.agent
|
7
|
-
@@newrelic_rails_dispatch_stat = @@newrelic_agent.stats_engine.get_stats 'Rails/HTTP Dispatch'
|
8
|
-
@@newrelic_mongrel_queue_stat = @@newrelic_agent.stats_engine.get_stats('WebFrontend/Mongrel/Average Queue Time')
|
9
|
-
|
10
8
|
def newrelic_dispatcher_start
|
11
9
|
# Put the current time on the thread. Can't put in @ivar because this could
|
12
10
|
# be a class or instance context
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
NewRelic::Agent::Instrumentation::DispatcherInstrumentation::BusyCalculator.dispatcher_start t0
|
11
|
+
newrelic_dispatcher_start_time = Time.now.to_f
|
12
|
+
Thread.current[:newrelic_dispatcher_start] = newrelic_dispatcher_start_time
|
13
|
+
NewRelic::Agent::Instrumentation::DispatcherInstrumentation::BusyCalculator.dispatcher_start newrelic_dispatcher_start_time
|
17
14
|
# capture the time spent in the mongrel queue, if running in mongrel. This is the
|
18
15
|
# current time less the timestamp placed in 'started_on' by mongrel.
|
19
16
|
mongrel_start = Thread.current[:started_on]
|
20
|
-
|
21
|
-
|
17
|
+
mongrel_queue_stat.trace_call(newrelic_dispatcher_start_time - mongrel_start.to_f) if mongrel_start
|
18
|
+
NewRelic::Agent.agent.start_transaction
|
22
19
|
|
23
20
|
# Reset the flag indicating the controller action should be ignored.
|
24
21
|
# It may be set by the action to either true or false or left nil meaning false
|
@@ -26,18 +23,26 @@ module NewRelic::Agent::Instrumentation
|
|
26
23
|
end
|
27
24
|
|
28
25
|
def newrelic_dispatcher_finish
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
#puts @env.to_a.map{|k,v| "#{'%32s' % k}: #{v.inspect[0..64]}"}.join("\n")
|
27
|
+
dispatcher_end_time = Time.now.to_f
|
28
|
+
NewRelic::Agent.agent.end_transaction
|
29
|
+
NewRelic::Agent::Instrumentation::DispatcherInstrumentation::BusyCalculator.dispatcher_finish dispatcher_end_time
|
30
|
+
unless Thread.current[:controller_ignored]
|
31
|
+
# Store the response header
|
32
|
+
newrelic_dispatcher_start_time = Thread.current[:newrelic_dispatcher_start]
|
33
|
+
response_code = newrelic_response_code
|
34
|
+
if response_code
|
35
|
+
stats = response_stats[response_code] ||= NewRelic::Agent.agent.stats_engine.get_stats("HTTP/Response/#{response_code}")
|
36
|
+
stats.trace_call(dispatcher_end_time - newrelic_dispatcher_start_time)
|
37
|
+
end
|
38
|
+
dispatch_stat.trace_call(dispatcher_end_time - newrelic_dispatcher_start_time)
|
33
39
|
end
|
34
|
-
t1 = Time.now.to_f
|
35
|
-
@@newrelic_agent.end_transaction
|
36
|
-
@@newrelic_rails_dispatch_stat.trace_call(t1 - t0) unless Thread.current[:controller_ignored]
|
37
|
-
NewRelic::Agent::Instrumentation::DispatcherInstrumentation::BusyCalculator.dispatcher_finish t1
|
38
|
-
Thread.current[:newrelic_t0] = nil
|
39
40
|
end
|
40
|
-
|
41
|
+
def newrelic_response_code
|
42
|
+
raise "Must be implemented in the dispatcher class"
|
43
|
+
end
|
44
|
+
# Used only when no before/after callbacks are available with
|
45
|
+
# the dispatcher, such as Rails before 2.0
|
41
46
|
def dispatch_newrelic(*args)
|
42
47
|
newrelic_dispatcher_start
|
43
48
|
begin
|
@@ -47,48 +52,62 @@ module NewRelic::Agent::Instrumentation
|
|
47
52
|
end
|
48
53
|
end
|
49
54
|
|
55
|
+
private
|
56
|
+
# memoize the stats to avoid the cost of the lookup each time.
|
57
|
+
def dispatch_stat
|
58
|
+
@@newrelic_rails_dispatch_stat ||= NewRelic::Agent.agent.stats_engine.get_stats 'Rails/HTTP Dispatch'
|
59
|
+
end
|
60
|
+
def mongrel_queue_stat
|
61
|
+
@@newrelic_mongrel_queue_stat ||= NewRelic::Agent.agent.stats_engine.get_stats('WebFrontend/Mongrel/Average Queue Time')
|
62
|
+
end
|
63
|
+
def response_stats
|
64
|
+
@@newrelic_response_stats ||= { '200' => NewRelic::Agent.agent.stats_engine.get_stats('HTTP/Response/200')}
|
65
|
+
end
|
66
|
+
|
50
67
|
# This won't work with Rails 2.2 multi-threading
|
51
68
|
module BusyCalculator
|
52
69
|
extend self
|
53
70
|
# the fraction of the sample period that the dispatcher was busy
|
54
|
-
|
71
|
+
|
55
72
|
@harvest_start = Time.now.to_f
|
56
73
|
@accumulator = 0
|
57
|
-
@
|
74
|
+
@entrypoint_stack = []
|
75
|
+
@lock = Mutex.new
|
76
|
+
|
58
77
|
def dispatcher_start(time)
|
59
|
-
|
60
|
-
|
61
|
-
|
78
|
+
@lock.synchronize do
|
79
|
+
@entrypoint_stack.push time
|
80
|
+
end
|
62
81
|
end
|
63
82
|
|
64
83
|
def dispatcher_finish(time)
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
Thread.critical = false
|
84
|
+
@lock.synchronize do
|
85
|
+
NewRelic::Control.instance.log.error("Stack underflow tracking dispatcher entry and exit!\n #{caller.join(" \n")}") and return if @entrypoint_stack.empty?
|
86
|
+
@accumulator += (time - @entrypoint_stack.pop)
|
87
|
+
end
|
70
88
|
end
|
71
89
|
|
72
|
-
def
|
73
|
-
@
|
90
|
+
def busy_count
|
91
|
+
@entrypoint_stack.size
|
74
92
|
end
|
75
|
-
|
93
|
+
|
94
|
+
# Called before uploading to to the server to collect current busy stats.
|
76
95
|
def harvest_busy
|
77
|
-
|
78
|
-
|
79
|
-
busy = @accumulator
|
80
|
-
@accumulator = 0
|
81
|
-
|
96
|
+
busy = 0
|
82
97
|
t0 = Time.now.to_f
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
98
|
+
@lock.synchronize do
|
99
|
+
busy = @accumulator
|
100
|
+
@accumulator = 0
|
101
|
+
|
102
|
+
# Walk through the stack and capture all times up to
|
103
|
+
# now for entrypoints
|
104
|
+
@entrypoint_stack.size.times do |frame|
|
105
|
+
busy += (t0 - @entrypoint_stack[frame])
|
106
|
+
@entrypoint_stack[frame] = t0
|
107
|
+
end
|
108
|
+
|
87
109
|
end
|
88
110
|
|
89
|
-
|
90
|
-
Thread.critical = false
|
91
|
-
|
92
111
|
busy = 0.0 if busy < 0.0 # don't go below 0%
|
93
112
|
|
94
113
|
time_window = (t0 - @harvest_start)
|
@@ -96,10 +115,16 @@ module NewRelic::Agent::Instrumentation
|
|
96
115
|
|
97
116
|
busy = busy / time_window
|
98
117
|
|
99
|
-
busy
|
100
|
-
@instance_busy.record_data_point busy
|
118
|
+
instance_busy_stats.record_data_point busy unless busy == 0
|
101
119
|
@harvest_start = t0
|
102
120
|
end
|
121
|
+
private
|
122
|
+
def instance_busy_stats
|
123
|
+
# Late binding on the Instance/busy stats
|
124
|
+
@instance_busy ||= NewRelic::Agent.agent.stats_engine.get_stats('Instance/Busy')
|
125
|
+
end
|
126
|
+
|
103
127
|
end
|
128
|
+
|
104
129
|
end
|
105
130
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module NewRelic::Agent::Instrumentation
|
2
|
+
module ErrorInstrumentation
|
3
|
+
module Shim
|
4
|
+
def newrelic_notice_error(*args); end
|
5
|
+
end
|
6
|
+
# Send the error instance to New Relic.
|
7
|
+
# +metric_path+ is the optional metric identifier given for the context of the error.
|
8
|
+
# +param_info+ is additional hash of info to be shown with the error.
|
9
|
+
def newrelic_notice_error(exception, metric_path = nil, param_info = {})
|
10
|
+
metric_path ||= self.newrelic_metric_path if self.respond_to? :newrelic_metric_path
|
11
|
+
NewRelic::Agent.agent.error_collector.notice_error(exception, nil, metric_path, param_info)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -4,7 +4,16 @@ require 'merb-core/controller/merb_controller'
|
|
4
4
|
Merb::Controller.class_eval do
|
5
5
|
include NewRelic::Agent::Instrumentation::ControllerInstrumentation
|
6
6
|
|
7
|
-
class_inheritable_accessor :
|
7
|
+
class_inheritable_accessor :do_not_trace
|
8
|
+
class_inheritable_accessor :ignore_apdex
|
9
|
+
|
10
|
+
def self.newrelic_write_attr(attr_name, value) # :nodoc:
|
11
|
+
self.send "#{attr_name}=", attr_name, value
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.newrelic_read_attr(attr_name) # :nodoc:
|
15
|
+
self.send attr_name
|
16
|
+
end
|
8
17
|
|
9
18
|
protected
|
10
19
|
# determine the path that is used in the metric name for
|
@@ -1,15 +1,13 @@
|
|
1
1
|
require 'merb-core/dispatch/dispatcher'
|
2
2
|
# NewRelic RPM instrumentation for http request dispatching (Routes mapping)
|
3
|
-
# Note, the dispatcher class from no module into into the ActionController modile
|
4
|
-
# in rails 2.0. Thus we need to check for both
|
5
|
-
|
6
3
|
Merb::Request.class_eval do
|
7
4
|
|
8
|
-
# This is for merb prior to 1.0
|
9
5
|
include NewRelic::Agent::Instrumentation::DispatcherInstrumentation
|
6
|
+
|
10
7
|
alias_method :dispatch_without_newrelic, :handle
|
11
8
|
alias_method :handle, :dispatch_newrelic
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
def newrelic_response_code
|
10
|
+
# Don't have an easy way to get the HTTP status from here yet
|
11
|
+
nil
|
12
|
+
end
|
15
13
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# Hook in the notification to merb
|
2
2
|
error_notifier = Proc.new {
|
3
|
-
|
3
|
+
if request.exceptions #check that there's actually an exception
|
4
|
+
NewRelic::Agent.agent.error_collector.notice_error(request.exceptions.first, request, "#{params[:controller]}/#{params[:action]}", params)
|
5
|
+
end
|
4
6
|
}
|
5
7
|
Merb::Dispatcher::DefaultException.before error_notifier
|
6
8
|
Exceptions.before error_notifier
|
@@ -0,0 +1,7 @@
|
|
1
|
+
if defined?(PhusionPassenger)
|
2
|
+
NewRelic::Control.instance.log.debug "Installing Passenger shutdown hook."
|
3
|
+
PhusionPassenger.on_event(:stopping_worker_process) do
|
4
|
+
NewRelic::Control.instance.log.info "Passenger stopping this process, shutdown the agent."
|
5
|
+
NewRelic::Agent.instance.shutdown
|
6
|
+
end
|
7
|
+
end
|
@@ -1,6 +1,30 @@
|
|
1
1
|
|
2
|
-
if defined? ActionController
|
2
|
+
if defined? ActionController
|
3
|
+
|
4
|
+
case Rails::VERSION::STRING
|
3
5
|
|
6
|
+
when /^(1\.|2\.0)/ # Rails 1.* - 2.0
|
7
|
+
ActionController::Base.class_eval do
|
8
|
+
add_method_tracer :render, 'View/#{newrelic_metric_path}/Rendering'
|
9
|
+
end
|
10
|
+
|
11
|
+
when /^2\.1\./ # Rails 2.1
|
12
|
+
ActionView::PartialTemplate.class_eval do
|
13
|
+
add_method_tracer :render, 'View/#{path_without_extension}.#{@view.template_format}.#{extension}/Partial'
|
14
|
+
end
|
15
|
+
ActionView::Template.class_eval do
|
16
|
+
add_method_tracer :render, 'View/#{path_without_extension}.#{@view.template_format}.#{extension}/Rendering'
|
17
|
+
end
|
18
|
+
|
19
|
+
when /^2\./ # Rails 2.2-2.*
|
20
|
+
ActionView::RenderablePartial.module_eval do
|
21
|
+
add_method_tracer :render_partial, 'View/#{path}/Partial'
|
22
|
+
end
|
23
|
+
ActionView::Template.class_eval do
|
24
|
+
add_method_tracer :render, 'View/#{path}/Rendering'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
4
28
|
ActionController::Base.class_eval do
|
5
29
|
include NewRelic::Agent::Instrumentation::ControllerInstrumentation
|
6
30
|
|
@@ -10,15 +34,18 @@ if defined? ActionController
|
|
10
34
|
alias_method :perform_action, :perform_action_with_newrelic_trace
|
11
35
|
private :perform_action
|
12
36
|
|
13
|
-
add_method_tracer :
|
37
|
+
#add_method_tracer :render_for_file, 'View/#{args[0]}/ForFile/Rendering'
|
38
|
+
#add_method_tracer :render_for_text, 'View/#{newrelic_metric_path}/Text/Rendering'
|
39
|
+
#add_method_tracer :render, 'View/#{newrelic_metric_path}/Rendering'
|
14
40
|
|
15
|
-
def self.
|
16
|
-
write_inheritable_attribute(
|
41
|
+
def self.newrelic_write_attr(attr_name, value) # :nodoc:
|
42
|
+
write_inheritable_attribute(attr_name, value)
|
17
43
|
end
|
18
|
-
|
19
|
-
|
44
|
+
|
45
|
+
def self.newrelic_read_attr(attr_name) # :nodoc:
|
46
|
+
read_inheritable_attribute(attr_name)
|
20
47
|
end
|
21
|
-
|
48
|
+
|
22
49
|
# determine the path that is used in the metric name for
|
23
50
|
# the called controller action
|
24
51
|
def newrelic_metric_path(action_name_override = nil)
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'dispatcher'
|
2
|
-
|
3
2
|
# NewRelic RPM instrumentation for http request dispatching (Routes mapping)
|
4
3
|
# Note, the dispatcher class from no module into into the ActionController module
|
5
4
|
# in Rails 2.0. Thus we need to check for both
|
@@ -7,23 +6,32 @@ if defined? ActionController::Dispatcher
|
|
7
6
|
target = ActionController::Dispatcher
|
8
7
|
elsif defined? Dispatcher
|
9
8
|
target = Dispatcher
|
10
|
-
else
|
11
|
-
target = nil
|
12
9
|
end
|
13
10
|
|
11
|
+
# NOTE TODO: maybe this should be done with a middleware?
|
14
12
|
if target
|
13
|
+
require 'action_pack/version'
|
15
14
|
NewRelic::Agent.instance.log.debug "Adding #{target} instrumentation"
|
16
15
|
|
17
|
-
# in Rails 2.3 (Rack-based) we don't want to add instrumentation on class level
|
18
|
-
unless defined? ::Rails::Rack
|
19
|
-
target = target.class_eval { class << self; self; end }
|
20
|
-
end
|
21
|
-
|
22
16
|
target.class_eval do
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
17
|
+
if ActionPack::VERSION::MAJOR >= 2
|
18
|
+
# In versions later that 1.* the dispatcher callbacks are used
|
19
|
+
include NewRelic::Agent::Instrumentation::DispatcherInstrumentation
|
20
|
+
before_dispatch :newrelic_dispatcher_start
|
21
|
+
after_dispatch :newrelic_dispatcher_finish
|
22
|
+
def newrelic_response_code
|
23
|
+
(@response.headers['Status']||'200')[0..2] if ActionPack::VERSION::MAJOR == 2 && ActionPack::VERSION::MINOR < 3
|
24
|
+
end
|
25
|
+
else
|
26
|
+
# In version 1.2.* the instrumentation is done by method chaining
|
27
|
+
# the static dispatch method on the dispatcher class
|
28
|
+
extend NewRelic::Agent::Instrumentation::DispatcherInstrumentation
|
29
|
+
class << self
|
30
|
+
alias_method :dispatch_without_newrelic, :dispatch
|
31
|
+
alias_method :dispatch, :dispatch_newrelic
|
32
|
+
def newrelic_response_code; end
|
33
|
+
end
|
34
|
+
end
|
27
35
|
end
|
28
36
|
else
|
29
37
|
NewRelic::Agent.instance.log.debug "WARNING: Dispatcher instrumentation not added"
|
@@ -1,11 +1,10 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
ActionController::Base.class_eval do
|
3
3
|
|
4
4
|
def newrelic_notice_error(exception)
|
5
|
-
|
5
|
+
filtered_params = (respond_to? :filter_parameters) ? filter_parameters(params) : params
|
6
6
|
|
7
|
-
NewRelic::Agent.agent.error_collector.notice_error(
|
8
|
-
local_params, exception)
|
7
|
+
NewRelic::Agent.agent.error_collector.notice_error(exception, request, newrelic_metric_path, filtered_params)
|
9
8
|
end
|
10
9
|
|
11
10
|
def rescue_action_with_newrelic_trace(exception)
|
@@ -21,3 +20,5 @@ ActionController::Base.class_eval do
|
|
21
20
|
protected :rescue_action
|
22
21
|
|
23
22
|
end if defined? ActionController
|
23
|
+
|
24
|
+
Object.send :include, NewRelic::Agent::Instrumentation::ErrorInstrumentation
|
@@ -1,171 +1,195 @@
|
|
1
|
+
module NewRelic::Agent
|
1
2
|
|
2
|
-
|
3
|
-
|
4
|
-
# Original method preserved for API backward compatibility
|
5
|
-
def trace_method_execution (metric_name, push_scope, produce_metric, deduct_call_time_from_parent, &block)
|
6
|
-
if push_scope
|
7
|
-
trace_method_execution_with_scope(metric_name, produce_metric, deduct_call_time_from_parent, &block)
|
8
|
-
else
|
9
|
-
trace_method_execution_no_scope(metric_name, &block)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
# This is duplicated inline in add_method_tracer
|
14
|
-
def trace_method_execution_no_scope(metric_name)
|
15
|
-
t0 = Time.now.to_f
|
16
|
-
stats = @@newrelic_stats_engine.get_stats_no_scope metric_name
|
17
|
-
|
18
|
-
result = yield
|
19
|
-
duration = Time.now.to_f - t0 # for some reason this is 3 usec faster than Time - Time
|
20
|
-
stats.trace_call(duration, duration)
|
21
|
-
result
|
22
|
-
end
|
3
|
+
# These are stubs for API methods installed when the agent is disabled.
|
23
4
|
|
24
|
-
|
5
|
+
module MethodTracerShim
|
6
|
+
def trace_method_execution(*args); yield; end
|
7
|
+
|
8
|
+
def trace_method_execution_no_scope(metric_name); yield; end
|
9
|
+
def trace_method_execution_with_scope(*args); yield; end
|
10
|
+
def add_method_tracer (*args); end
|
11
|
+
def remove_method_tracer(*args); end
|
12
|
+
end
|
25
13
|
|
26
|
-
|
27
|
-
|
14
|
+
# These are the class methods added to support installing custom
|
15
|
+
# metric tracers and executing for individual metrics.
|
16
|
+
# This module is included in class Module.
|
17
|
+
module MethodTracer
|
28
18
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
19
|
+
# Deprecated: original method preserved for API backward compatibility, use one of the other
|
20
|
+
# +trace_method..+ calls.
|
21
|
+
def trace_method_execution(metric_name, push_scope, produce_metric, deduct_call_time_from_parent, &block)
|
22
|
+
if push_scope
|
23
|
+
trace_method_execution_with_scope(metric_name, produce_metric, deduct_call_time_from_parent, &block)
|
24
|
+
else
|
25
|
+
trace_method_execution_no_scope(metric_name, &block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Trace a given block with stats assigned to the given metric_name. It does not
|
30
|
+
# provide scoped measurements, meaning whatever is being traced will not 'blame the
|
31
|
+
# Controller'--that is to say appear in the breakdown chart.
|
32
|
+
# This is duplicated inline in #add_method_tracer.
|
33
|
+
def trace_method_execution_no_scope(metric_name)
|
34
|
+
t0 = Time.now.to_f
|
35
|
+
stats = NewRelic::Agent.instance.stats_engine.get_stats_no_scope metric_name
|
33
36
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
begin
|
38
|
+
yield
|
39
|
+
ensure
|
40
|
+
duration = Time.now.to_f - t0 # for some reason this is 3 usec faster than Time - Time
|
41
|
+
stats.trace_call(duration, duration)
|
42
|
+
end
|
38
43
|
end
|
39
44
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
duration = t1 - t0
|
45
|
+
# Trace a given block with stats, like #trace_method_execution_no_scope but
|
46
|
+
def trace_method_execution_with_scope(metric_name, produce_metric, deduct_call_time_from_parent, scoped_metric_only=false)
|
47
|
+
t0 = Time.now.to_f
|
48
|
+
stats = nil
|
45
49
|
|
46
50
|
begin
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
51
|
+
# Keep a reference to the scope we are pushing so we can do a sanity check making
|
52
|
+
# sure when we pop we get the one we 'expected'
|
53
|
+
expected_scope = NewRelic::Agent.instance.stats_engine.push_scope(metric_name, t0, deduct_call_time_from_parent)
|
54
|
+
|
55
|
+
stats = NewRelic::Agent.instance.stats_engine.get_stats(metric_name, true, scoped_metric_only) if produce_metric
|
53
56
|
rescue => e
|
54
|
-
NewRelic::
|
55
|
-
NewRelic::
|
57
|
+
NewRelic::Control.instance.log.error("Caught exception in trace_method_execution header. Metric name = #{metric_name}, exception = #{e}")
|
58
|
+
NewRelic::Control.instance.log.error(e.backtrace.join("\n"))
|
59
|
+
end
|
60
|
+
|
61
|
+
begin
|
62
|
+
yield
|
63
|
+
ensure
|
64
|
+
t1 = Time.now.to_f
|
65
|
+
duration = t1 - t0
|
66
|
+
|
67
|
+
begin
|
68
|
+
if expected_scope
|
69
|
+
scope = NewRelic::Agent.instance.stats_engine.pop_scope expected_scope, duration, t1
|
70
|
+
|
71
|
+
exclusive = duration - scope.children_time
|
72
|
+
stats.trace_call(duration, exclusive) if stats
|
73
|
+
end
|
74
|
+
rescue => e
|
75
|
+
NewRelic::Control.instance.log.error("Caught exception in trace_method_execution footer. Metric name = #{metric_name}, exception = #{e}")
|
76
|
+
NewRelic::Control.instance.log.error(e.backtrace.join("\n"))
|
77
|
+
end
|
56
78
|
end
|
57
79
|
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# Add a method tracer to the specified method.
|
61
|
-
# metric_name_code is ruby code that determines the name of the
|
62
|
-
# metric to be collected during tracing. As such, the code
|
63
|
-
# should be provided in 'single quote' strings rather than
|
64
|
-
# "double quote" strings, so that #{} evaluation happens
|
65
|
-
# at traced method execution time.
|
66
|
-
# Example: tracing a method :foo, where the metric name is
|
67
|
-
# the first argument converted to a string
|
68
|
-
# add_method_tracer :foo, '#{args.first.to_s}'
|
69
|
-
# statically defined metric names can be specified as regular strings
|
70
|
-
# push_scope specifies whether this method tracer should push
|
71
|
-
# the metric name onto the scope stack.
|
72
|
-
def add_method_tracer (method_name, metric_name_code, options = {})
|
73
|
-
return unless NewRelic::Agent.agent.config.tracers_enabled?
|
74
|
-
|
75
|
-
@@newrelic_stats_engine ||= NewRelic::Agent.agent.stats_engine
|
76
|
-
|
77
|
-
if !options.is_a?(Hash)
|
78
|
-
options = {:push_scope => options}
|
79
|
-
end
|
80
|
-
# options[:push_scope] true if we are noting the scope of this for
|
81
|
-
# stats collection as well as the transaction tracing
|
82
|
-
options[:push_scope] = true if options[:push_scope].nil?
|
83
|
-
# options[:metric] true if you are tracking stats for a metric, otherwise
|
84
|
-
# it's just for transaction tracing.
|
85
|
-
options[:metric] = true if options[:metric].nil?
|
86
|
-
options[:deduct_call_time_from_parent] = false if options[:deduct_call_time_from_parent].nil? && !options[:metric]
|
87
|
-
options[:deduct_call_time_from_parent] = true if options[:deduct_call_time_from_parent].nil?
|
88
|
-
options[:code_header] ||= ""
|
89
|
-
options[:code_footer] ||= ""
|
90
|
-
|
91
|
-
klass = (self === Module) ? "self" : "self.class"
|
92
|
-
|
93
|
-
unless method_defined?(method_name) || private_method_defined?(method_name)
|
94
|
-
NewRelic::Config.instance.log.warn("Did not trace #{self}##{method_name} because that method does not exist")
|
95
|
-
return
|
96
|
-
end
|
97
|
-
|
98
|
-
traced_method_name = _traced_method_name(method_name, metric_name_code)
|
99
|
-
if method_defined? traced_method_name
|
100
|
-
NewRelic::Config.instance.log.warn("Attempt to trace a method twice with the same metric: Method = #{method_name}, Metric Name = #{metric_name_code}")
|
101
|
-
return
|
102
|
-
end
|
103
|
-
|
104
|
-
fail "Can't add a tracer where push_scope is false and metric is false" if options[:push_scope] == false && !options[:metric]
|
105
80
|
|
106
|
-
|
107
|
-
|
108
|
-
|
81
|
+
# Add a method tracer to the specified method.
|
82
|
+
# metric_name_code is ruby code that determines the name of the
|
83
|
+
# metric to be collected during tracing. As such, the code
|
84
|
+
# should be provided in 'single quote' strings rather than
|
85
|
+
# "double quote" strings, so that #{} evaluation happens
|
86
|
+
# at traced method execution time.
|
87
|
+
#
|
88
|
+
# Example: tracing a method :foo, where the metric name is
|
89
|
+
# the first argument converted to a string
|
90
|
+
#
|
91
|
+
# add_method_tracer :foo, '#{args.first.to_s}'
|
92
|
+
#
|
93
|
+
# Statically defined metric names can be specified as regular strings.
|
94
|
+
# The option +:push_scope+ specifies whether this method tracer should
|
95
|
+
# keep track of the caller so it will show up in controller breakdown
|
96
|
+
# pie charts.
|
97
|
+
def add_method_tracer (method_name, metric_name_code, options = {})
|
98
|
+
if !options.is_a?(Hash)
|
99
|
+
options = {:push_scope => options}
|
100
|
+
end
|
101
|
+
# options[:push_scope] true if we are noting the scope of this for
|
102
|
+
# stats collection as well as the transaction tracing
|
103
|
+
options[:push_scope] = true if options[:push_scope].nil?
|
104
|
+
# options[:metric] true if you are tracking stats for a metric, otherwise
|
105
|
+
# it's just for transaction tracing.
|
106
|
+
options[:metric] = true if options[:metric].nil?
|
107
|
+
options[:deduct_call_time_from_parent] = false if options[:deduct_call_time_from_parent].nil? && !options[:metric]
|
108
|
+
options[:deduct_call_time_from_parent] = true if options[:deduct_call_time_from_parent].nil?
|
109
|
+
options[:code_header] ||= ""
|
110
|
+
options[:code_footer] ||= ""
|
111
|
+
options[:scoped_metric_only] ||= false
|
112
|
+
|
113
|
+
klass = (self === Module) ? "self" : "self.class"
|
114
|
+
|
115
|
+
unless method_defined?(method_name) || private_method_defined?(method_name)
|
116
|
+
NewRelic::Control.instance.log.warn("Did not trace #{self}##{method_name} because that method does not exist")
|
117
|
+
return
|
118
|
+
end
|
119
|
+
|
120
|
+
traced_method_name = _traced_method_name(method_name, metric_name_code)
|
121
|
+
if method_defined? traced_method_name
|
122
|
+
NewRelic::Control.instance.log.warn("Attempt to trace a method twice with the same metric: Method = #{method_name}, Metric Name = #{metric_name_code}")
|
123
|
+
return
|
124
|
+
end
|
125
|
+
|
126
|
+
fail "Can't add a tracer where push_scope is false and metric is false" if options[:push_scope] == false && !options[:metric]
|
127
|
+
|
128
|
+
if options[:push_scope] == false
|
129
|
+
code = <<-CODE
|
109
130
|
def #{_traced_method_name(method_name, metric_name_code)}(*args, &block)
|
110
131
|
#{options[:code_header]}
|
111
132
|
|
112
133
|
t0 = Time.now.to_f
|
113
|
-
stats =
|
134
|
+
stats = NewRelic::Agent.instance.stats_engine.get_stats_no_scope "#{metric_name_code}"
|
114
135
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
136
|
+
begin
|
137
|
+
#{_untraced_method_name(method_name, metric_name_code)}(*args, &block)
|
138
|
+
ensure
|
139
|
+
duration = Time.now.to_f - t0
|
140
|
+
stats.trace_call(duration, duration) # for some reason this is 3 usec faster than Time - Time
|
141
|
+
#{options[:code_footer]}
|
142
|
+
end
|
120
143
|
end
|
121
144
|
CODE
|
122
|
-
|
123
|
-
|
145
|
+
else
|
146
|
+
code = <<-CODE
|
124
147
|
def #{_traced_method_name(method_name, metric_name_code)}(*args, &block)
|
125
148
|
#{options[:code_header]}
|
126
|
-
result = #{klass}.trace_method_execution_with_scope("#{metric_name_code}", #{options[:metric]}, #{options[:deduct_call_time_from_parent]}) do
|
149
|
+
result = #{klass}.trace_method_execution_with_scope("#{metric_name_code}", #{options[:metric]}, #{options[:deduct_call_time_from_parent]}, #{options[:scoped_metric_only]}) do
|
127
150
|
#{_untraced_method_name(method_name, metric_name_code)}(*args, &block)
|
128
151
|
end
|
129
152
|
#{options[:code_footer]}
|
130
153
|
result
|
131
154
|
end
|
132
155
|
CODE
|
156
|
+
end
|
157
|
+
|
158
|
+
class_eval code, __FILE__, __LINE__
|
159
|
+
|
160
|
+
alias_method _untraced_method_name(method_name, metric_name_code), method_name
|
161
|
+
alias_method method_name, _traced_method_name(method_name, metric_name_code)
|
162
|
+
|
163
|
+
NewRelic::Control.instance.log.debug("Traced method: class = #{self}, method = #{method_name}, "+
|
164
|
+
"metric = '#{metric_name_code}', options: #{options.inspect}, ")
|
133
165
|
end
|
166
|
+
|
167
|
+
# Not recommended for production use, because tracers must be removed in reverse-order
|
168
|
+
# from when they were added, or else other tracers that were added to the same method
|
169
|
+
# may get removed as well.
|
170
|
+
def remove_method_tracer(method_name, metric_name_code)
|
171
|
+
return unless NewRelic::Control.instance.agent_enabled?
|
134
172
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
173
|
+
if method_defined? "#{_traced_method_name(method_name, metric_name_code)}"
|
174
|
+
alias_method method_name, "#{_untraced_method_name(method_name, metric_name_code)}"
|
175
|
+
undef_method "#{_traced_method_name(method_name, metric_name_code)}"
|
176
|
+
else
|
177
|
+
raise "No tracer for '#{metric_name_code}' on method '#{method_name}'"
|
178
|
+
end
|
179
|
+
end
|
139
180
|
|
140
|
-
|
141
|
-
"metric = '#{metric_name_code}', options: #{options}, ")
|
142
|
-
end
|
143
|
-
|
144
|
-
# Not recommended for production use, because tracers must be removed in reverse-order
|
145
|
-
# from when they were added, or else other tracers that were added to the same method
|
146
|
-
# may get removed as well.
|
147
|
-
def remove_method_tracer(method_name, metric_name_code)
|
148
|
-
return unless NewRelic::Agent.agent.config.tracers_enabled?
|
181
|
+
private
|
149
182
|
|
150
|
-
|
151
|
-
alias_method method_name, "#{_untraced_method_name(method_name, metric_name_code)}"
|
152
|
-
undef_method "#{_traced_method_name(method_name, metric_name_code)}"
|
153
|
-
else
|
154
|
-
raise "No tracer for '#{metric_name_code}' on method '#{method_name}'"
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
private
|
159
|
-
|
160
|
-
def _untraced_method_name(method_name, metric_name)
|
183
|
+
def _untraced_method_name(method_name, metric_name)
|
161
184
|
"#{_sanitize_name(method_name)}_without_trace_#{_sanitize_name(metric_name)}"
|
162
|
-
|
163
|
-
|
164
|
-
|
185
|
+
end
|
186
|
+
|
187
|
+
def _traced_method_name(method_name, metric_name)
|
165
188
|
"#{_sanitize_name(method_name)}_with_trace_#{_sanitize_name(metric_name)}"
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
189
|
+
end
|
190
|
+
|
191
|
+
def _sanitize_name(name)
|
192
|
+
name.to_s.tr('^a-z,A-Z,0-9', '_')
|
193
|
+
end
|
170
194
|
end
|
171
195
|
end
|