newrelic_rpm 3.6.0.83 → 3.6.1.85.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +27 -0
- data/Gemfile +2 -7
- data/README.md +1 -1
- data/lib/new_relic/agent/agent.rb +3 -2
- data/lib/new_relic/agent/autostart.rb +56 -0
- data/lib/new_relic/agent/browser_monitoring.rb +19 -14
- data/lib/new_relic/agent/configuration/defaults.rb +12 -2
- data/lib/new_relic/agent/configuration/environment_source.rb +4 -1
- data/lib/new_relic/agent/cross_app_monitor.rb +2 -1
- data/lib/new_relic/agent/cross_app_tracing.rb +19 -10
- data/lib/new_relic/agent/error_collector.rb +5 -4
- data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +204 -0
- data/lib/new_relic/agent/instrumentation/action_view_subscriber.rb +88 -0
- data/lib/new_relic/agent/instrumentation/active_record.rb +1 -1
- data/lib/new_relic/agent/instrumentation/active_record_helper.rb +1 -1
- data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +84 -0
- data/lib/new_relic/agent/instrumentation/browser_monitoring_timings.rb +3 -2
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +104 -87
- data/lib/new_relic/agent/instrumentation/evented_subscriber.rb +91 -0
- data/lib/new_relic/agent/instrumentation/memcache.rb +4 -4
- data/lib/new_relic/agent/instrumentation/merb/errors.rb +4 -4
- data/lib/new_relic/agent/instrumentation/rack.rb +1 -1
- data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +20 -20
- data/lib/new_relic/agent/instrumentation/rails/errors.rb +5 -5
- data/lib/new_relic/agent/instrumentation/rails3/errors.rb +3 -3
- data/lib/new_relic/agent/instrumentation/rails4/action_controller.rb +3 -25
- data/lib/new_relic/agent/instrumentation/rails4/action_view.rb +2 -115
- data/lib/new_relic/agent/instrumentation/rails4/active_record.rb +2 -82
- data/lib/new_relic/agent/instrumentation/rails4/errors.rb +3 -4
- data/lib/new_relic/agent/method_tracer.rb +93 -56
- data/lib/new_relic/agent/null_logger.rb +6 -0
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +9 -4
- data/lib/new_relic/agent/sql_sampler.rb +10 -6
- data/lib/new_relic/agent/stats_engine/metric_stats.rb +19 -3
- data/lib/new_relic/agent/stats_engine/transactions.rb +53 -34
- data/lib/new_relic/agent/system_info.rb +54 -0
- data/lib/new_relic/agent/thread.rb +2 -2
- data/lib/new_relic/agent/transaction/pop.rb +52 -0
- data/lib/new_relic/agent/transaction.rb +388 -0
- data/lib/new_relic/agent/transaction_info.rb +5 -13
- data/lib/new_relic/agent/transaction_sample_builder.rb +13 -20
- data/lib/new_relic/agent/transaction_sampler.rb +13 -15
- data/lib/new_relic/agent/uri_util.rb +35 -0
- data/lib/new_relic/agent.rb +54 -11
- data/lib/new_relic/build.rb +2 -2
- data/lib/new_relic/control/frameworks/rails.rb +0 -1
- data/lib/new_relic/control/frameworks/rails3.rb +2 -0
- data/lib/new_relic/control/frameworks/rails4.rb +0 -4
- data/lib/new_relic/control/instance_methods.rb +5 -19
- data/lib/new_relic/control/server_methods.rb +2 -0
- data/lib/new_relic/environment_report.rb +4 -34
- data/lib/new_relic/latest_changes.rb +1 -1
- data/lib/new_relic/local_environment.rb +0 -6
- data/lib/new_relic/metric_spec.rb +2 -2
- data/lib/new_relic/rack/error_collector.rb +6 -4
- data/lib/new_relic/transaction_sample.rb +7 -1
- data/lib/new_relic/version.rb +1 -1
- data/lib/newrelic_rpm.rb +2 -2
- data/newrelic.yml +20 -20
- data/test/config/test_control.rb +2 -2
- data/test/multiverse/suites/agent_only/audit_log_test.rb +1 -1
- data/test/multiverse/suites/agent_only/cross_application_tracing_test.rb +0 -2
- data/test/multiverse/suites/agent_only/logging_test.rb +1 -1
- data/test/multiverse/suites/agent_only/marshaling_test.rb +5 -3
- data/test/multiverse/suites/agent_only/rename_rule_test.rb +2 -0
- data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +96 -0
- data/test/multiverse/suites/agent_only/testing_app.rb +1 -0
- data/test/multiverse/suites/rails/error_tracing_test.rb +17 -29
- data/test/multiverse/suites/rails/queue_time_test.rb +8 -2
- data/test/multiverse/suites/rails/view_instrumentation_test.rb +6 -3
- data/test/multiverse/suites/resque/instrumentation_test.rb +1 -1
- data/test/multiverse/suites/sinatra/sinatra_error_tracing_test.rb +8 -0
- data/test/new_relic/agent/agent/connect_test.rb +2 -1
- data/test/new_relic/agent/agent/start_test.rb +0 -10
- data/test/new_relic/agent/agent_logger_test.rb +15 -0
- data/test/new_relic/agent/agent_test_controller.rb +6 -2
- data/test/new_relic/agent/agent_test_controller_test.rb +20 -69
- data/test/new_relic/agent/autostart_test.rb +67 -0
- data/test/new_relic/agent/browser_monitoring_test.rb +60 -38
- data/test/new_relic/agent/configuration/environment_source_test.rb +19 -17
- data/test/new_relic/agent/cross_app_monitor_test.rb +8 -0
- data/test/new_relic/agent/error_collector/notice_error_test.rb +0 -5
- data/test/new_relic/agent/error_collector_test.rb +8 -9
- data/test/new_relic/agent/instrumentation/action_controller_subscriber_test.rb +228 -0
- data/test/new_relic/agent/instrumentation/action_view_subscriber_test.rb +18 -34
- data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +5 -5
- data/test/new_relic/agent/instrumentation/active_record_subscriber_test.rb +8 -9
- data/test/new_relic/agent/instrumentation/browser_monitoring_timings_test.rb +1 -1
- data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +24 -38
- data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +126 -178
- data/test/new_relic/agent/instrumentation/rack_test.rb +1 -1
- data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +135 -151
- data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +153 -81
- data/test/new_relic/agent/method_tracer_test.rb +42 -33
- data/test/new_relic/agent/mock_scope_listener.rb +4 -4
- data/test/new_relic/agent/pipe_channel_manager_test.rb +4 -2
- data/test/new_relic/agent/rpm_agent_test.rb +86 -89
- data/test/new_relic/agent/sql_sampler_test.rb +18 -19
- data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +5 -8
- data/test/new_relic/agent/stats_engine/metric_stats_test.rb +20 -8
- data/test/new_relic/agent/stats_engine/samplers_test.rb +31 -14
- data/test/new_relic/agent/stats_engine_test.rb +53 -60
- data/test/new_relic/agent/thread_test.rb +7 -7
- data/test/new_relic/agent/transaction/pop_test.rb +96 -0
- data/test/new_relic/agent/transaction_info_test.rb +6 -17
- data/test/new_relic/agent/transaction_sample_builder_test.rb +10 -18
- data/test/new_relic/agent/transaction_sampler_test.rb +80 -75
- data/test/new_relic/agent/{instrumentation/metric_frame_test.rb → transaction_test.rb} +76 -42
- data/test/new_relic/agent/uri_util_test.rb +75 -0
- data/test/new_relic/agent_test.rb +115 -9
- data/test/test_helper.rb +138 -9
- data.tar.gz.sig +0 -0
- metadata +37 -74
- metadata.gz.sig +0 -0
- data/lib/new_relic/agent/instrumentation/metric_frame/pop.rb +0 -84
- data/lib/new_relic/agent/instrumentation/metric_frame.rb +0 -353
- data/test/new_relic/agent/instrumentation/metric_frame/pop_test.rb +0 -175
- data/test/test_contexts.rb +0 -34
@@ -0,0 +1,88 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
require 'new_relic/agent/instrumentation/evented_subscriber'
|
5
|
+
|
6
|
+
# Listen for ActiveSupport::Notifications events for ActionView render
|
7
|
+
# events. Write metric data and transaction trace segments for each event.
|
8
|
+
module NewRelic
|
9
|
+
module Agent
|
10
|
+
module Instrumentation
|
11
|
+
class ActionViewSubscriber < EventedSubscriber
|
12
|
+
def start(name, id, payload)
|
13
|
+
event = RenderEvent.new(name, Time.now, nil, id, payload)
|
14
|
+
push_event(event)
|
15
|
+
|
16
|
+
if NewRelic::Agent.is_execution_traced? && event.recordable?
|
17
|
+
event.scope = NewRelic::Agent.instance.stats_engine \
|
18
|
+
.push_scope(:action_view, event.time)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def finish(name, id, payload)
|
23
|
+
event = pop_event(id)
|
24
|
+
|
25
|
+
if NewRelic::Agent.is_execution_traced? && event.recordable?
|
26
|
+
record_metrics(event)
|
27
|
+
NewRelic::Agent.instance.stats_engine \
|
28
|
+
.pop_scope(event.scope, event.metric_name, event.end)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def record_metrics(event)
|
33
|
+
NewRelic::Agent.instance.stats_engine \
|
34
|
+
.record_metrics(event.metric_name,
|
35
|
+
event.duration,
|
36
|
+
:scoped => true)
|
37
|
+
end
|
38
|
+
|
39
|
+
class RenderEvent < Event
|
40
|
+
# Nearly every "render_blah.action_view" event has a child
|
41
|
+
# in the form of "!render_blah.action_view". The children
|
42
|
+
# are the ones we want to record. There are a couple
|
43
|
+
# special cases of events without children.
|
44
|
+
def recordable?
|
45
|
+
name[0] == '!' ||
|
46
|
+
metric_name == 'View/text template/Rendering' ||
|
47
|
+
metric_name == 'View/(unknown)/Partial'
|
48
|
+
end
|
49
|
+
|
50
|
+
def metric_name
|
51
|
+
if parent && (payload[:virtual_path] ||
|
52
|
+
(parent.payload[:identifier] =~ /template$/))
|
53
|
+
return parent.metric_name
|
54
|
+
elsif payload[:virtual_path]
|
55
|
+
identifier = payload[:virtual_path]
|
56
|
+
else
|
57
|
+
identifier = payload[:identifier]
|
58
|
+
end
|
59
|
+
|
60
|
+
# memoize
|
61
|
+
@metric_name ||= "View/#{metric_path(identifier)}/#{metric_action(name)}"
|
62
|
+
@metric_name
|
63
|
+
end
|
64
|
+
|
65
|
+
def metric_path(identifier)
|
66
|
+
if identifier == nil
|
67
|
+
'file'
|
68
|
+
elsif identifier =~ /template$/
|
69
|
+
identifier
|
70
|
+
elsif (parts = identifier.split('/')).size > 1
|
71
|
+
parts[-2..-1].join('/')
|
72
|
+
else
|
73
|
+
'(unknown)'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def metric_action(name)
|
78
|
+
case name
|
79
|
+
when /render_template.action_view$/ then 'Rendering'
|
80
|
+
when 'render_partial.action_view' then 'Partial'
|
81
|
+
when 'render_collection.action_view' then 'Partial'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -72,7 +72,7 @@ module NewRelic
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def metric_for_sql(sql)
|
75
|
-
metric = NewRelic::Agent::
|
75
|
+
metric = NewRelic::Agent::Transaction.database_metric_name
|
76
76
|
if metric.nil?
|
77
77
|
if sql =~ /^(select|update|insert|delete|show)/i
|
78
78
|
# Could not determine the model/operation so let's find a better
|
@@ -32,7 +32,7 @@ module NewRelic
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def metric_for_sql(sql)
|
35
|
-
metric = NewRelic::Agent::
|
35
|
+
metric = NewRelic::Agent::Transaction.database_metric_name
|
36
36
|
if metric.nil?
|
37
37
|
if sql =~ /^(select|update|insert|delete|show)/i
|
38
38
|
# Could not determine the model/operation so let's find a better
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
require 'new_relic/agent/instrumentation/active_record_helper'
|
5
|
+
|
6
|
+
# Listen for ActiveSupport::Notifications events for ActiveRecord query
|
7
|
+
# events. Write metric data, transaction trace segments and slow sql
|
8
|
+
# nodes for each event.
|
9
|
+
module NewRelic
|
10
|
+
module Agent
|
11
|
+
module Instrumentation
|
12
|
+
class ActiveRecordSubscriber
|
13
|
+
include NewRelic::Agent::Instrumentation
|
14
|
+
|
15
|
+
def self.subscribed?
|
16
|
+
# TODO: need to talk to Rails core about an API for this,
|
17
|
+
# rather than digging through Listener ivars
|
18
|
+
ActiveSupport::Notifications.notifier.listeners_for('sql.active_record') \
|
19
|
+
.find{|l| l.instance_variable_get(:@delegate).class == self }
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(*args)
|
23
|
+
return unless NewRelic::Agent.is_execution_traced?
|
24
|
+
|
25
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
26
|
+
record_metrics(event)
|
27
|
+
notice_sql(event)
|
28
|
+
end
|
29
|
+
|
30
|
+
def notice_sql(event)
|
31
|
+
config = active_record_config_for_event(event)
|
32
|
+
metric = base_metric(event)
|
33
|
+
|
34
|
+
# enter transaction trace segment
|
35
|
+
scope = NewRelic::Agent.instance.stats_engine.push_scope(:active_record, event.time)
|
36
|
+
|
37
|
+
NewRelic::Agent.instance.transaction_sampler \
|
38
|
+
.notice_sql(event.payload[:sql], config,
|
39
|
+
Helper.milliseconds_to_seconds(event.duration))
|
40
|
+
|
41
|
+
NewRelic::Agent.instance.sql_sampler \
|
42
|
+
.notice_sql(event.payload[:sql], metric, config,
|
43
|
+
Helper.milliseconds_to_seconds(event.duration))
|
44
|
+
|
45
|
+
# exit transaction trace segment
|
46
|
+
NewRelic::Agent.instance.stats_engine.pop_scope(scope, metric, event.end)
|
47
|
+
end
|
48
|
+
|
49
|
+
def record_metrics(event)
|
50
|
+
base = base_metric(event)
|
51
|
+
NewRelic::Agent.instance.stats_engine.record_metrics(base,
|
52
|
+
Helper.milliseconds_to_seconds(event.duration),
|
53
|
+
:scoped => true)
|
54
|
+
|
55
|
+
other_metrics = ActiveRecordHelper.rollup_metrics_for(base)
|
56
|
+
if config = active_record_config_for_event(event)
|
57
|
+
other_metrics << ActiveRecordHelper.remote_service_metric(config[:adapter], config[:host])
|
58
|
+
end
|
59
|
+
|
60
|
+
other_metrics.compact.each do |metric_name|
|
61
|
+
NewRelic::Agent.instance.stats_engine.record_metrics(metric_name,
|
62
|
+
Helper.milliseconds_to_seconds(event.duration),
|
63
|
+
:scoped => false)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def base_metric(event)
|
68
|
+
ActiveRecordHelper.metric_for_name(event.payload[:name]) ||
|
69
|
+
ActiveRecordHelper.metric_for_sql(NewRelic::Helper.correctly_encoded(event.payload[:sql]))
|
70
|
+
end
|
71
|
+
|
72
|
+
def active_record_config_for_event(event)
|
73
|
+
return unless event.payload[:connection_id]
|
74
|
+
|
75
|
+
# TODO: This will not work for JRuby and in any case we want
|
76
|
+
# this to be part of the event meta data so it doesn't have
|
77
|
+
# to be dug out of an ivar.
|
78
|
+
connection = ObjectSpace._id2ref(event.payload[:connection_id])
|
79
|
+
connection.instance_variable_get(:@config) if connection
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -8,11 +8,12 @@ module NewRelic
|
|
8
8
|
class BrowserMonitoringTimings
|
9
9
|
|
10
10
|
def initialize(queue_time_in_seconds, transaction)
|
11
|
+
@transaction = transaction
|
11
12
|
@now = Time.now.to_i
|
12
|
-
if transaction.nil?
|
13
|
+
if @transaction.nil?
|
13
14
|
@start_time_in_seconds = 0.0
|
14
15
|
else
|
15
|
-
@transaction_name = transaction.
|
16
|
+
@transaction_name = transaction.transaction.name
|
16
17
|
@start_time_in_seconds = transaction.start_time.to_i
|
17
18
|
end
|
18
19
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# This file is distributed under New Relic's license terms.
|
3
3
|
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
4
|
|
5
|
-
require 'new_relic/agent/
|
5
|
+
require 'new_relic/agent/transaction'
|
6
6
|
require 'new_relic/agent/instrumentation/queue_time'
|
7
7
|
module NewRelic
|
8
8
|
module Agent
|
@@ -39,7 +39,6 @@ module NewRelic
|
|
39
39
|
end
|
40
40
|
def newrelic_notice_error(*args); end
|
41
41
|
def new_relic_trace_controller_action(*args); yield; end
|
42
|
-
def newrelic_metric_path; end
|
43
42
|
def perform_action_with_newrelic_trace(*args); yield; end
|
44
43
|
end
|
45
44
|
|
@@ -75,6 +74,7 @@ module NewRelic
|
|
75
74
|
def newrelic_write_attr(attr_name, value) # :nodoc:
|
76
75
|
instance_variable_set "@#{attr_name}", value
|
77
76
|
end
|
77
|
+
|
78
78
|
def newrelic_read_attr(attr_name) # :nodoc:
|
79
79
|
instance_variable_get "@#{attr_name}"
|
80
80
|
end
|
@@ -167,12 +167,54 @@ module NewRelic
|
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
170
|
+
class TransactionNamer
|
171
|
+
def initialize(traced_obj)
|
172
|
+
@traced_obj = traced_obj
|
173
|
+
if (@traced_obj.is_a?(Class) || @traced_obj.is_a?(Module))
|
174
|
+
@traced_class_name = @traced_obj.name
|
175
|
+
else
|
176
|
+
@traced_class_name = @traced_obj.class.name
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def name(options={})
|
181
|
+
name = "#{category_name(options[:category])}/#{path_name(options)}"
|
182
|
+
end
|
183
|
+
|
184
|
+
def category_name(type = nil)
|
185
|
+
type ||= Transaction.current && Transaction.current.type
|
186
|
+
case type
|
187
|
+
when :controller, nil then 'Controller'
|
188
|
+
when :task then 'OtherTransaction/Background'
|
189
|
+
when :rack then 'Controller/Rack'
|
190
|
+
when :uri then 'Controller'
|
191
|
+
when :sinatra then 'Controller/Sinatra'
|
192
|
+
# for internal use only
|
193
|
+
else type.to_s
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def path_name(options={})
|
198
|
+
# if we have the path, use the path
|
199
|
+
path = options[:path]
|
200
|
+
|
201
|
+
class_name = options[:class_name] || @traced_class_name
|
202
|
+
|
203
|
+
# if we have an explicit action name option, use that
|
204
|
+
if options[:name]
|
205
|
+
path ||= [ class_name, options[:name] ].compact.join('/')
|
206
|
+
end
|
207
|
+
|
208
|
+
# if newrelic_metric_path() is defined, use that
|
209
|
+
if @traced_obj.respond_to?(:newrelic_metric_path)
|
210
|
+
path ||= @traced_obj.newrelic_metric_path
|
211
|
+
end
|
212
|
+
|
213
|
+
# fall back on just the traced class name
|
214
|
+
path ||= class_name
|
215
|
+
|
216
|
+
return path
|
217
|
+
end
|
176
218
|
end
|
177
219
|
|
178
220
|
# Yield to the given block with NewRelic tracing. Used by
|
@@ -262,39 +304,58 @@ module NewRelic
|
|
262
304
|
control = NewRelic::Control.instance
|
263
305
|
return perform_action_with_newrelic_profile(args, &block) if control.profiling?
|
264
306
|
|
265
|
-
|
307
|
+
txn = _start_transaction(block_given? ? args : [])
|
266
308
|
begin
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
309
|
+
options = { :force => txn.force_flag, :transaction => true }
|
310
|
+
return yield if !(NewRelic::Agent.is_execution_traced? || options[:force])
|
311
|
+
options[:metric] = true if options[:metric].nil?
|
312
|
+
options[:deduct_call_time_from_parent] = true if options[:deduct_call_time_from_parent].nil?
|
313
|
+
start_time, expected_scope = NewRelic::Agent::MethodTracer::InstanceMethods::TraceExecutionScoped.trace_execution_scoped_header(options)
|
314
|
+
|
315
|
+
begin
|
316
|
+
NewRelic::Agent::BusyCalculator.dispatcher_start txn.start_time
|
317
|
+
result = if block_given?
|
318
|
+
yield
|
319
|
+
else
|
320
|
+
perform_action_without_newrelic_trace(*args)
|
321
|
+
end
|
322
|
+
if defined?(request) && request && defined?(response) && response
|
323
|
+
if !Agent.config[:disable_mobile_headers]
|
324
|
+
NewRelic::Agent::BrowserMonitoring.insert_mobile_response_header(request, response)
|
275
325
|
end
|
276
|
-
if defined?(request) && request && defined?(response) && response
|
277
|
-
if !Agent.config[:disable_mobile_headers]
|
278
|
-
NewRelic::Agent::BrowserMonitoring.insert_mobile_response_header(request, response)
|
279
|
-
end
|
280
|
-
end
|
281
|
-
result
|
282
|
-
rescue => e
|
283
|
-
frame_data.notice_error(e)
|
284
|
-
raise
|
285
326
|
end
|
327
|
+
result
|
328
|
+
rescue => e
|
329
|
+
txn.notice_error(e)
|
330
|
+
raise
|
286
331
|
end
|
332
|
+
|
287
333
|
ensure
|
334
|
+
txn.freeze_name
|
335
|
+
metrics = recorded_metrics(txn)
|
336
|
+
txn_name = metrics.first
|
337
|
+
|
338
|
+
metric_names = Array(metrics)
|
339
|
+
first_name = metric_names.shift
|
340
|
+
scope = NewRelic::Agent.instance.stats_engine.scope_name
|
341
|
+
NewRelic::Agent::MethodTracer::InstanceMethods::TraceExecutionScoped.trace_execution_scoped_footer(start_time, first_name, metric_names, expected_scope, scope, options)
|
288
342
|
NewRelic::Agent::BusyCalculator.dispatcher_finish
|
289
|
-
# Look for a
|
343
|
+
# Look for a transaction in the thread local and process it.
|
290
344
|
# Clear the thread local when finished to ensure it only gets called once.
|
291
|
-
|
292
|
-
|
345
|
+
txn = Transaction.stop(txn_name)
|
346
|
+
txn.record_apdex(txn_name) unless ignore_apdex?
|
293
347
|
|
294
348
|
NewRelic::Agent::TransactionInfo.get.ignore_end_user = true if ignore_enduser?
|
295
349
|
end
|
296
350
|
end
|
297
351
|
|
352
|
+
def recorded_metrics(txn)
|
353
|
+
metric_parser = NewRelic::MetricParser::MetricParser.for_metric_named(txn.name)
|
354
|
+
metrics = [txn.name]
|
355
|
+
metrics += metric_parser.summary_metrics unless txn.has_parent?
|
356
|
+
metrics
|
357
|
+
end
|
358
|
+
|
298
359
|
protected
|
299
360
|
|
300
361
|
def newrelic_request(args)
|
@@ -343,10 +404,9 @@ module NewRelic
|
|
343
404
|
# Profile the instrumented call. Dev mode only. Experimental
|
344
405
|
# - should definitely not be used on production applications
|
345
406
|
def perform_action_with_newrelic_profile(args)
|
346
|
-
|
407
|
+
txn = _start_transaction(block_given? ? args : [])
|
347
408
|
val = nil
|
348
|
-
NewRelic::Agent.trace_execution_scoped
|
349
|
-
MetricFrame.current(true).start_transaction
|
409
|
+
NewRelic::Agent.trace_execution_scoped txn.metric_name do
|
350
410
|
NewRelic::Agent.disable_all_tracing do
|
351
411
|
# turn on profiling
|
352
412
|
profile = RubyProf.profile do
|
@@ -361,78 +421,35 @@ module NewRelic
|
|
361
421
|
end
|
362
422
|
return val
|
363
423
|
ensure
|
364
|
-
|
365
|
-
end
|
366
|
-
|
367
|
-
def transaction_name(options={})
|
368
|
-
name = "#{category_name(options)}/#{path_name(options)}"
|
369
|
-
NewRelic::Agent.instance.transaction_rules.rename(name)
|
370
|
-
end
|
371
|
-
|
372
|
-
def category_name(options)
|
373
|
-
case options[:category]
|
374
|
-
when :controller, nil then 'Controller'
|
375
|
-
when :task then 'OtherTransaction/Background' # 'Task'
|
376
|
-
when :rack then 'Controller/Rack' #'WebTransaction/Rack'
|
377
|
-
when :uri then 'Controller' #'WebTransaction/Uri'
|
378
|
-
when :sinatra then 'Controller/Sinatra' #'WebTransaction/Uri'
|
379
|
-
# for internal use only
|
380
|
-
else options[:category].to_s
|
381
|
-
end
|
382
|
-
end
|
383
|
-
|
384
|
-
def path_name(options)
|
385
|
-
if options.any?
|
386
|
-
options[:path] || path_class_and_action(options)
|
387
|
-
else
|
388
|
-
newrelic_metric_path
|
389
|
-
end
|
424
|
+
txn.pop
|
390
425
|
end
|
391
426
|
|
392
|
-
|
393
|
-
metric_class = options[:class_name]
|
394
|
-
|
395
|
-
if !metric_class
|
396
|
-
if (self.is_a?(Class) || self.is_a?(Module))
|
397
|
-
metric_class = self.name
|
398
|
-
else
|
399
|
-
metric_class = self.class.name
|
400
|
-
end
|
401
|
-
end
|
402
|
-
|
403
|
-
[ metric_class, options[:name] ].compact.join('/')
|
404
|
-
end
|
405
|
-
|
406
|
-
# Write a metric frame onto a thread local if there isn't already one there.
|
427
|
+
# Write a transaction onto a thread local if there isn't already one there.
|
407
428
|
# If there is one, just update it.
|
408
|
-
def
|
409
|
-
frame_data = NewRelic::Agent::Instrumentation::MetricFrame.current(true)
|
410
|
-
|
411
|
-
frame_data.apdex_start ||= _detect_upstream_wait(frame_data.start)
|
412
|
-
_record_queue_length
|
413
|
-
|
429
|
+
def _start_transaction(args) # :nodoc:
|
414
430
|
# If a block was passed in, then the arguments represent options for the instrumentation,
|
415
431
|
# not app method arguments.
|
416
432
|
options = {}
|
417
433
|
if args.any?
|
418
434
|
if args.last.is_a?(Hash)
|
419
435
|
options = args.pop
|
420
|
-
frame_data.force_flag = options[:force]
|
421
|
-
frame_data.request = options[:request] if options[:request]
|
422
436
|
end
|
423
437
|
available_params = options[:params] || {}
|
424
438
|
options[:name] ||= args.first
|
425
439
|
else
|
426
440
|
available_params = self.respond_to?(:params) ? self.params : {}
|
427
441
|
end
|
428
|
-
frame_data.request ||= self.request if self.respond_to? :request
|
429
442
|
|
430
|
-
|
443
|
+
options[:request] ||= self.request if self.respond_to? :request
|
444
|
+
options[:filtered_params] = (respond_to? :filter_parameters) ? filter_parameters(available_params) : available_params
|
445
|
+
category = options[:category] || :controller
|
446
|
+
txn = Transaction.start(category, options)
|
447
|
+
txn.name = TransactionNamer.new(self).name(options)
|
448
|
+
|
449
|
+
txn.apdex_start ||= _detect_upstream_wait(txn.start_time)
|
450
|
+
_record_queue_length
|
431
451
|
|
432
|
-
|
433
|
-
NewRelic::Agent::TransactionInfo.get.transaction_name = txn_name
|
434
|
-
frame_data.filtered_params = (respond_to? :filter_parameters) ? filter_parameters(available_params) : available_params
|
435
|
-
frame_data
|
452
|
+
return txn
|
436
453
|
end
|
437
454
|
|
438
455
|
# Filter out a request if it matches one of our parameters for
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
module NewRelic
|
6
|
+
module Agent
|
7
|
+
module Instrumentation
|
8
|
+
class EventedSubscriber
|
9
|
+
def initialize
|
10
|
+
@queue_key = ['NewRelic', self.class.name, object_id].join('-')
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.subscribed?
|
14
|
+
# TODO: need to talk to Rails core about an API for this,
|
15
|
+
# rather than digging through Listener ivars
|
16
|
+
ActiveSupport::Notifications.notifier.instance_variable_get(:@subscribers) \
|
17
|
+
.find{|s| s.instance_variable_get(:@delegate).class == self }
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.subscribe(pattern)
|
21
|
+
if !subscribed?
|
22
|
+
ActiveSupport::Notifications.subscribe(pattern, new)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def start(name, id, payload)
|
27
|
+
event = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
|
28
|
+
push_event(event)
|
29
|
+
return event
|
30
|
+
end
|
31
|
+
|
32
|
+
def finish(name, id, payload)
|
33
|
+
pop_event(id)
|
34
|
+
end
|
35
|
+
|
36
|
+
def push_event(event)
|
37
|
+
parent = event_stack[event.transaction_id].last
|
38
|
+
event.parent = parent
|
39
|
+
parent << event if parent
|
40
|
+
event_stack[event.transaction_id].push event
|
41
|
+
end
|
42
|
+
|
43
|
+
def pop_event(transaction_id)
|
44
|
+
event = event_stack[transaction_id].pop
|
45
|
+
event.end = Time.now
|
46
|
+
return event
|
47
|
+
end
|
48
|
+
|
49
|
+
def event_stack
|
50
|
+
Thread.current[@queue_key] ||= Hash.new {|h,id| h[id] = [] }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Taken from ActiveSupport::Notifications::Event, pasted here
|
55
|
+
# with a couple minor additions so we don't have a hard
|
56
|
+
# dependency on ActiveSupport::Notifications.
|
57
|
+
#
|
58
|
+
# Represents an intrumentation event, provides timing and metric
|
59
|
+
# name information useful when recording metrics.
|
60
|
+
class Event
|
61
|
+
attr_reader :name, :time, :transaction_id, :payload, :children
|
62
|
+
attr_accessor :end, :parent, :scope
|
63
|
+
|
64
|
+
def initialize(name, start, ending, transaction_id, payload)
|
65
|
+
@name = name
|
66
|
+
@payload = payload.dup
|
67
|
+
@time = start
|
68
|
+
@transaction_id = transaction_id
|
69
|
+
@end = ending
|
70
|
+
@children = []
|
71
|
+
end
|
72
|
+
|
73
|
+
def metric_name
|
74
|
+
raise NotImplementedError
|
75
|
+
end
|
76
|
+
|
77
|
+
def duration
|
78
|
+
self.end - time
|
79
|
+
end
|
80
|
+
|
81
|
+
def <<(event)
|
82
|
+
@children << event
|
83
|
+
end
|
84
|
+
|
85
|
+
def parent_of?(event)
|
86
|
+
@children.include? event
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -20,7 +20,7 @@ module NewRelic
|
|
20
20
|
the_class.class_eval <<-EOD
|
21
21
|
def #{method_name}_with_newrelic_trace(*args, &block)
|
22
22
|
metrics = ["Memcache/#{method_name}",
|
23
|
-
(NewRelic::Agent::
|
23
|
+
(NewRelic::Agent::Transaction.recording_web_transaction? ? 'Memcache/allWeb' : 'Memcache/allOther')]
|
24
24
|
self.class.trace_execution_scoped(metrics) do
|
25
25
|
t0 = Time.now
|
26
26
|
begin
|
@@ -33,7 +33,7 @@ module NewRelic
|
|
33
33
|
alias #{method_name}_without_newrelic_trace #{method_name}
|
34
34
|
alias #{method_name} #{method_name}_with_newrelic_trace
|
35
35
|
EOD
|
36
|
-
end
|
36
|
+
end
|
37
37
|
end
|
38
38
|
def memcache_key_snippet(method_name)
|
39
39
|
return "" unless NewRelic::Agent.config[:capture_memcache_keys]
|
@@ -46,7 +46,7 @@ end
|
|
46
46
|
|
47
47
|
DependencyDetection.defer do
|
48
48
|
@name = :memcache
|
49
|
-
|
49
|
+
|
50
50
|
depends_on do
|
51
51
|
!NewRelic::Agent.config[:disable_memcache_instrumentation]
|
52
52
|
end
|
@@ -55,7 +55,7 @@ DependencyDetection.defer do
|
|
55
55
|
defined?(::MemCache) || defined?(::Memcached) ||
|
56
56
|
defined?(::Dalli::Client) || defined?(::Spymemcached)
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
executes do
|
60
60
|
commands = %w[get get_multi set add incr decr delete replace append prepend]
|
61
61
|
if defined? ::MemCache
|
@@ -12,18 +12,18 @@ DependencyDetection.defer do
|
|
12
12
|
depends_on do
|
13
13
|
Merb::Dispatcher::DefaultException.respond_to?(:before)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
executes do
|
17
17
|
::NewRelic::Agent.logger.info 'Installing Merb Errors instrumentation'
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
executes do
|
21
21
|
|
22
22
|
# Hook in the notification to merb
|
23
23
|
error_notifier = Proc.new {
|
24
24
|
if request.exceptions #check that there's actually an exception
|
25
|
-
# Note, this assumes we have already captured the other information such as uri and params in the
|
26
|
-
NewRelic::Agent::
|
25
|
+
# Note, this assumes we have already captured the other information such as uri and params in the Transaction.
|
26
|
+
NewRelic::Agent::Transaction.notice_error(request.exceptions.first)
|
27
27
|
end
|
28
28
|
}
|
29
29
|
Merb::Dispatcher::DefaultException.before error_notifier
|
@@ -77,7 +77,7 @@ module NewRelic
|
|
77
77
|
perform_action_with_newrelic_trace(:category => :rack, :request => @newrelic_request) do
|
78
78
|
result = call_without_newrelic(*args)
|
79
79
|
# Ignore cascaded calls
|
80
|
-
|
80
|
+
Transaction.abort_transaction! if result.first == 404
|
81
81
|
result
|
82
82
|
end
|
83
83
|
end
|