newrelic_rpm 2.8.0
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/LICENSE +37 -0
- data/README +93 -0
- data/Rakefile +38 -0
- data/install.rb +37 -0
- data/lib/new_relic/agent.rb +26 -0
- data/lib/new_relic/agent/agent.rb +762 -0
- data/lib/new_relic/agent/chained_call.rb +13 -0
- data/lib/new_relic/agent/collection_helper.rb +81 -0
- data/lib/new_relic/agent/error_collector.rb +105 -0
- data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +95 -0
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +151 -0
- data/lib/new_relic/agent/instrumentation/data_mapper.rb +90 -0
- data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +105 -0
- data/lib/new_relic/agent/instrumentation/memcache.rb +18 -0
- data/lib/new_relic/agent/instrumentation/merb/controller.rb +17 -0
- data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +15 -0
- data/lib/new_relic/agent/instrumentation/merb/errors.rb +6 -0
- data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +35 -0
- data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +27 -0
- data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +30 -0
- data/lib/new_relic/agent/instrumentation/rails/errors.rb +23 -0
- data/lib/new_relic/agent/instrumentation/rails/rails.rb +6 -0
- data/lib/new_relic/agent/method_tracer.rb +171 -0
- data/lib/new_relic/agent/patch_const_missing.rb +31 -0
- data/lib/new_relic/agent/samplers/cpu.rb +29 -0
- data/lib/new_relic/agent/samplers/memory.rb +55 -0
- data/lib/new_relic/agent/samplers/mongrel.rb +26 -0
- data/lib/new_relic/agent/stats_engine.rb +241 -0
- data/lib/new_relic/agent/synchronize.rb +40 -0
- data/lib/new_relic/agent/transaction_sampler.rb +281 -0
- data/lib/new_relic/agent/worker_loop.rb +128 -0
- data/lib/new_relic/api/deployments.rb +92 -0
- data/lib/new_relic/config.rb +194 -0
- data/lib/new_relic/config/merb.rb +35 -0
- data/lib/new_relic/config/rails.rb +113 -0
- data/lib/new_relic/config/ruby.rb +9 -0
- data/lib/new_relic/local_environment.rb +108 -0
- data/lib/new_relic/merbtasks.rb +6 -0
- data/lib/new_relic/metric_data.rb +26 -0
- data/lib/new_relic/metric_spec.rb +39 -0
- data/lib/new_relic/metrics.rb +7 -0
- data/lib/new_relic/noticed_error.rb +21 -0
- data/lib/new_relic/shim_agent.rb +95 -0
- data/lib/new_relic/stats.rb +359 -0
- data/lib/new_relic/transaction_analysis.rb +122 -0
- data/lib/new_relic/transaction_sample.rb +499 -0
- data/lib/new_relic/version.rb +111 -0
- data/lib/new_relic_api.rb +275 -0
- data/lib/newrelic_rpm.rb +27 -0
- data/lib/tasks/agent_tests.rake +14 -0
- data/lib/tasks/all.rb +4 -0
- data/lib/tasks/install.rake +7 -0
- data/newrelic.yml +137 -0
- data/recipes/newrelic.rb +46 -0
- data/test/config/newrelic.yml +26 -0
- data/test/config/test_config.rb +9 -0
- data/test/new_relic/agent/mock_ar_connection.rb +40 -0
- data/test/new_relic/agent/mock_scope_listener.rb +23 -0
- data/test/new_relic/agent/model_fixture.rb +17 -0
- data/test/new_relic/agent/tc_active_record.rb +91 -0
- data/test/new_relic/agent/tc_agent.rb +112 -0
- data/test/new_relic/agent/tc_collection_helper.rb +104 -0
- data/test/new_relic/agent/tc_controller.rb +98 -0
- data/test/new_relic/agent/tc_dispatcher_instrumentation.rb +52 -0
- data/test/new_relic/agent/tc_error_collector.rb +127 -0
- data/test/new_relic/agent/tc_method_tracer.rb +306 -0
- data/test/new_relic/agent/tc_stats_engine.rb +218 -0
- data/test/new_relic/agent/tc_synchronize.rb +37 -0
- data/test/new_relic/agent/tc_transaction_sample.rb +175 -0
- data/test/new_relic/agent/tc_transaction_sample_builder.rb +200 -0
- data/test/new_relic/agent/tc_transaction_sampler.rb +305 -0
- data/test/new_relic/agent/tc_worker_loop.rb +101 -0
- data/test/new_relic/agent/testable_agent.rb +13 -0
- data/test/new_relic/tc_config.rb +36 -0
- data/test/new_relic/tc_deployments_api.rb +37 -0
- data/test/new_relic/tc_environment.rb +94 -0
- data/test/new_relic/tc_metric_spec.rb +150 -0
- data/test/new_relic/tc_shim_agent.rb +9 -0
- data/test/new_relic/tc_stats.rb +141 -0
- data/test/test_helper.rb +39 -0
- data/test/ui/tc_newrelic_helper.rb +44 -0
- data/ui/controllers/newrelic_controller.rb +200 -0
- data/ui/helpers/google_pie_chart.rb +55 -0
- data/ui/helpers/newrelic_helper.rb +286 -0
- data/ui/views/layouts/newrelic_default.rhtml +49 -0
- data/ui/views/newrelic/_explain_plans.rhtml +27 -0
- data/ui/views/newrelic/_sample.rhtml +12 -0
- data/ui/views/newrelic/_segment.rhtml +28 -0
- data/ui/views/newrelic/_segment_row.rhtml +14 -0
- data/ui/views/newrelic/_show_sample_detail.rhtml +22 -0
- data/ui/views/newrelic/_show_sample_sql.rhtml +19 -0
- data/ui/views/newrelic/_show_sample_summary.rhtml +3 -0
- data/ui/views/newrelic/_sql_row.rhtml +11 -0
- data/ui/views/newrelic/_stack_trace.rhtml +30 -0
- data/ui/views/newrelic/_table.rhtml +12 -0
- data/ui/views/newrelic/explain_sql.rhtml +45 -0
- data/ui/views/newrelic/images/arrow-close.png +0 -0
- data/ui/views/newrelic/images/arrow-open.png +0 -0
- data/ui/views/newrelic/images/blue_bar.gif +0 -0
- data/ui/views/newrelic/images/gray_bar.gif +0 -0
- data/ui/views/newrelic/index.rhtml +37 -0
- data/ui/views/newrelic/javascript/transaction_sample.js +107 -0
- data/ui/views/newrelic/sample_not_found.rhtml +2 -0
- data/ui/views/newrelic/show_sample.rhtml +62 -0
- data/ui/views/newrelic/show_source.rhtml +3 -0
- data/ui/views/newrelic/stylesheets/style.css +394 -0
- metadata +180 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
module NewRelic::Agent::CollectionHelper
|
2
|
+
# Transform parameter hash into a hash whose values are strictly
|
3
|
+
# strings
|
4
|
+
def normalize_params(params)
|
5
|
+
case params
|
6
|
+
when Symbol, FalseClass, TrueClass, nil:
|
7
|
+
params
|
8
|
+
when Numeric
|
9
|
+
truncate(params.to_s)
|
10
|
+
when String
|
11
|
+
truncate(params)
|
12
|
+
when Hash:
|
13
|
+
new_params = {}
|
14
|
+
params.each do | key, value |
|
15
|
+
new_params[truncate(normalize_params(key),32)] = normalize_params(value)
|
16
|
+
end
|
17
|
+
new_params
|
18
|
+
when Enumerable:
|
19
|
+
# We only want the first 20 values of any enumerable. Invoking to_a.first(20) works but
|
20
|
+
# the to_a call might be expensive, so we'll just build it manually, even though it's
|
21
|
+
# more verbose.
|
22
|
+
new_values = []
|
23
|
+
count = 1
|
24
|
+
params.each do | item |
|
25
|
+
new_values << normalize_params(item)
|
26
|
+
break if (count += 1) > 20
|
27
|
+
end
|
28
|
+
new_values
|
29
|
+
else
|
30
|
+
truncate(flatten(params))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return an array of strings (backtrace), cleaned up for readability
|
35
|
+
# Return nil if there is no backtrace
|
36
|
+
|
37
|
+
def strip_nr_from_backtrace(backtrace)
|
38
|
+
if backtrace
|
39
|
+
# strip newrelic from the trace
|
40
|
+
backtrace = backtrace.reject {|line| line =~ /new_relic\/agent\// }
|
41
|
+
# rename methods back to their original state
|
42
|
+
backtrace = backtrace.collect {|line| line.gsub /_without_(newrelic|trace)/, ""}
|
43
|
+
end
|
44
|
+
backtrace
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Convert any kind of object to a descriptive string
|
50
|
+
# Only call this on unknown objects. Otherwise call to_s.
|
51
|
+
def flatten(object)
|
52
|
+
s =
|
53
|
+
if object.respond_to? :inspect
|
54
|
+
object.inspect
|
55
|
+
elsif object.respond_to? :to_s
|
56
|
+
object.to_s
|
57
|
+
elsif object.nil?
|
58
|
+
"nil"
|
59
|
+
else
|
60
|
+
"#<#{object.class.to_s}>"
|
61
|
+
end
|
62
|
+
|
63
|
+
if !(s.instance_of? String)
|
64
|
+
s = "#<#{object.class.to_s}>"
|
65
|
+
end
|
66
|
+
|
67
|
+
s
|
68
|
+
end
|
69
|
+
|
70
|
+
def truncate(string, len=256)
|
71
|
+
if string.instance_of? Symbol
|
72
|
+
string
|
73
|
+
elsif string.nil?
|
74
|
+
""
|
75
|
+
elsif string.instance_of? String
|
76
|
+
string.to_s.gsub(/^(.{#{len}})(.*)/) {$2.blank? ? $1 : $1 + "..."}
|
77
|
+
else
|
78
|
+
truncate(flatten(string), len)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
|
2
|
+
module NewRelic::Agent
|
3
|
+
class ErrorCollector
|
4
|
+
include Synchronize
|
5
|
+
include CollectionHelper
|
6
|
+
|
7
|
+
MAX_ERROR_QUEUE_LENGTH = 20 unless defined? MAX_ERROR_QUEUE_LENGTH
|
8
|
+
|
9
|
+
attr_accessor :capture_params
|
10
|
+
attr_accessor :capture_source
|
11
|
+
attr_accessor :enabled
|
12
|
+
|
13
|
+
def initialize(agent = nil)
|
14
|
+
@agent = agent
|
15
|
+
@errors = []
|
16
|
+
# lookup of exception class names to ignore. Hash for fast access
|
17
|
+
@ignore = {}
|
18
|
+
@ignore_filter = nil
|
19
|
+
@capture_params = true
|
20
|
+
@capture_source = false
|
21
|
+
@enabled = true
|
22
|
+
end
|
23
|
+
|
24
|
+
def ignore_error_filter(&block)
|
25
|
+
@ignore_filter = block
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# errors is an array of Exception Class Names
|
30
|
+
#
|
31
|
+
def ignore(errors)
|
32
|
+
errors.each { |error| @ignore[error] = true; log.debug("Ignoring error: '#{error}'") }
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def notice_error(path, request_uri, params, exception)
|
37
|
+
|
38
|
+
return unless @enabled
|
39
|
+
return if @ignore[exception.class.name]
|
40
|
+
|
41
|
+
if @ignore_filter
|
42
|
+
exception = @ignore_filter.call(exception)
|
43
|
+
|
44
|
+
return if exception.nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
error_stat.increment_count
|
48
|
+
|
49
|
+
data = {}
|
50
|
+
|
51
|
+
data[:request_params] = normalize_params(params) if @capture_params
|
52
|
+
data[:custom_params] = normalize_params(@agent.custom_params) if @agent
|
53
|
+
|
54
|
+
data[:request_uri] = request_uri
|
55
|
+
|
56
|
+
data[:rails_root] = NewRelic::Config.instance.root
|
57
|
+
|
58
|
+
data[:file_name] = exception.file_name if exception.respond_to?('file_name')
|
59
|
+
data[:line_number] = exception.line_number if exception.respond_to?('line_number')
|
60
|
+
|
61
|
+
if @capture_source && exception.respond_to?('source_extract')
|
62
|
+
data[:source] = exception.source_extract
|
63
|
+
end
|
64
|
+
|
65
|
+
if exception.respond_to? 'original_exception'
|
66
|
+
inside_exception = exception.original_exception
|
67
|
+
else
|
68
|
+
inside_exception = exception
|
69
|
+
end
|
70
|
+
data[:stack_trace] = strip_nr_from_backtrace(inside_exception.backtrace)
|
71
|
+
noticed_error = NewRelic::NoticedError.new(path, data, exception)
|
72
|
+
|
73
|
+
synchronize do
|
74
|
+
if @errors.length >= MAX_ERROR_QUEUE_LENGTH
|
75
|
+
log.info("The error reporting queue has reached #{MAX_ERROR_QUEUE_LENGTH}. This error will not be reported to RPM: #{exception.message}")
|
76
|
+
else
|
77
|
+
@errors << noticed_error
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Get the errors currently queued up. Unsent errors are left
|
83
|
+
# over from a previous unsuccessful attempt to send them to the server.
|
84
|
+
# We first clear out all unsent errors before sending the newly queued errors.
|
85
|
+
def harvest_errors(unsent_errors)
|
86
|
+
if unsent_errors && !unsent_errors.empty?
|
87
|
+
return unsent_errors
|
88
|
+
else
|
89
|
+
synchronize do
|
90
|
+
errors = @errors
|
91
|
+
@errors = []
|
92
|
+
return errors
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
def error_stat
|
99
|
+
@error_stat ||= NewRelic::Agent.get_stats("Errors/all")
|
100
|
+
end
|
101
|
+
def log
|
102
|
+
NewRelic::Config.instance.log
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
# NewRelic instrumentation for ActiveRecord
|
3
|
+
if defined? ActiveRecord
|
4
|
+
|
5
|
+
ActiveRecord::Base.class_eval do
|
6
|
+
class << self
|
7
|
+
[:find_by_sql, :count].each do |find_method|
|
8
|
+
add_method_tracer find_method, 'ActiveRecord/#{self.name}/find'
|
9
|
+
add_method_tracer find_method, 'ActiveRecord/find', :push_scope => false
|
10
|
+
add_method_tracer find_method, 'ActiveRecord/all', :push_scope => false
|
11
|
+
end
|
12
|
+
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
|
+
|
19
|
+
add_method_tracer :destroy, 'ActiveRecord/#{self.class.name}/destroy'
|
20
|
+
add_method_tracer :destroy, 'ActiveRecord/destroy', :push_scope => false
|
21
|
+
add_method_tracer :destroy, 'ActiveRecord/all', :push_scope => false
|
22
|
+
end
|
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
|
29
|
+
|
30
|
+
def log_with_newrelic_instrumentation(sql, name, &block)
|
31
|
+
# if we aren't in a blamed context, then add one so that we can see that
|
32
|
+
# controllers are calling SQL directly
|
33
|
+
# we check scope_depth vs 2 since the controller is 1, and the
|
34
|
+
#
|
35
|
+
if NewRelic::Agent.instance.transaction_sampler.scope_depth < 2
|
36
|
+
self.class.trace_method_execution_with_scope "Database/DirectSQL", true, true do
|
37
|
+
log_with_capture_sql(sql, name, &block)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
log_with_capture_sql(sql, name, &block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def log_with_capture_sql(sql, name, &block)
|
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
|
49
|
+
else
|
50
|
+
config = nil
|
51
|
+
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
|
+
end
|
60
|
+
|
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
|
+
end
|
76
|
+
|
77
|
+
# instrumentation for associations
|
78
|
+
ActiveRecord::Associations::AssociationCollection.class_eval do
|
79
|
+
add_method_tracer :delete, 'ActiveRecord/#{@owner.class.name}/association delete'
|
80
|
+
end
|
81
|
+
=begin
|
82
|
+
# Consider enabling these in the future
|
83
|
+
class HasAndBelongsToManyAssociation
|
84
|
+
add_method_tracer :find, 'ActiveRecord/#{@owner.class.name}/association find'
|
85
|
+
add_method_tracer :create_record, 'ActiveRecord/#{@owner.class.name}/association create'
|
86
|
+
add_method_tracer :insert_record, 'ActiveRecord/#{@owner.class.name}/association insert'
|
87
|
+
end
|
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'
|
93
|
+
end
|
94
|
+
=end
|
95
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# NewRelic instrumentation for controllers
|
2
|
+
#
|
3
|
+
# This instrumentation is applied to the action controller by default if the agent
|
4
|
+
# is actively collecting statistics. It will collect statistics for the
|
5
|
+
# given action.
|
6
|
+
#
|
7
|
+
# In cases where you don't want to instrument the top level action, but instead
|
8
|
+
# have other methods which are dispatched to by your action, and you want to treat
|
9
|
+
# these as distinct actions, then what you need to do is call newrelic_ignore
|
10
|
+
# on the top level action, and manually instrument the called 'actions'.
|
11
|
+
#
|
12
|
+
# Here's an example of a controller with a send_message action which dispatches
|
13
|
+
# to more specific send_servicename methods. This results in the controller
|
14
|
+
# action stats showing up for send_servicename.
|
15
|
+
#
|
16
|
+
# MyController < ActionController::Base
|
17
|
+
# newrelic_ignore :only => 'send_message'
|
18
|
+
# # dispatch this action to the method given by the service parameter.
|
19
|
+
# def send_message
|
20
|
+
# service = params['service']
|
21
|
+
# dispatch_to_method = "send_messge_to_#{service}"
|
22
|
+
# perform_action_with_newrelic_trace(dispatch_to_method) do
|
23
|
+
# send dispatch_to_method, params['message']
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
|
28
|
+
module NewRelic::Agent::Instrumentation
|
29
|
+
module ControllerInstrumentation
|
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
|
+
def self.included(clazz)
|
34
|
+
clazz.extend(ClassMethods)
|
35
|
+
end
|
36
|
+
|
37
|
+
module ClassMethods
|
38
|
+
# Have NewRelic ignore actions in this controller. Specify the actions as hash options
|
39
|
+
# using :except and :only. If no actions are specified, all actions are ignored.
|
40
|
+
def newrelic_ignore(specifiers={})
|
41
|
+
if specifiers.empty?
|
42
|
+
self.newrelic_ignore_attr = true
|
43
|
+
elsif ! (Hash === specifiers)
|
44
|
+
logger.error "newrelic_ignore takes an optional hash with :only and :except lists of actions (illegal argument type '#{specifiers.class}')"
|
45
|
+
else
|
46
|
+
self.newrelic_ignore_attr = specifiers
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# Should be implemented in the controller class via the inheritable attribute mechanism.
|
50
|
+
def newrelic_ignore_attr=(value); end
|
51
|
+
def newrelic_ignore_attr; end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Must be implemented in the controller class:
|
55
|
+
# Determine the path that is used in the metric name for
|
56
|
+
# the called controller action. Of the form controller_path/action_name
|
57
|
+
#
|
58
|
+
def newrelic_metric_path(action_name_override = nil)
|
59
|
+
raise "Not implemented!"
|
60
|
+
end
|
61
|
+
|
62
|
+
@@newrelic_apdex_t = NewRelic::Agent.instance.apdex_t
|
63
|
+
# Perform the current action with NewRelic tracing. Used in a method
|
64
|
+
# chain via aliasing. Call directly if you want to instrument a specifc
|
65
|
+
# block as if it were an action. Pass the block along with the path.
|
66
|
+
# The metric is named according to the action name, or the given path if called
|
67
|
+
# directly.
|
68
|
+
def perform_action_with_newrelic_trace(*args)
|
69
|
+
agent = NewRelic::Agent.instance
|
70
|
+
stats_engine = agent.stats_engine
|
71
|
+
|
72
|
+
ignore_actions = self.class.newrelic_ignore_attr
|
73
|
+
# Skip instrumentation based on the value of 'do_not_trace' and if
|
74
|
+
# we aren't calling directly with a block.
|
75
|
+
should_skip = !block_given? && case ignore_actions
|
76
|
+
when nil: false
|
77
|
+
when Hash
|
78
|
+
only_actions = Array(ignore_actions[:only])
|
79
|
+
except_actions = Array(ignore_actions[:except])
|
80
|
+
only_actions.include?(action_name.to_sym) || (except_actions.any? && !except_actions.include?(action_name.to_sym))
|
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
|
95
|
+
end
|
96
|
+
|
97
|
+
Thread.current[:controller_ignored] = false
|
98
|
+
|
99
|
+
start = Time.now.to_f
|
100
|
+
agent.ensure_worker_thread_started
|
101
|
+
|
102
|
+
# generate metrics for all all controllers (no scope)
|
103
|
+
self.class.trace_method_execution_no_scope "Controller" do
|
104
|
+
# generate metrics for this specific action
|
105
|
+
# assuming the first argument, if present, is the action name
|
106
|
+
path = newrelic_metric_path(args.size > 0 ? args[0] : nil)
|
107
|
+
stats_engine.transaction_name ||= "Controller/#{path}" if stats_engine
|
108
|
+
|
109
|
+
self.class.trace_method_execution_with_scope "Controller/#{path}", true, true do
|
110
|
+
# send request and parameter info to the transaction sampler
|
111
|
+
|
112
|
+
local_params = (respond_to? :filter_parameters) ? filter_parameters(params) : params
|
113
|
+
|
114
|
+
agent.transaction_sampler.notice_transaction(path, request, local_params)
|
115
|
+
|
116
|
+
t = Process.times.utime + Process.times.stime
|
117
|
+
|
118
|
+
begin
|
119
|
+
# run the action
|
120
|
+
if block_given?
|
121
|
+
yield
|
122
|
+
else
|
123
|
+
perform_action_without_newrelic_trace(*args)
|
124
|
+
end
|
125
|
+
ensure
|
126
|
+
cpu_burn = (Process.times.utime + Process.times.stime) - t
|
127
|
+
agent.transaction_sampler.notice_transaction_cpu_time(cpu_burn)
|
128
|
+
|
129
|
+
duration = Time.now.to_f - start
|
130
|
+
# do the apdex bucketing
|
131
|
+
if duration <= @@newrelic_apdex_t
|
132
|
+
@@newrelic_apdex_overall.record_apdex_s cpu_burn # satisfied
|
133
|
+
stats_engine.get_stats_no_scope("Apdex/#{path}").record_apdex_s cpu_burn
|
134
|
+
elsif duration <= (4 * @@newrelic_apdex_t)
|
135
|
+
@@newrelic_apdex_overall.record_apdex_t cpu_burn # tolerating
|
136
|
+
stats_engine.get_stats_no_scope("Apdex/#{path}").record_apdex_t cpu_burn
|
137
|
+
else
|
138
|
+
@@newrelic_apdex_overall.record_apdex_f cpu_burn # frustrated
|
139
|
+
stats_engine.get_stats_no_scope("Apdex/#{path}").record_apdex_f cpu_burn
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
ensure
|
147
|
+
# clear out the name of the traced transaction under all circumstances
|
148
|
+
stats_engine.transaction_name = nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|