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.
Files changed (118) hide show
  1. data/CHANGELOG +27 -0
  2. data/Gemfile +2 -7
  3. data/README.md +1 -1
  4. data/lib/new_relic/agent/agent.rb +3 -2
  5. data/lib/new_relic/agent/autostart.rb +56 -0
  6. data/lib/new_relic/agent/browser_monitoring.rb +19 -14
  7. data/lib/new_relic/agent/configuration/defaults.rb +12 -2
  8. data/lib/new_relic/agent/configuration/environment_source.rb +4 -1
  9. data/lib/new_relic/agent/cross_app_monitor.rb +2 -1
  10. data/lib/new_relic/agent/cross_app_tracing.rb +19 -10
  11. data/lib/new_relic/agent/error_collector.rb +5 -4
  12. data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +204 -0
  13. data/lib/new_relic/agent/instrumentation/action_view_subscriber.rb +88 -0
  14. data/lib/new_relic/agent/instrumentation/active_record.rb +1 -1
  15. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +1 -1
  16. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +84 -0
  17. data/lib/new_relic/agent/instrumentation/browser_monitoring_timings.rb +3 -2
  18. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +104 -87
  19. data/lib/new_relic/agent/instrumentation/evented_subscriber.rb +91 -0
  20. data/lib/new_relic/agent/instrumentation/memcache.rb +4 -4
  21. data/lib/new_relic/agent/instrumentation/merb/errors.rb +4 -4
  22. data/lib/new_relic/agent/instrumentation/rack.rb +1 -1
  23. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +20 -20
  24. data/lib/new_relic/agent/instrumentation/rails/errors.rb +5 -5
  25. data/lib/new_relic/agent/instrumentation/rails3/errors.rb +3 -3
  26. data/lib/new_relic/agent/instrumentation/rails4/action_controller.rb +3 -25
  27. data/lib/new_relic/agent/instrumentation/rails4/action_view.rb +2 -115
  28. data/lib/new_relic/agent/instrumentation/rails4/active_record.rb +2 -82
  29. data/lib/new_relic/agent/instrumentation/rails4/errors.rb +3 -4
  30. data/lib/new_relic/agent/method_tracer.rb +93 -56
  31. data/lib/new_relic/agent/null_logger.rb +6 -0
  32. data/lib/new_relic/agent/samplers/cpu_sampler.rb +9 -4
  33. data/lib/new_relic/agent/sql_sampler.rb +10 -6
  34. data/lib/new_relic/agent/stats_engine/metric_stats.rb +19 -3
  35. data/lib/new_relic/agent/stats_engine/transactions.rb +53 -34
  36. data/lib/new_relic/agent/system_info.rb +54 -0
  37. data/lib/new_relic/agent/thread.rb +2 -2
  38. data/lib/new_relic/agent/transaction/pop.rb +52 -0
  39. data/lib/new_relic/agent/transaction.rb +388 -0
  40. data/lib/new_relic/agent/transaction_info.rb +5 -13
  41. data/lib/new_relic/agent/transaction_sample_builder.rb +13 -20
  42. data/lib/new_relic/agent/transaction_sampler.rb +13 -15
  43. data/lib/new_relic/agent/uri_util.rb +35 -0
  44. data/lib/new_relic/agent.rb +54 -11
  45. data/lib/new_relic/build.rb +2 -2
  46. data/lib/new_relic/control/frameworks/rails.rb +0 -1
  47. data/lib/new_relic/control/frameworks/rails3.rb +2 -0
  48. data/lib/new_relic/control/frameworks/rails4.rb +0 -4
  49. data/lib/new_relic/control/instance_methods.rb +5 -19
  50. data/lib/new_relic/control/server_methods.rb +2 -0
  51. data/lib/new_relic/environment_report.rb +4 -34
  52. data/lib/new_relic/latest_changes.rb +1 -1
  53. data/lib/new_relic/local_environment.rb +0 -6
  54. data/lib/new_relic/metric_spec.rb +2 -2
  55. data/lib/new_relic/rack/error_collector.rb +6 -4
  56. data/lib/new_relic/transaction_sample.rb +7 -1
  57. data/lib/new_relic/version.rb +1 -1
  58. data/lib/newrelic_rpm.rb +2 -2
  59. data/newrelic.yml +20 -20
  60. data/test/config/test_control.rb +2 -2
  61. data/test/multiverse/suites/agent_only/audit_log_test.rb +1 -1
  62. data/test/multiverse/suites/agent_only/cross_application_tracing_test.rb +0 -2
  63. data/test/multiverse/suites/agent_only/logging_test.rb +1 -1
  64. data/test/multiverse/suites/agent_only/marshaling_test.rb +5 -3
  65. data/test/multiverse/suites/agent_only/rename_rule_test.rb +2 -0
  66. data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +96 -0
  67. data/test/multiverse/suites/agent_only/testing_app.rb +1 -0
  68. data/test/multiverse/suites/rails/error_tracing_test.rb +17 -29
  69. data/test/multiverse/suites/rails/queue_time_test.rb +8 -2
  70. data/test/multiverse/suites/rails/view_instrumentation_test.rb +6 -3
  71. data/test/multiverse/suites/resque/instrumentation_test.rb +1 -1
  72. data/test/multiverse/suites/sinatra/sinatra_error_tracing_test.rb +8 -0
  73. data/test/new_relic/agent/agent/connect_test.rb +2 -1
  74. data/test/new_relic/agent/agent/start_test.rb +0 -10
  75. data/test/new_relic/agent/agent_logger_test.rb +15 -0
  76. data/test/new_relic/agent/agent_test_controller.rb +6 -2
  77. data/test/new_relic/agent/agent_test_controller_test.rb +20 -69
  78. data/test/new_relic/agent/autostart_test.rb +67 -0
  79. data/test/new_relic/agent/browser_monitoring_test.rb +60 -38
  80. data/test/new_relic/agent/configuration/environment_source_test.rb +19 -17
  81. data/test/new_relic/agent/cross_app_monitor_test.rb +8 -0
  82. data/test/new_relic/agent/error_collector/notice_error_test.rb +0 -5
  83. data/test/new_relic/agent/error_collector_test.rb +8 -9
  84. data/test/new_relic/agent/instrumentation/action_controller_subscriber_test.rb +228 -0
  85. data/test/new_relic/agent/instrumentation/action_view_subscriber_test.rb +18 -34
  86. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +5 -5
  87. data/test/new_relic/agent/instrumentation/active_record_subscriber_test.rb +8 -9
  88. data/test/new_relic/agent/instrumentation/browser_monitoring_timings_test.rb +1 -1
  89. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +24 -38
  90. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +126 -178
  91. data/test/new_relic/agent/instrumentation/rack_test.rb +1 -1
  92. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +135 -151
  93. data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +153 -81
  94. data/test/new_relic/agent/method_tracer_test.rb +42 -33
  95. data/test/new_relic/agent/mock_scope_listener.rb +4 -4
  96. data/test/new_relic/agent/pipe_channel_manager_test.rb +4 -2
  97. data/test/new_relic/agent/rpm_agent_test.rb +86 -89
  98. data/test/new_relic/agent/sql_sampler_test.rb +18 -19
  99. data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +5 -8
  100. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +20 -8
  101. data/test/new_relic/agent/stats_engine/samplers_test.rb +31 -14
  102. data/test/new_relic/agent/stats_engine_test.rb +53 -60
  103. data/test/new_relic/agent/thread_test.rb +7 -7
  104. data/test/new_relic/agent/transaction/pop_test.rb +96 -0
  105. data/test/new_relic/agent/transaction_info_test.rb +6 -17
  106. data/test/new_relic/agent/transaction_sample_builder_test.rb +10 -18
  107. data/test/new_relic/agent/transaction_sampler_test.rb +80 -75
  108. data/test/new_relic/agent/{instrumentation/metric_frame_test.rb → transaction_test.rb} +76 -42
  109. data/test/new_relic/agent/uri_util_test.rb +75 -0
  110. data/test/new_relic/agent_test.rb +115 -9
  111. data/test/test_helper.rb +138 -9
  112. data.tar.gz.sig +0 -0
  113. metadata +37 -74
  114. metadata.gz.sig +0 -0
  115. data/lib/new_relic/agent/instrumentation/metric_frame/pop.rb +0 -84
  116. data/lib/new_relic/agent/instrumentation/metric_frame.rb +0 -353
  117. data/test/new_relic/agent/instrumentation/metric_frame/pop_test.rb +0 -175
  118. data/test/test_contexts.rb +0 -34
