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
@@ -27,43 +27,32 @@ module NewRelic::Agent::CollectionHelper
|
|
27
27
|
|
28
28
|
def strip_nr_from_backtrace(backtrace)
|
29
29
|
if backtrace
|
30
|
+
# this is for 1.9.1, where strings no longer have Enumerable
|
31
|
+
backtrace = backtrace.split("\n") if String === backtrace
|
30
32
|
# strip newrelic from the trace
|
31
33
|
backtrace = backtrace.reject {|line| line =~ /new_relic\/agent\// }
|
32
34
|
# rename methods back to their original state
|
33
|
-
backtrace = backtrace.collect {|line| line.gsub
|
35
|
+
backtrace = backtrace.collect {|line| line.gsub(/_without_(newrelic|trace)/, "")}
|
34
36
|
end
|
35
37
|
backtrace
|
36
38
|
end
|
37
39
|
|
38
40
|
private
|
39
41
|
|
40
|
-
# Convert any kind of object to a
|
41
|
-
# Only call this on unknown objects. Otherwise call to_s.
|
42
|
+
# Convert any kind of object to a short string.
|
42
43
|
def flatten(object)
|
43
|
-
s =
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
elsif object.nil?
|
49
|
-
"nil"
|
50
|
-
else
|
51
|
-
"#<#{object.class.to_s}>"
|
52
|
-
end
|
53
|
-
|
54
|
-
if !(s.instance_of? String)
|
55
|
-
s = "#<#{object.class.to_s}>"
|
44
|
+
s = case object
|
45
|
+
when nil then ''
|
46
|
+
when object.instance_of?(String) then object
|
47
|
+
when String then String.new(object) # convert string subclasses to strings
|
48
|
+
else "#<#{object.class.to_s}>"
|
56
49
|
end
|
57
|
-
|
58
|
-
s
|
59
50
|
end
|
60
|
-
|
61
51
|
def truncate(string, len=256)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
elsif string.instance_of? String
|
52
|
+
case string
|
53
|
+
when Symbol then string
|
54
|
+
when nil then ""
|
55
|
+
when String
|
67
56
|
string.to_s.gsub(/^(.{#{len}})(.*)/) {$2.blank? ? $1 : $1 + "..."}
|
68
57
|
else
|
69
58
|
truncate(flatten(string), len)
|
@@ -1,13 +1,10 @@
|
|
1
1
|
|
2
2
|
module NewRelic::Agent
|
3
3
|
class ErrorCollector
|
4
|
-
include Synchronize
|
5
4
|
include CollectionHelper
|
6
5
|
|
7
6
|
MAX_ERROR_QUEUE_LENGTH = 20 unless defined? MAX_ERROR_QUEUE_LENGTH
|
8
7
|
|
9
|
-
attr_accessor :capture_params
|
10
|
-
attr_accessor :capture_source
|
11
8
|
attr_accessor :enabled
|
12
9
|
|
13
10
|
def initialize(agent = nil)
|
@@ -16,9 +13,17 @@ module NewRelic::Agent
|
|
16
13
|
# lookup of exception class names to ignore. Hash for fast access
|
17
14
|
@ignore = {}
|
18
15
|
@ignore_filter = nil
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
|
17
|
+
config = NewRelic::Control.instance.fetch('error_collector', {})
|
18
|
+
|
19
|
+
@enabled = config.fetch('enabled', true)
|
20
|
+
@capture_source = config.fetch('capture_source', true)
|
21
|
+
|
22
|
+
ignore_errors = config.fetch('ignore_errors', "")
|
23
|
+
ignore_errors = ignore_errors.split(",")
|
24
|
+
ignore_errors.each { |error| error.strip! }
|
25
|
+
ignore(ignore_errors)
|
26
|
+
@lock = Mutex.new
|
22
27
|
end
|
23
28
|
|
24
29
|
def ignore_error_filter(&block)
|
@@ -33,7 +38,7 @@ module NewRelic::Agent
|
|
33
38
|
end
|
34
39
|
|
35
40
|
|
36
|
-
def notice_error(
|
41
|
+
def notice_error(exception, request=nil, action_path=nil, filtered_params={})
|
37
42
|
|
38
43
|
return unless @enabled
|
39
44
|
return if @ignore[exception.class.name]
|
@@ -48,12 +53,19 @@ module NewRelic::Agent
|
|
48
53
|
|
49
54
|
data = {}
|
50
55
|
|
51
|
-
|
56
|
+
action_path ||= ''
|
57
|
+
|
58
|
+
data[:request_params] = normalize_params(filtered_params) if NewRelic::Control.instance.capture_params
|
59
|
+
|
52
60
|
data[:custom_params] = normalize_params(@agent.custom_params) if @agent
|
53
61
|
|
54
|
-
data[:request_uri] =
|
62
|
+
data[:request_uri] = request.path if request
|
63
|
+
data[:request_uri] ||= ""
|
64
|
+
|
65
|
+
data[:request_referer] = request.referer if request
|
66
|
+
data[:request_referer] ||= ""
|
55
67
|
|
56
|
-
data[:rails_root] = NewRelic::
|
68
|
+
data[:rails_root] = NewRelic::Control.instance.root
|
57
69
|
|
58
70
|
data[:file_name] = exception.file_name if exception.respond_to?('file_name')
|
59
71
|
data[:line_number] = exception.line_number if exception.respond_to?('line_number')
|
@@ -67,10 +79,12 @@ module NewRelic::Agent
|
|
67
79
|
else
|
68
80
|
inside_exception = exception
|
69
81
|
end
|
70
|
-
|
71
|
-
|
82
|
+
|
83
|
+
data[:stack_trace] = inside_exception.backtrace
|
84
|
+
|
85
|
+
noticed_error = NewRelic::NoticedError.new(action_path, data, exception)
|
72
86
|
|
73
|
-
synchronize do
|
87
|
+
@lock.synchronize do
|
74
88
|
if @errors.length >= MAX_ERROR_QUEUE_LENGTH
|
75
89
|
log.info("The error reporting queue has reached #{MAX_ERROR_QUEUE_LENGTH}. This error will not be reported to RPM: #{exception.message}")
|
76
90
|
else
|
@@ -86,7 +100,7 @@ module NewRelic::Agent
|
|
86
100
|
if unsent_errors && !unsent_errors.empty?
|
87
101
|
return unsent_errors
|
88
102
|
else
|
89
|
-
synchronize do
|
103
|
+
@lock.synchronize do
|
90
104
|
errors = @errors
|
91
105
|
@errors = []
|
92
106
|
return errors
|
@@ -99,7 +113,7 @@ module NewRelic::Agent
|
|
99
113
|
@error_stat ||= NewRelic::Agent.get_stats("Errors/all")
|
100
114
|
end
|
101
115
|
def log
|
102
|
-
NewRelic::
|
116
|
+
NewRelic::Control.instance.log
|
103
117
|
end
|
104
118
|
end
|
105
119
|
end
|
@@ -1,95 +1,82 @@
|
|
1
1
|
|
2
2
|
# NewRelic instrumentation for ActiveRecord
|
3
|
-
if defined?
|
3
|
+
if defined?(ActiveRecord::Base) && !NewRelic::Control.instance['skip_ar_instrumentation']
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
module NewRelic::Agent::Instrumentation::ActiveRecordInstrumentation
|
6
|
+
|
7
|
+
def self.included(instrumented_class)
|
8
|
+
instrumented_class.class_eval do
|
9
|
+
alias_method :log_without_newrelic_instrumentation, :log
|
10
|
+
alias_method :log, :log_with_newrelic_instrumentation
|
11
|
+
protected :log
|
11
12
|
end
|
12
13
|
end
|
13
|
-
[:save, :save!].each do |save_method|
|
14
|
-
add_method_tracer save_method, 'ActiveRecord/#{self.class.name}/save'
|
15
|
-
add_method_tracer save_method, 'ActiveRecord/save', :push_scope => false
|
16
|
-
add_method_tracer save_method, 'ActiveRecord/all', :push_scope => false
|
17
|
-
end
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# instrumentation to catch logged SQL statements in sampled transactions
|
25
|
-
|
26
|
-
ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
|
27
|
-
@@my_sql_defined = defined? ActiveRecord::ConnectionAdapters::MysqlAdapter
|
28
|
-
@@postgres_defined = defined? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
15
|
+
def active_record_all_stats
|
16
|
+
# need to lazy init this
|
17
|
+
@@active_record_all ||= NewRelic::Agent.instance.stats_engine.get_stats_no_scope("ActiveRecord/all")
|
18
|
+
end
|
29
19
|
|
30
20
|
def log_with_newrelic_instrumentation(sql, name, &block)
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
21
|
+
# Capture db config if we are going to try to get the explain plans
|
22
|
+
if (defined? ActiveRecord::ConnectionAdapters::MysqlAdapter && self.is_a?(ActiveRecord::ConnectionAdapters::MysqlAdapter)) ||
|
23
|
+
(defined? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter && self.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter))
|
24
|
+
supported_config = @config
|
25
|
+
end
|
26
|
+
if name && (parts = name.split " ") && parts.size == 2
|
27
|
+
model = parts.first
|
28
|
+
operation = parts.last.downcase
|
29
|
+
metric_name = case operation
|
30
|
+
when 'load' then 'find'
|
31
|
+
when 'indexes', 'columns' then nil # fall back to DirectSQL
|
32
|
+
when 'destroy', 'find', 'save', 'create' then operation
|
33
|
+
when 'update' then 'save'
|
34
|
+
else
|
35
|
+
if model == 'Join'
|
36
|
+
operation
|
37
|
+
end
|
38
38
|
end
|
39
|
-
|
40
|
-
|
39
|
+
metric = "ActiveRecord/#{model}/#{metric_name}" if metric_name
|
40
|
+
end
|
41
|
+
if metric.nil? && sql =~ /^(select|update|insert|delete)/i
|
42
|
+
# Could not determine the model/operation so let's find a better
|
43
|
+
# metric. If it doesn't match the regex, it's probably a show
|
44
|
+
# command or some DDL which we'll ignore.
|
45
|
+
metric = "Database/SQL/#{$1.downcase}"
|
41
46
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
if @@my_sql_defined && self.is_a?(ActiveRecord::ConnectionAdapters::MysqlAdapter)
|
46
|
-
config = @config
|
47
|
-
elsif @@postgres_defined && self.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
|
48
|
-
config = @config
|
47
|
+
|
48
|
+
if !metric
|
49
|
+
log_without_newrelic_instrumentation(sql, name, &block)
|
49
50
|
else
|
50
|
-
|
51
|
+
self.class.trace_method_execution_with_scope metric, true, true do
|
52
|
+
t0 = Time.now.to_f
|
53
|
+
result = log_without_newrelic_instrumentation(sql, name, &block)
|
54
|
+
duration = Time.now.to_f - t0
|
55
|
+
|
56
|
+
NewRelic::Agent.instance.transaction_sampler.notice_sql(sql, supported_config, duration)
|
57
|
+
# Record in the overall summary metric
|
58
|
+
active_record_all_stats.record_data_point(duration)
|
59
|
+
# Record in the summary metric for this operation
|
60
|
+
NewRelic::Agent.instance.stats_engine.get_stats_no_scope("ActiveRecord/#{metric_name}").record_data_point(duration) if metric_name
|
61
|
+
result
|
62
|
+
end
|
51
63
|
end
|
52
|
-
|
53
|
-
t0 = Time.now
|
54
|
-
result = log_without_newrelic_instrumentation(sql, name, &block)
|
55
|
-
|
56
|
-
NewRelic::Agent.instance.transaction_sampler.notice_sql(sql, config, Time.now - t0)
|
57
|
-
|
58
|
-
result
|
59
64
|
end
|
60
65
|
|
61
|
-
# Compare with #alias_method_chain, which is not available in
|
62
|
-
# Rails 1.1:
|
63
|
-
alias_method :log_without_newrelic_instrumentation, :log
|
64
|
-
alias_method :log, :log_with_newrelic_instrumentation
|
65
|
-
protected :log
|
66
|
-
|
67
|
-
add_method_tracer :log, 'Database/#{adapter_name}/#{args[1]}', :metric => false
|
68
|
-
add_method_tracer :log, 'Database/all', :push_scope => false
|
69
|
-
|
70
|
-
end
|
71
|
-
ActiveRecord::Associations::ClassMethods.class_eval do
|
72
|
-
add_method_tracer :find_with_associations, 'ActiveRecord/#{self.name}/find'
|
73
|
-
add_method_tracer :find_with_associations, 'ActiveRecord/find', :push_scope => false
|
74
|
-
add_method_tracer :find_with_associations, 'ActiveRecord/all', :push_scope => false
|
75
66
|
end
|
76
67
|
|
77
|
-
# instrumentation
|
78
|
-
ActiveRecord::
|
79
|
-
|
68
|
+
# instrumentation to catch logged SQL statements in sampled transactions
|
69
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.module_eval do
|
70
|
+
include ::NewRelic::Agent::Instrumentation::ActiveRecordInstrumentation
|
80
71
|
end
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
class HasManyAssociation
|
90
|
-
add_method_tracer :find, 'ActiveRecord/#{@owner.class.name}/association find'
|
91
|
-
add_method_tracer :insert_record, 'ActiveRecord/#{@owner.class.name}/association insert'
|
92
|
-
add_method_tracer :create_record, 'ActiveRecord/#{@owner.class.name}/association create'
|
72
|
+
|
73
|
+
# This instrumentation will add an extra scope to the transaction traces
|
74
|
+
# which will show the code surrounding the query, inside the model find_by_sql
|
75
|
+
# method.
|
76
|
+
ActiveRecord::Base.class_eval do
|
77
|
+
class << self
|
78
|
+
add_method_tracer :find_by_sql, 'ActiveRecord/#{self.name}/find_by_sql', :metric => false
|
93
79
|
end
|
94
|
-
|
80
|
+
end
|
81
|
+
|
95
82
|
end
|
@@ -28,38 +28,66 @@
|
|
28
28
|
module NewRelic::Agent::Instrumentation
|
29
29
|
module ControllerInstrumentation
|
30
30
|
|
31
|
-
@@newrelic_apdex_t = NewRelic::Agent.instance.apdex_t
|
32
|
-
@@newrelic_apdex_overall = NewRelic::Agent.instance.stats_engine.get_stats_no_scope("Apdex")
|
33
31
|
def self.included(clazz)
|
34
32
|
clazz.extend(ClassMethods)
|
35
33
|
end
|
36
34
|
|
35
|
+
# This module is for importing stubs when the agent is disabled
|
36
|
+
module ClassMethodsShim
|
37
|
+
def newrelic_ignore(*args); end
|
38
|
+
def newrelic_ignore_apdex(*args); end
|
39
|
+
end
|
40
|
+
|
41
|
+
module Shim
|
42
|
+
def self.included(clazz)
|
43
|
+
clazz.extend(ClassMethodsShim)
|
44
|
+
end
|
45
|
+
def newrelic_notice_error(*args); end
|
46
|
+
def new_relic_trace_controller_action(*args); yield; end
|
47
|
+
def newrelic_metric_path; end
|
48
|
+
def perform_action_with_newrelic_trace(*args); yield; end
|
49
|
+
end
|
50
|
+
|
37
51
|
module ClassMethods
|
38
52
|
# Have NewRelic ignore actions in this controller. Specify the actions as hash options
|
39
53
|
# using :except and :only. If no actions are specified, all actions are ignored.
|
40
54
|
def newrelic_ignore(specifiers={})
|
55
|
+
newrelic_ignore_aspect('do_not_trace', specifiers)
|
56
|
+
end
|
57
|
+
# Have NewRelic omit apdex measurements on the given actions. Typically used for
|
58
|
+
# actions that are not user facing or that skew your overall apdex measurement.
|
59
|
+
# Accepts :except and :only options, as with #newrelic_ignore.
|
60
|
+
def newrelic_ignore_apdex(specifiers={})
|
61
|
+
newrelic_ignore_aspect('ignore_apdex', specifiers)
|
62
|
+
end
|
63
|
+
|
64
|
+
def newrelic_ignore_aspect(property, specifiers={}) # :nodoc:
|
41
65
|
if specifiers.empty?
|
42
|
-
self.
|
66
|
+
self.newrelic_write_attr property, true
|
43
67
|
elsif ! (Hash === specifiers)
|
44
|
-
logger.error "
|
68
|
+
logger.error "newrelic_#{property} takes an optional hash with :only and :except lists of actions (illegal argument type '#{specifiers.class}')"
|
45
69
|
else
|
46
|
-
self.
|
70
|
+
self.newrelic_write_attr property, specifiers
|
47
71
|
end
|
48
72
|
end
|
49
|
-
|
50
|
-
|
51
|
-
def
|
73
|
+
|
74
|
+
# Should be monkey patched into the controller class implemented with the inheritable attribute mechanism.
|
75
|
+
def newrelic_write_attr(attr_name, value) # :nodoc:
|
76
|
+
instance_variable_set "@#{attr_name}", value
|
77
|
+
end
|
78
|
+
def newrelic_read_attr(attr_name) # :nodoc:
|
79
|
+
instance_variable_get "@#{attr_name}", value
|
80
|
+
end
|
52
81
|
end
|
53
82
|
|
54
83
|
# Must be implemented in the controller class:
|
55
84
|
# Determine the path that is used in the metric name for
|
56
85
|
# the called controller action. Of the form controller_path/action_name
|
57
86
|
#
|
58
|
-
def newrelic_metric_path(action_name_override = nil)
|
87
|
+
def newrelic_metric_path(action_name_override = nil) # :nodoc:
|
59
88
|
raise "Not implemented!"
|
60
89
|
end
|
61
90
|
|
62
|
-
@@newrelic_apdex_t = NewRelic::Agent.instance.apdex_t
|
63
91
|
# Perform the current action with NewRelic tracing. Used in a method
|
64
92
|
# chain via aliasing. Call directly if you want to instrument a specifc
|
65
93
|
# block as if it were an action. Pass the block along with the path.
|
@@ -69,45 +97,30 @@ module NewRelic::Agent::Instrumentation
|
|
69
97
|
agent = NewRelic::Agent.instance
|
70
98
|
stats_engine = agent.stats_engine
|
71
99
|
|
72
|
-
ignore_actions = self.class.newrelic_ignore_attr
|
73
100
|
# Skip instrumentation based on the value of 'do_not_trace' and if
|
74
101
|
# we aren't calling directly with a block.
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
else
|
82
|
-
true
|
83
|
-
end
|
84
|
-
if should_skip
|
85
|
-
begin
|
86
|
-
return perform_action_without_newrelic_trace(*args)
|
87
|
-
ensure
|
88
|
-
# Tell the dispatcher instrumentation that we ignored this action and it shouldn't
|
89
|
-
# be counted for the overall HTTP operations measurement. The if.. appears here
|
90
|
-
# because we might be ignoring the top level action instrumenting but instrumenting
|
91
|
-
# a direct invocation that already happened, so we need to make sure if this var
|
92
|
-
# has already been set to false we don't reset it.
|
93
|
-
Thread.current[:controller_ignored] = true if Thread.current[:controller_ignored].nil?
|
94
|
-
end
|
102
|
+
if !block_given? && is_filtered?(self.class.newrelic_read_attr('do_not_trace'))
|
103
|
+
# Tell the dispatcher instrumentation that we ignored this action and it shouldn't
|
104
|
+
# be counted for the overall HTTP operations measurement.
|
105
|
+
Thread.current[:controller_ignored] = true
|
106
|
+
|
107
|
+
return perform_action_without_newrelic_trace(*args)
|
95
108
|
end
|
96
109
|
|
97
|
-
|
110
|
+
# reset this in case we came through a code path where the top level controller is ignored
|
111
|
+
Thread.current[:controller_ignored] = nil
|
98
112
|
|
99
113
|
start = Time.now.to_f
|
100
114
|
agent.ensure_worker_thread_started
|
101
115
|
|
102
116
|
# generate metrics for all all controllers (no scope)
|
103
117
|
self.class.trace_method_execution_no_scope "Controller" do
|
104
|
-
# generate metrics for this specific action
|
105
118
|
# assuming the first argument, if present, is the action name
|
106
119
|
path = newrelic_metric_path(args.size > 0 ? args[0] : nil)
|
107
|
-
|
120
|
+
controller_metric = "Controller/#{path}"
|
108
121
|
|
109
|
-
self.class.trace_method_execution_with_scope
|
110
|
-
|
122
|
+
self.class.trace_method_execution_with_scope controller_metric, true, true do
|
123
|
+
stats_engine.transaction_name = controller_metric
|
111
124
|
|
112
125
|
local_params = (respond_to? :filter_parameters) ? filter_parameters(params) : params
|
113
126
|
|
@@ -115,6 +128,8 @@ module NewRelic::Agent::Instrumentation
|
|
115
128
|
|
116
129
|
t = Process.times.utime + Process.times.stime
|
117
130
|
|
131
|
+
failed = false
|
132
|
+
|
118
133
|
begin
|
119
134
|
# run the action
|
120
135
|
if block_given?
|
@@ -122,30 +137,57 @@ module NewRelic::Agent::Instrumentation
|
|
122
137
|
else
|
123
138
|
perform_action_without_newrelic_trace(*args)
|
124
139
|
end
|
140
|
+
rescue Exception => e
|
141
|
+
failed = true
|
142
|
+
raise e
|
125
143
|
ensure
|
126
144
|
cpu_burn = (Process.times.utime + Process.times.stime) - t
|
145
|
+
stats_engine.get_stats_no_scope("ControllerCPU/#{path}").record_data_point(cpu_burn)
|
127
146
|
agent.transaction_sampler.notice_transaction_cpu_time(cpu_burn)
|
128
147
|
|
129
|
-
duration = Time.now.to_f - start
|
130
148
|
# do the apdex bucketing
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
149
|
+
#
|
150
|
+
unless is_filtered?(self.class.newrelic_read_attr('ignore_apdex'))
|
151
|
+
duration = Time.now.to_f - start
|
152
|
+
controller_stat = stats_engine.get_custom_stats("Apdex/#{path}", NewRelic::ApdexStats)
|
153
|
+
case
|
154
|
+
when failed
|
155
|
+
apdex_overall_stat.record_apdex_f # frustrated
|
156
|
+
controller_stat.record_apdex_f
|
157
|
+
when duration <= NewRelic::Control.instance['apdex_t']
|
158
|
+
apdex_overall_stat.record_apdex_s # satisfied
|
159
|
+
controller_stat.record_apdex_s
|
160
|
+
when duration <= 4 * NewRelic::Control.instance['apdex_t']
|
161
|
+
apdex_overall_stat.record_apdex_t # tolerating
|
162
|
+
controller_stat.record_apdex_t
|
163
|
+
else
|
164
|
+
apdex_overall_stat.record_apdex_f # frustrated
|
165
|
+
controller_stat.record_apdex_f
|
166
|
+
end
|
140
167
|
end
|
141
|
-
|
142
168
|
end
|
143
169
|
end
|
144
170
|
end
|
145
|
-
|
146
171
|
ensure
|
147
172
|
# clear out the name of the traced transaction under all circumstances
|
148
173
|
stats_engine.transaction_name = nil
|
149
174
|
end
|
175
|
+
|
176
|
+
private
|
177
|
+
def apdex_overall_stat
|
178
|
+
@@newrelic_apdex_overall ||= NewRelic::Agent.instance.stats_engine.get_custom_stats("Apdex", NewRelic::ApdexStats)
|
179
|
+
end
|
180
|
+
|
181
|
+
def is_filtered?(ignore_actions)
|
182
|
+
case ignore_actions
|
183
|
+
when nil; false
|
184
|
+
when Hash
|
185
|
+
only_actions = Array(ignore_actions[:only])
|
186
|
+
except_actions = Array(ignore_actions[:except])
|
187
|
+
only_actions.include?(action_name.to_sym) || (except_actions.any? && !except_actions.include?(action_name.to_sym))
|
188
|
+
else
|
189
|
+
true
|
190
|
+
end
|
191
|
+
end
|
150
192
|
end
|
151
193
|
end
|