data/CHANGELOG CHANGED
@@ -1,6 +1,33 @@
1
1
 
2
2
  # New Relic Ruby Agent Release Notes #
3
3
 
4
+ ## v3.6.1 ##
5
+
6
+ * Full URIs for HTTP requests are recorded in transaction traces
7
+
8
+ When recording a transaction trace node for an outgoing HTTP call via
9
+ Net::HTTP, the agent will now save the full URI (instead of just the hostname)
10
+ for the request. Embedded credentials, the query string, and the fragment will
11
+ be stripped from the URI before it is saved.
12
+
13
+ * Simplify Agent Autostart Logic
14
+
15
+ Previously the agent would only start when it detected a supported
16
+ "Dispatcher", meaning a known web server or background task framework. This
17
+ was problematic for customers using webservers that the agent was not
18
+ configured to detect (e.g. Puma). Now the agent will attempt to report any
19
+ time it detects it is running in a monitored environment (e.g. production).
20
+ There are two exceptions to this. The agent will not autostart in a rails
21
+ console or irb session or when the process was invoked by a rake task (e.g.
22
+ rake assets:precompile). The NEWRELIC_ENABLE environment variable can be set
23
+ to true or false to force the agent to start or not start.
24
+
25
+ * Don't attempt to resolve collector hostname when proxy is in use
26
+
27
+ When a proxy is configured, the agent will not attempt to lookup and cache the
28
+ IP address of New Relic server to which it is sending data, since DNS may not
29
+ be available in some environments. Thanks to Bill Kirtley for the contribution
30
+
4
31
  ## v3.6.0 ##
5
32
 
6
33
  * Sidekiq supprt
data/Gemfile CHANGED
@@ -5,15 +5,10 @@ group :development do
5
5
  # There's problems with the test task in rake 10
6
6
  # https://github.com/jimweirich/rake/issues/144
7
7
  gem 'rake', '0.9.6'
8
- if RUBY_VERSION > '1.9.0'
9
- gem 'mocha', '~>0.13.0', :require => false
10
- else
11
- gem 'mocha', '~>0.12.0'
12
- end
13
- gem 'shoulda', '~>3.0.1'
8
+ gem 'mocha', '~>0.13.0', :require => false
14
9
  gem 'sdoc-helpers'
15
10
  gem 'rdoc', '>= 2.4.2'
16
- gem 'rails', '~>3.2.0'
11
+ gem 'rails', '~>3.2.13'
17
12
  gem 'sqlite3', :platform => 'mri'
18
13
  gem 'activerecord-jdbcsqlite3-adapter', :platform => 'jruby'
19
14
  gem 'jruby-openssl', :platform => 'jruby'
data/README.md CHANGED
@@ -23,7 +23,7 @@ automatically in Rails applications.
23
23
 
24
24
  ## Supported Environments
25
25
 
26
- * Ruby 1.8.6, 1.8.7, REE, 1.9.x
26
+ * Ruby 1.8.6, 1.8.7, REE, 1.9.x, 2.0.x
27
27
  * JRuby 1.6 and 1.7
28
28
  * Rails 2.0 or later for Production Mode
29
29
  * Rails 2.3 or later for Developer Mode
@@ -8,6 +8,7 @@ require 'net/http'
8
8
  require 'logger'
9
9
  require 'zlib'
10
10
  require 'stringio'
11
+ require 'new_relic/agent/autostart'
11
12
  require 'new_relic/agent/new_relic_service'
12
13
  require 'new_relic/agent/pipe_service'
13
14
  require 'new_relic/agent/configuration/manager'
@@ -141,7 +142,7 @@ module NewRelic
141
142
  metric_info = NewRelic::MetricParser::MetricParser.for_metric_named(metric)
142
143
 
143
144
  if metric_info.is_web_transaction?
144
- NewRelic::Agent::Instrumentation::MetricFrame.record_apdex(metric_info, duration_seconds, duration_seconds, is_error)
145
+ NewRelic::Agent::Transaction.record_apdex(metric_info, duration_seconds, duration_seconds, is_error)
145
146
  end
146
147
  metrics = metric_info.summary_metrics
147
148
 
@@ -337,7 +338,7 @@ module NewRelic
337
338
  # assist with proper dispatcher detection
338
339
  def log_dispatcher
339
340
  dispatcher_name = Agent.config[:dispatcher].to_s
340
- return if log_if(dispatcher_name.empty?, :warn, "No dispatcher detected.")
341
+ return if log_if(dispatcher_name.empty?, :info, "No known dispatcher detected.")
341
342
  ::NewRelic::Agent.logger.info "Dispatcher: #{dispatcher_name}"
342
343
  end
343
344
 
@@ -0,0 +1,56 @@
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
+ # A singleton responsible for determining if the agent should start
8
+ # monitoring.
9
+ #
10
+ # If the agent is in a monitored environment (e.g. production) it will
11
+ # attempt to avoid starting at "inapproriate" times, for example in an IRB
12
+ # session. On Heroku, logs typically go to STDOUT so agent logs can spam
13
+ # the console during interactive sessions.
14
+ #
15
+ # It should be possible to override Autostart logic with an explicit
16
+ # configuration, for example the NEWRELIC_ENABLE environment variable or
17
+ # agent_enabled key in newrelic.yml
18
+ module Autostart
19
+ extend self
20
+
21
+
22
+ # The constants and execuatables (i.e. $0) used can be configured with
23
+ # via the config keys 'autostart.blacklisted_constants' and
24
+ # 'autostart.blacklisted_executables'
25
+ def agent_should_start?
26
+ !::NewRelic::Agent.config['autostart.blacklisted_constants'] \
27
+ .split(/\s*,\s*/).any?{ |name| constant_is_defined?(name) } &&
28
+ !::NewRelic::Agent.config['autostart.blacklisted_executables'] \
29
+ .split(/\s*,\s*/).any?{ |bin| File.basename($0) == bin } &&
30
+ !in_blacklisted_rake_task?
31
+ end
32
+
33
+ # Lookup whether namespaced constants (e.g. ::Foo::Bar::Baz) are in the
34
+ # environment.
35
+ def constant_is_defined?(const_name)
36
+ const_name.to_s.sub(/\A::/,'').split('::').inject(Object) do |namespace, name|
37
+ begin
38
+ namespace.const_get(name)
39
+ rescue NameError
40
+ false
41
+ end
42
+ end
43
+ end
44
+
45
+ def in_blacklisted_rake_task?
46
+ tasks = begin
47
+ Rake.application.top_level_tasks
48
+ rescue => e
49
+ ::NewRelic::Agent.logger.debug("Not in Rake environment so skipping blacklisted_rake_tasks check: #{e}")
50
+ []
51
+ end
52
+ !(tasks & ::NewRelic::Agent.config['autostart.blacklisted_rake_tasks'].split(/\s*,\s*/)).empty?
53
+ end
54
+ end
55
+ end
56
+ end
@@ -9,7 +9,7 @@ module NewRelic
9
9
  # This module contains support for Real User Monitoring - the
10
10
  # javascript generation and configuration
11
11
  module BrowserMonitoring
12
- class DummyMetricFrame
12
+ class DummyTransaction
13
13
  def initialize
14
14
  @attributes = {}
15
15
  end
@@ -21,9 +21,13 @@ module NewRelic
21
21
  def queue_time
22
22
  0.0
23
23
  end
24
+
25
+ def name
26
+ '(unknown)'
27
+ end
24
28
  end
25
29
 
26
- @@dummy_metric_frame = DummyMetricFrame.new
30
+ @@dummy_txn = DummyTransaction.new
27
31
 
28
32
  # This method returns a string suitable for inclusion in a page
29
33
  # - known as 'manual instrumentation' for Real User
@@ -52,6 +56,7 @@ module NewRelic
52
56
  # page as is reasonably possible.
53
57
  def browser_timing_footer
54
58
  if insert_js?
59
+ NewRelic::Agent::Transaction.freeze_name
55
60
  generate_footer_js(NewRelic::Agent.instance.beacon_configuration)
56
61
  else
57
62
  ""
@@ -73,19 +78,19 @@ module NewRelic
73
78
  end
74
79
 
75
80
  def browser_monitoring_transaction_name
76
- NewRelic::Agent::TransactionInfo.get.transaction_name
81
+ current_transaction.name || '(unknown)'
77
82
  end
78
83
 
79
84
  def browser_monitoring_queue_time
80
- clamp_to_positive((current_metric_frame.queue_time.to_f * 1000.0).round)
85
+ clamp_to_positive((current_transaction.queue_time.to_f * 1000.0).round)
81
86
  end
82
87
 
83
88
  def browser_monitoring_app_time
84
89
  clamp_to_positive(((Time.now - browser_monitoring_start_time).to_f * 1000.0).round)
85
90
  end
86
91
 
87
- def current_metric_frame
88
- Thread.current[:last_metric_frame] || @@dummy_metric_frame
92
+ def current_transaction
93
+ NewRelic::Agent::TransactionInfo.get.transaction || @@dummy_txn
89
94
  end
90
95
 
91
96
  def clamp_to_positive(value)
@@ -99,7 +104,7 @@ module NewRelic
99
104
 
100
105
  def self.timings
101
106
  NewRelic::Agent::Instrumentation::BrowserMonitoringTimings.new(
102
- current_metric_frame.queue_time,
107
+ current_transaction.queue_time,
103
108
  NewRelic::Agent::TransactionInfo.get)
104
109
  end
105
110
 
@@ -163,13 +168,13 @@ module NewRelic
163
168
  end
164
169
  end
165
170
 
166
- def metric_frame_attribute(key)
167
- current_metric_frame.user_attributes[key] || ""
171
+ def transaction_attribute(key)
172
+ current_transaction.user_attributes[key] || ""
168
173
  end
169
174
 
170
175
  def tt_guid
171
- txn = NewRelic::Agent::TransactionInfo.get
172
- return txn.guid if txn.include_guid?
176
+ transaction = NewRelic::Agent::TransactionInfo.get
177
+ return transaction.guid if transaction.include_guid?
173
178
  ""
174
179
  end
175
180
 
@@ -180,9 +185,9 @@ module NewRelic
180
185
  def footer_js_string(config)
181
186
  obfuscated_transaction_name = obfuscate(config, browser_monitoring_transaction_name)
182
187
 
183
- user = obfuscate(config, metric_frame_attribute(:user))
184
- account = obfuscate(config, metric_frame_attribute(:account))
185
- product = obfuscate(config, metric_frame_attribute(:product))
188
+ user = obfuscate(config, transaction_attribute(:user))
189
+ account = obfuscate(config, transaction_attribute(:account))
190
+ product = obfuscate(config, transaction_attribute(:product))
186
191
 
187
192
  html_safe_if_needed(%'<script type="text/javascript">#{config.browser_timing_static_footer}NREUMQ.push(["#{config.finish_command}","#{Agent.config[:beacon]}","#{Agent.config[:browser_key]}","#{Agent.config[:application_id]}","#{obfuscated_transaction_name}",#{browser_monitoring_queue_time},#{browser_monitoring_app_time},new Date().getTime(),"#{tt_guid}","#{tt_token}","#{user}","#{account}","#{product}"]);</script>')
188
193
  end
@@ -48,11 +48,21 @@ module NewRelic
48
48
  end,
49
49
  :enabled => true,
50
50
  :monitor_mode => Proc.new { self[:enabled] },
51
+
52
+ # agent_enabled determines whether the agent should try to start and
53
+ # report data.
51
54
  :agent_enabled => Proc.new do
52
55
  self[:enabled] &&
53
56
  (self[:developer_mode] || self[:monitor_mode] || self[:monitor_daemons]) &&
54
- !!NewRelic::Agent.config[:dispatcher]
57
+ ::NewRelic::Agent::Autostart.agent_should_start?
55
58
  end,
59
+ # Don't autostart the agent if we're in IRB or Rails console.
60
+ # This config option accepts a comma seperated list of constants.
61
+ :'autostart.blacklisted_constants' => 'Rails::Console',
62
+ # Comma seperated list of executables that you don't want to trigger
63
+ # agents start. e.g. 'rake,my_ruby_script.rb'
64
+ :'autostart.blacklisted_executables' => 'irb',
65
+ :'autostart.blacklisted_rake_tasks' => 'about,assets:clean,assets:clobber,assets:environment,assets:precompile,db:create,db:drop,db:fixtures:load,db:migrate,db:migrate:status,db:rollback,db:schema:cache:clear,db:schema:cache:dump,db:schema:dump,db:schema:load,db:seed,db:setup,db:structure:dump,db:version,doc:app,log:clear,middleware,notes,notes:custom,rails:template,rails:update,routes,secret,spec,spec:controllers,spec:helpers,spec:models,spec:rcov,stats,test,test:all,test:all:db,test:recent,test:single,test:uncommitted,time:zones:all,tmp:clear,tmp:create',
56
66
  :developer_mode => Proc.new { self[:developer] },
57
67
  :developer => false,
58
68
  :apdex_t => 0.5,
@@ -117,7 +127,7 @@ module NewRelic
117
127
 
118
128
  :'error_collector.enabled' => true,
119
129
  :'error_collector.capture_source' => true,
120
- :'error_collector.ignore_errors' => 'ActionController::RoutingError',
130
+ :'error_collector.ignore_errors' => 'ActionController::RoutingError,Sinatra::NotFound',
121
131
 
122
132
  :'rum.enabled' => true,
123
133
  :'rum.jsonp' => true,
@@ -29,7 +29,10 @@ module NewRelic
29
29
  end
30
30
 
31
31
  boolean_map = {
32
- 'NEWRELIC_ENABLE' => :agent_enabled
32
+ 'NEWRELIC_ENABLE' => :agent_enabled,
33
+ 'NEWRELIC_ENABLED' => :agent_enabled,
34
+ 'NEW_RELIC_ENABLE' => :agent_enabled,
35
+ 'NEW_RELIC_ENABLED' => :agent_enabled
33
36
  }.each do |key, val|
34
37
  if ENV[key].to_s =~ /false|off|no/i
35
38
  self[val] = false
@@ -87,7 +87,7 @@ module NewRelic
87
87
  end
88
88
  end
89
89
 
90
- events.subscribe(:start_transaction) do |name|
90
+ events.subscribe(:start_transaction) do
91
91
  set_transaction_custom_parameters
92
92
  end
93
93
 
@@ -138,6 +138,7 @@ module NewRelic
138
138
 
139
139
  def insert_response_header(request_headers, response_headers)
140
140
  unless client_cross_app_id.nil?
141
+ NewRelic::Agent::Transaction.freeze_name
141
142
  timings = NewRelic::Agent::BrowserMonitoring.timings
142
143
  content_length = content_length_from_request(request_headers)
143
144
 
@@ -52,7 +52,7 @@ module NewRelic
52
52
 
53
53
  # Create a segment and time the call
54
54
  t0 = Time.now
55
- segment = stats_engine.push_scope( "External/#{http.address}/all", t0 )
55
+ segment = stats_engine.push_scope( :net_http, t0 )
56
56
 
57
57
  return t0, segment
58
58
  rescue => err
@@ -78,13 +78,13 @@ module NewRelic
78
78
  stats_engine.record_metrics(scoped_metric, duration, :scoped => true)
79
79
 
80
80
  # Add TT custom parameters
81
- stats_engine.rename_scope_segment( scoped_metric )
82
- extract_custom_parameters( response ) if response_is_crossapp?( response )
81
+ segment.name = scoped_metric
82
+ add_transaction_trace_parameters(http, request, response)
83
83
  end
84
84
  ensure
85
85
  # We always need to pop the scope stack to avoid an inconsistent
86
86
  # state, which will prevent tracing of the whole transaction.
87
- stats_engine.pop_scope( segment, duration, t1 )
87
+ stats_engine.pop_scope( segment, scoped_metric, t1 )
88
88
  end
89
89
  rescue NewRelic::Agent::CrossAppTracing::Error => err
90
90
  NewRelic::Agent.logger.debug "while cross app tracing", err
@@ -124,15 +124,21 @@ module NewRelic
124
124
  NewRelic::Agent.logger.debug "Not injecting x-process header", err
125
125
  end
126
126
 
127
+ def add_transaction_trace_parameters(http, request, response)
128
+ filtered_uri = NewRelic::Agent::URIUtil.filtered_uri_for(http, request)
129
+ transaction_sampler.add_segment_parameters(:uri => filtered_uri)
130
+ if response_is_crossapp?( response )
131
+ add_cat_transaction_trace_parameters( response )
132
+ end
133
+ end
134
+
127
135
 
128
136
  # Extract any custom parameters from +response+ if it's cross-application and
129
137
  # add them to the current TT node.
130
- def extract_custom_parameters( response )
131
-
138
+ def add_cat_transaction_trace_parameters( response )
132
139
  appdata = extract_appdata( response )
133
- sampler = NewRelic::Agent.instance.transaction_sampler
134
- sampler.add_segment_parameters( :transaction_guid => appdata[APPDATA_TXN_GUID_INDEX] )
135
-
140
+ transaction_sampler.add_segment_parameters( \
141
+ :transaction_guid => appdata[APPDATA_TXN_GUID_INDEX] )
136
142
  end
137
143
 
138
144
 
@@ -164,7 +170,7 @@ module NewRelic
164
170
  metrics = [ "External/all" ]
165
171
  metrics << "External/#{http.address}/all"
166
172
 
167
- if NewRelic::Agent::Instrumentation::MetricFrame.recording_web_transaction?
173
+ if NewRelic::Agent::Transaction.recording_web_transaction?
168
174
  metrics << "External/allWeb"
169
175
  else
170
176
  metrics << "External/allOther"
@@ -246,6 +252,9 @@ module NewRelic
246
252
  NewRelic::Agent.instance.stats_engine
247
253
  end
248
254
 
255
+ def transaction_sampler
256
+ NewRelic::Agent.instance.transaction_sampler
257
+ end
249
258
 
250
259
  # Check the given +id+ to ensure it conforms to the format of a cross-application
251
260
  # ID. Raises an NewRelic::Agent::CrossAppTracing::Error if it doesn't.
@@ -110,13 +110,14 @@ module NewRelic
110
110
 
111
111
  # Increments a statistic that tracks total error rate
112
112
  # Be sure not to double-count same exception. This clears per harvest.
113
- def increment_error_count!(exception)
113
+ def increment_error_count!(exception, options={})
114
114
  return if seen?(exception)
115
115
  tag_as_seen(exception)
116
116
 
117
- txn_info = NewRelic::Agent::TransactionInfo.get
118
117
  metric_names = ["Errors/all"]
119
- metric_names << "Errors/#{txn_info.transaction_name}" if txn_info.transaction_name_set?
118
+ if options[:metric] && options[:metric] != '(unknown)'
119
+ metric_names << "Errors/#{options[:metric]}"
120
+ end
120
121
  stats_engine = NewRelic::Agent.agent.stats_engine
121
122
  stats_engine.record_metrics(metric_names) do |stats|
122
123
  stats.increment_count
@@ -129,7 +130,6 @@ module NewRelic
129
130
  def should_exit_notice_error?(exception)
130
131
  if enabled?
131
132
  if !error_is_ignored?(exception)
132
- increment_error_count!(exception)
133
133
  return exception.nil? # exit early if the exception is nil
134
134
  end
135
135
  end
@@ -251,6 +251,7 @@ module NewRelic
251
251
  # If exception is nil, the error count is bumped and no traced error is recorded
252
252
  def notice_error(exception, options={})
253
253
  return if should_exit_notice_error?(exception)
254
+ increment_error_count!(exception, options)
254
255
  NewRelic::Agent.instance.events.notify(:notice_error, exception, options)
255
256
  action_path = fetch_from_options(options, :metric, (NewRelic::Agent.instance.stats_engine.scope_name || ''))
256
257
  exception_options = error_params_from_options(options).merge(exception_info(exception))
@@ -0,0 +1,204 @@
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
+ begin
6
+ require 'rack'
7
+ rescue LoadError => e
8
+ Agent.logger.debug(e)
9
+ end
10
+
11
+ module NewRelic
12
+ module Agent
13
+ module Instrumentation
14
+ class ActionControllerSubscriber < EventedSubscriber
15
+ def initialize
16
+ super
17
+ NewRelic::Agent.instance.events.subscribe(:before_call) do |env|
18
+
19
+ request = begin
20
+ ::Rack::Request.new(env)
21
+ rescue => e
22
+ Agent.logger.debug("Error creating Rack::Request object: #{e}")
23
+ nil
24
+ end
25
+ TransactionInfo.reset(request)
26
+ end
27
+ end
28
+
29
+ def start(name, id, payload)
30
+ payload[:request] = TransactionInfo.get.request
31
+ event = ControllerEvent.new(name, Time.now, nil, id, payload)
32
+ push_event(event)
33
+
34
+ if NewRelic::Agent.is_execution_traced? && !event.ignored?
35
+ start_transaction(event)
36
+ else
37
+ # if this transaction is ignored, make sure child
38
+ # transaction are also ignored
39
+ NewRelic::Agent.instance.push_trace_execution_flag(false)
40
+ end
41
+ end
42
+
43
+ def finish(name, id, payload)
44
+ event = pop_event(id)
45
+ event.payload.merge!(payload)
46
+
47
+ set_enduser_ignore if event.enduser_ignored?
48
+
49
+ if NewRelic::Agent.is_execution_traced? && !event.ignored?
50
+ event.finalize_metric_name!
51
+ record_queue_time(event)
52
+ record_metrics(event)
53
+ record_apdex(event)
54
+ record_instance_busy(event)
55
+ stop_transaction(event)
56
+ else
57
+ Agent.instance.pop_trace_execution_flag
58
+ end
59
+ end
60
+
61
+ def set_enduser_ignore
62
+ TransactionInfo.get.ignore_end_user = true
63
+ end
64
+
65
+ def record_metrics(event)
66
+ controller_metric = MetricSpec.new(event.metric_name)
67
+ txn = Transaction.current
68
+ metrics = [ 'HttpDispatcher']
69
+ if txn.has_parent?
70
+ controller_metric.scope = StatsEngine::MetricStats::SCOPE_PLACEHOLDER
71
+ record_metric_on_parent_transaction(controller_metric, event.duration)
72
+ end
73
+ metrics << controller_metric.dup
74
+
75
+ Agent.instance.stats_engine.record_metrics(metrics, event.duration)
76
+ end
77
+
78
+ def record_metric_on_parent_transaction(metric, time)
79
+ Agent.instance.stats_engine.transaction_stats_stack[-2] \
80
+ .record(metric, time)
81
+ end
82
+
83
+ def record_apdex(event)
84
+ return if event.apdex_ignored?
85
+ metric_parser = MetricParser::MetricParser \
86
+ .for_metric_named(event.metric_name)
87
+ duration_plus_queue_time = event.end - (event.queue_start || event.time)
88
+ Transaction.record_apdex(metric_parser,
89
+ event.duration,
90
+ duration_plus_queue_time,
91
+ event.exception_encountered?)
92
+ end
93
+
94
+ def record_instance_busy(event)
95
+ BusyCalculator.dispatcher_start(event.time)
96
+ BusyCalculator.dispatcher_finish(event.end)
97
+ end
98
+
99
+ def record_queue_time(event)
100
+ return unless event.queue_start
101
+ QueueTime.record_frontend_metrics(event.queue_start, event.time)
102
+ end
103
+
104
+ def start_transaction(event)
105
+ txn = Transaction.start(:controller,
106
+ :request => event.payload[:request],
107
+ :filtered_params => filter(event.payload[:params]))
108
+ txn.apdex_start = (event.queue_start || event.time)
109
+ event.scope = Agent.instance.stats_engine \
110
+ .push_scope(:action_controller, event.time)
111
+ end
112
+
113
+ def stop_transaction(event)
114
+ TransactionInfo.get.transaction.name = event.metric_name
115
+ Agent.instance.stats_engine \
116
+ .pop_scope(event.scope, event.metric_name, event.end)
117
+ Transaction.stop
118
+ end
119
+
120
+ def filter(params)
121
+ filters = Rails.application.config.filter_parameters
122
+ ActionDispatch::Http::ParameterFilter.new(filters).filter(params)
123
+ end
124
+ end
125
+
126
+ class ControllerEvent < Event
127
+ attr_accessor :parent, :scope
128
+ attr_reader :queue_start
129
+
130
+ def initialize(name, start, ending, transaction_id, payload)
131
+ super
132
+
133
+ @controller_class = payload[:controller].split('::') \
134
+ .inject(Object){|m,o| m.const_get(o)}
135
+
136
+ if payload[:request] && payload[:request].respond_to?(:env)
137
+ @queue_start = QueueTime.parse_frontend_timestamp(payload[:request].env, self.time)
138
+ end
139
+ end
140
+
141
+ def metric_name
142
+ @metric_name || "Controller/#{metric_path}/#{metric_action}"
143
+ end
144
+
145
+ def finalize_metric_name!
146
+ txn = NewRelic::Agent::Transaction.current
147
+
148
+ # the event provides the default name but the transaction has the final say
149
+ txn.name ||= metric_name
150
+
151
+ # this applies the transaction name rules if not already applied
152
+ txn.freeze_name
153
+ @metric_name = txn.name
154
+ return @metric_name
155
+ end
156
+
157
+ def metric_path
158
+ @controller_class.controller_path
159
+ end
160
+
161
+ def metric_action
162
+ payload[:action]
163
+ end
164
+
165
+ def ignored?
166
+ _is_filtered?('do_not_trace')
167
+ end
168
+
169
+ def apdex_ignored?
170
+ _is_filtered?('ignore_apdex')
171
+ end
172
+
173
+ def enduser_ignored?
174
+ _is_filtered?('ignore_enduser')
175
+ end
176
+
177
+ def exception_encountered?
178
+ payload[:exception]
179
+ end
180
+
181
+ # FIXME: shamelessly ripped from ControllerInstrumentation
182
+ def _is_filtered?(key)
183
+ if @controller_class.respond_to? :newrelic_read_attr
184
+ ignore_actions = @controller_class.newrelic_read_attr(key)
185
+ end
186
+
187
+ case ignore_actions
188
+ when nil; false
189
+ when Hash
190
+ only_actions = Array(ignore_actions[:only])
191
+ except_actions = Array(ignore_actions[:except])
192
+ only_actions.include?(metric_action.to_sym) || (except_actions.any? && !except_actions.include?(metric_action.to_sym))
193
+ else
194
+ true
195
+ end
196
+ end
197
+
198
+ def to_s
199
+ "#<NewRelic::Agent::Instrumentation::ControllerEvent:#{object_id} name: \"#{name}\" id: #{transaction_id} payload: #{payload}}>"
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end