newrelic_rpm 3.9.9.275 → 3.10.0.279

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +51 -0
  3. data/config.dot +0 -3
  4. data/lib/new_relic/agent.rb +7 -5
  5. data/lib/new_relic/agent/agent.rb +4 -4
  6. data/lib/new_relic/agent/instrumentation/active_job.rb +5 -8
  7. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +17 -34
  8. data/lib/new_relic/agent/instrumentation/grape.rb +60 -23
  9. data/lib/new_relic/agent/instrumentation/merb/controller.rb +10 -2
  10. data/lib/new_relic/agent/instrumentation/middleware_tracing.rb +33 -21
  11. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +7 -3
  12. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +0 -9
  13. data/lib/new_relic/agent/instrumentation/sinatra.rb +9 -12
  14. data/lib/new_relic/agent/javascript_instrumentor.rb +1 -0
  15. data/lib/new_relic/agent/new_relic_service.rb +9 -6
  16. data/lib/new_relic/agent/parameter_filtering.rb +37 -0
  17. data/lib/new_relic/agent/supported_versions.rb +7 -0
  18. data/lib/new_relic/agent/traced_method_stack.rb +1 -1
  19. data/lib/new_relic/agent/transaction.rb +182 -186
  20. data/lib/new_relic/agent/vm/rubinius_vm.rb +93 -3
  21. data/lib/new_relic/control/frameworks/rails.rb +1 -0
  22. data/lib/new_relic/rack/agent_hooks.rb +15 -23
  23. data/lib/new_relic/version.rb +2 -2
  24. data/newrelic_rpm.gemspec +12 -5
  25. data/test/agent_helper.rb +26 -14
  26. data/test/multiverse/lib/multiverse/suite.rb +1 -5
  27. data/test/multiverse/suites/activemerchant/Envfile +4 -1
  28. data/test/multiverse/suites/agent_only/cross_application_tracing_test.rb +2 -12
  29. data/test/multiverse/suites/agent_only/logging_test.rb +1 -1
  30. data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +4 -0
  31. data/test/multiverse/suites/agent_only/synthetics_test.rb +1 -8
  32. data/test/multiverse/suites/agent_only/testing_app.rb +1 -7
  33. data/test/multiverse/suites/agent_only/xray_sessions_test.rb +11 -11
  34. data/test/multiverse/suites/deferred_instrumentation/sinatra_test.rb +4 -0
  35. data/test/multiverse/suites/grape/Envfile +1 -3
  36. data/test/multiverse/suites/grape/grape_test.rb +87 -6
  37. data/test/multiverse/suites/grape/grape_test_api.rb +5 -0
  38. data/test/multiverse/suites/grape/grape_versioning_test.rb +67 -0
  39. data/test/multiverse/suites/grape/grape_versioning_test_api.rb +72 -0
  40. data/test/multiverse/suites/rack/example_app.rb +31 -1
  41. data/test/multiverse/suites/rack/rack_auto_instrumentation_test.rb +11 -10
  42. data/test/multiverse/suites/rack/rack_cascade_test.rb +46 -0
  43. data/test/multiverse/suites/rack/rack_parameter_filtering_test.rb +40 -0
  44. data/test/multiverse/suites/rails/Envfile +8 -0
  45. data/test/multiverse/suites/rails/activejob_test.rb +16 -0
  46. data/test/multiverse/suites/rails/gc_instrumentation_test.rb +4 -2
  47. data/test/multiverse/suites/rails/parameter_capture_test.rb +49 -0
  48. data/test/multiverse/suites/rails/rails3_app/app_rails3_plus.rb +12 -1
  49. data/test/multiverse/suites/sinatra/sinatra_classic_test.rb +4 -0
  50. data/test/multiverse/suites/sinatra/sinatra_modular_test.rb +4 -0
  51. data/test/multiverse/suites/sinatra/sinatra_test_cases.rb +11 -0
  52. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +0 -58
  53. data/test/new_relic/agent/instrumentation/middleware_proxy_test.rb +49 -0
  54. data/test/new_relic/agent/instrumentation/middleware_tracing_test.rb +26 -14
  55. data/test/new_relic/agent/parameter_filtering_test.rb +39 -0
  56. data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +1 -1
  57. data/test/new_relic/agent/transaction_test.rb +106 -2
  58. data/test/new_relic/agent/vm/rubinius_vm_test.rb +38 -37
  59. data/test/new_relic/agent_test.rb +8 -3
  60. data/test/new_relic/filtering_test_app.rb +18 -0
  61. data/test/new_relic/latest_changes_test.rb +1 -1
  62. data/test/new_relic/rack/browser_monitoring_test.rb +4 -4
  63. data/test/performance/lib/performance/instrumentation/gc_stats.rb +6 -4
  64. data/test/performance/lib/performance/platform.rb +1 -0
  65. data/test/performance/suites/thread_profiling.rb +12 -0
  66. metadata +38 -15
  67. metadata.gz.sig +2 -1
@@ -95,10 +95,14 @@ DependencyDetection.defer do
95
95
  ActionController::Base.class_eval do
96
96
  include NewRelic::Agent::Instrumentation::ControllerInstrumentation
97
97
 
98
- # Compare with #alias_method_chain, which is not available in
99
- # Rails 1.1:
98
+ def perform_action_with_newrelic_trace_wrapper
99
+ options = {}
100
+ options[:params] = (respond_to?(:filter_parameters)) ? filter_parameters(params) : params
101
+ perform_action_with_newrelic_trace(options) { perform_action_without_newrelic_trace }
102
+ end
103
+
100
104
  alias_method :perform_action_without_newrelic_trace, :perform_action
101
- alias_method :perform_action, :perform_action_with_newrelic_trace
105
+ alias_method :perform_action, :perform_action_with_newrelic_trace_wrapper
102
106
  private :perform_action
103
107
 
104
108
  # determine the path that is used in the metric name for
@@ -19,15 +19,6 @@ module NewRelic
19
19
  end
20
20
 
21
21
  def process_action(*args) #THREAD_LOCAL_ACCESS
22
- # skip instrumentation if we are in an ignored action
23
- if _is_filtered?(ControllerInstrumentation::NR_DO_NOT_TRACE_KEY)
24
- txn = NewRelic::Agent::Transaction.tl_current
25
- txn.ignore! if txn
26
- NewRelic::Agent.disable_all_tracing do
27
- return super
28
- end
29
- end
30
-
31
22
  perform_action_with_newrelic_trace(:category => :controller, :name => self.action_name, :path => newrelic_metric_path, :params => request.filtered_parameters, :class_name => self.class.name) do
32
23
  super
33
24
  end
@@ -85,8 +85,11 @@ module NewRelic
85
85
 
86
86
  module ClassMethods
87
87
  def newrelic_middlewares
88
- [ NewRelic::Rack::AgentHooks,
89
- NewRelic::Rack::BrowserMonitoring ]
88
+ middlewares = [NewRelic::Rack::BrowserMonitoring]
89
+ if NewRelic::Rack::AgentHooks.needed?
90
+ middlewares << NewRelic::Rack::AgentHooks
91
+ end
92
+ middlewares
90
93
  end
91
94
 
92
95
  def build_with_newrelic(*args, &block)
@@ -126,7 +129,7 @@ module NewRelic
126
129
  txn_name = TransactionNamer.transaction_name_for_route(env, request)
127
130
  unless txn_name.nil?
128
131
  ::NewRelic::Agent::Transaction.set_default_transaction_name(
129
- "#{self.class.name}/#{txn_name}", :category => :sinatra)
132
+ "#{self.class.name}/#{txn_name}", :sinatra)
130
133
  end
131
134
  rescue => e
132
135
  ::NewRelic::Agent.logger.debug("Failed during route_eval to set transaction name", e)
@@ -136,19 +139,13 @@ module NewRelic
136
139
  end
137
140
 
138
141
  def dispatch_with_newrelic #THREAD_LOCAL_ACCESS
139
- if ignore_request?
140
- env['newrelic.ignored'] = true
141
- txn = ::NewRelic::Agent::Transaction.tl_current
142
- txn.ignore! if txn
143
- return dispatch_without_newrelic
144
- end
145
-
146
142
  request_params = get_request_params
143
+ filtered_params = ParameterFiltering::apply_filters(request.env, request_params || {})
147
144
 
148
145
  name = TransactionNamer.initial_transaction_name(request)
149
146
  perform_action_with_newrelic_trace(:category => :sinatra,
150
147
  :name => name,
151
- :params => request_params) do
148
+ :params => filtered_params) do
152
149
  dispatch_and_notice_errors_with_newrelic
153
150
  end
154
151
  end
@@ -171,7 +168,7 @@ module NewRelic
171
168
  ::NewRelic::Agent.notice_error(env['sinatra.error']) if had_error
172
169
  end
173
170
 
174
- def ignore_request?
171
+ def do_not_trace?
175
172
  Ignorer.should_ignore?(self, :routes)
176
173
  end
177
174
 
@@ -90,6 +90,7 @@ module NewRelic
90
90
  return '' unless js_enabled_and_ready? # fast exit
91
91
 
92
92
  state = NewRelic::Agent::TransactionState.tl_get
93
+
93
94
  return '' unless insert_js?(state) # slower exit
94
95
 
95
96
  bt_config = browser_timing_config(state)
@@ -469,6 +469,8 @@ module NewRelic
469
469
  end
470
470
  end
471
471
 
472
+ log_response(response)
473
+
472
474
  case response
473
475
  when Net::HTTPSuccess
474
476
  true # do nothing
@@ -488,16 +490,17 @@ module NewRelic
488
490
  response
489
491
  end
490
492
 
493
+ def log_response(response)
494
+ ::NewRelic::Agent.logger.debug "Received response, status: #{response.code}, encoding: '#{response['content-encoding']}'"
495
+ end
496
+
491
497
  # Decompresses the response from the server, if it is gzip
492
498
  # encoded, otherwise returns it verbatim
493
499
  def decompress_response(response)
494
- if response['content-encoding'] != 'gzip'
495
- ::NewRelic::Agent.logger.debug "Uncompressed content returned"
496
- response.body
500
+ if response['content-encoding'] == 'gzip'
501
+ Zlib::GzipReader.new(StringIO.new(response.body)).read
497
502
  else
498
- ::NewRelic::Agent.logger.debug "Decompressing return value"
499
- i = Zlib::GzipReader.new(StringIO.new(response.body))
500
- i.read
503
+ response.body
501
504
  end
502
505
  end
503
506
 
@@ -0,0 +1,37 @@
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 ParameterFiltering
8
+ extend self
9
+
10
+ def apply_filters(env, params)
11
+ params = filter_using_rails(env, params)
12
+ params = filter_rack_file_data(env, params)
13
+ params
14
+ end
15
+
16
+ def filter_using_rails(env, params)
17
+ return params unless defined?(ActionDispatch::Http::ParameterFilter) && env.key?("action_dispatch.parameter_filter")
18
+ filters = env["action_dispatch.parameter_filter"]
19
+ ActionDispatch::Http::ParameterFilter.new(filters).filter(params)
20
+ end
21
+
22
+ def filter_rack_file_data(env, params)
23
+ content_type = env["CONTENT_TYPE"]
24
+ multipart = content_type && content_type.start_with?("multipart")
25
+
26
+ params.inject({}) do |memo, (k,v)|
27
+ if multipart && v.is_a?(Hash) && v[:tempfile]
28
+ memo[k] = "[FILE]"
29
+ else
30
+ memo[k] = v
31
+ end
32
+ memo
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -113,6 +113,13 @@ module NewRelic
113
113
  :url => "https://rubygems.org/gems/rack",
114
114
  :feed => "https://rubygems.org/gems/rack/versions.atom"
115
115
  },
116
+ :grape =>
117
+ {
118
+ :type => :web,
119
+ :supported => [">= 0.2.0"],
120
+ :url => "https://rubygems.org/gems/grape",
121
+ :feed => "https://rubygems.org/gems/grape/versions.atom"
122
+ },
116
123
 
117
124
  # Database
118
125
  :activerecord =>
@@ -6,7 +6,7 @@ module NewRelic
6
6
  module Agent
7
7
  class TracedMethodFrame
8
8
  attr_reader :tag
9
- attr_accessor :name, :start_time, :children_time, :category
9
+ attr_accessor :name, :start_time, :children_time
10
10
  def initialize(tag, start_time)
11
11
  @tag = tag
12
12
  @start_time = start_time
@@ -22,16 +22,21 @@ module NewRelic
22
22
  TASK_PREFIX = 'OtherTransaction/Background/'.freeze
23
23
  RACK_PREFIX = 'Controller/Rack/'.freeze
24
24
  SINATRA_PREFIX = 'Controller/Sinatra/'.freeze
25
+ GRAPE_PREFIX = 'Controller/Grape/'.freeze
25
26
  OTHER_TRANSACTION_PREFIX = 'OtherTransaction/'.freeze
26
27
 
27
28
  CONTROLLER_MIDDLEWARE_PREFIX = 'Controller/Middleware/Rack'.freeze
28
29
 
29
30
  NESTED_TRACE_STOP_OPTIONS = { :metric => true }.freeze
30
- WEB_TRANSACTION_CATEGORIES = [:controller, :uri, :rack, :sinatra, :middleware].freeze
31
+ WEB_TRANSACTION_CATEGORIES = [:controller, :uri, :rack, :sinatra, :grape, :middleware].freeze
32
+ TRANSACTION_NAMING_SOURCES = [:child, :api].freeze
31
33
 
32
34
  MIDDLEWARE_SUMMARY_METRICS = ['Middleware/all'.freeze].freeze
33
35
  EMPTY_SUMMARY_METRICS = [].freeze
34
36
 
37
+ TRACE_OPTIONS_SCOPED = { :metric => true, :scoped_metric => true }.freeze
38
+ TRACE_OPTIONS_UNSCOPED = { :metric => true, :scoped_metric => false }.freeze
39
+
35
40
  # A Time instance for the start time, never nil
36
41
  attr_accessor :start_time
37
42
 
@@ -43,7 +48,8 @@ module NewRelic
43
48
  attr_accessor :exceptions,
44
49
  :filtered_params,
45
50
  :jruby_cpu_start,
46
- :process_cpu_start
51
+ :process_cpu_start,
52
+ :http_response_code
47
53
 
48
54
  # Give the current transaction a request context. Use this to
49
55
  # get the URI and referer. The request is interpreted loosely
@@ -58,7 +64,8 @@ module NewRelic
58
64
  :metrics,
59
65
  :gc_start_snapshot,
60
66
  :category,
61
- :name_from_child
67
+ :frame_stack,
68
+ :cat_path_hashes
62
69
 
63
70
  # Populated with the trace sample once this transaction is completed.
64
71
  attr_reader :transaction_trace
@@ -71,63 +78,46 @@ module NewRelic
71
78
  TransactionState.tl_get.current_transaction
72
79
  end
73
80
 
74
- def self.set_default_transaction_name(name, options = {}) #THREAD_LOCAL_ACCESS
81
+ def self.set_default_transaction_name(name, category = nil, segment_name = nil) #THREAD_LOCAL_ACCESS
75
82
  txn = tl_current
76
- name = txn.make_transaction_name(name, options[:category])
77
-
78
- if txn.frame_stack.empty?
79
- txn.set_default_transaction_name(name, options)
80
- else
81
- txn.frame_stack.last.name = name
82
- txn.frame_stack.last.category = options[:category] if options[:category]
83
- end
83
+ name = txn.make_transaction_name(name, category)
84
+ txn.name_last_frame(segment_name || name)
85
+ txn.set_default_transaction_name(name, category)
84
86
  end
85
87
 
86
- def self.set_overriding_transaction_name(name, options = {}) #THREAD_LOCAL_ACCESS
88
+ def self.set_overriding_transaction_name(name, category = nil) #THREAD_LOCAL_ACCESS
87
89
  txn = tl_current
88
90
  return unless txn
89
91
 
90
- name = txn.make_transaction_name(name, options[:category])
92
+ name = txn.make_transaction_name(name, category)
91
93
 
92
- if txn.frame_stack.empty?
93
- txn.set_overriding_transaction_name(name, options)
94
- else
95
- txn.frame_stack.last.name = name
96
- txn.frame_stack.last.category = options[:category] if options[:category]
94
+ txn.name_last_frame(name)
95
+ txn.set_overriding_transaction_name(name, category)
96
+ end
97
97
 
98
- # Parent transaction also takes this name, but only if they
99
- # are both/neither web transactions.
100
- child_is_web_category = transaction_category_is_web?(txn.frame_stack.last.category)
101
- txn_is_web_category = transaction_category_is_web?(txn.category)
98
+ def self.wrap(state, name, category, options = {})
99
+ Transaction.start(state, category, options.merge(:transaction_name => name))
102
100
 
103
- if (child_is_web_category == txn_is_web_category)
104
- txn.name_from_api = name
105
- end
101
+ begin
102
+ # We shouldn't raise from Transaction.start, but only wrap the yield
103
+ # to be absolutely sure we don't report agent problems as app errors
104
+ yield
105
+ rescue => e
106
+ Transaction.notice_error(e)
107
+ raise e
108
+ ensure
109
+ Transaction.stop(state)
106
110
  end
107
111
  end
108
112
 
109
- def make_transaction_name(name, category=nil)
110
- namer = Instrumentation::ControllerInstrumentation::TransactionNamer
111
- "#{namer.prefix_for_category(self, category)}#{name}"
112
- end
113
-
114
113
  def self.start(state, category, options)
115
114
  category ||= :controller
116
115
  txn = state.current_transaction
117
116
 
118
117
  if txn
119
- if options[:filtered_params] && !options[:filtered_params].empty?
120
- txn.filtered_params = options[:filtered_params]
121
- end
122
-
123
- nested_frame = NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped_header(state, Time.now.to_f)
124
- nested_frame.name = options[:transaction_name]
125
- nested_frame.category = category
126
- txn.frame_stack << nested_frame
118
+ txn.create_nested_frame(state, category, options)
127
119
  else
128
- txn = Transaction.new(category, options)
129
- state.reset(txn)
130
- txn.start(state)
120
+ txn = start_new_transaction(state, category, options)
131
121
  end
132
122
 
133
123
  txn
@@ -136,6 +126,13 @@ module NewRelic
136
126
  nil
137
127
  end
138
128
 
129
+ def self.start_new_transaction(state, category, options)
130
+ txn = Transaction.new(category, options)
131
+ state.reset(txn)
132
+ txn.start(state)
133
+ txn
134
+ end
135
+
139
136
  FAILED_TO_STOP_MESSAGE = "Failed during Transaction.stop because there is no current transaction"
140
137
 
141
138
  def self.stop(state, end_time=Time.now)
@@ -146,22 +143,12 @@ module NewRelic
146
143
  return
147
144
  end
148
145
 
146
+ nested_frame = txn.frame_stack.pop
147
+
149
148
  if txn.frame_stack.empty?
150
- txn.stop(state, end_time)
149
+ txn.stop(state, end_time, nested_frame)
151
150
  state.reset
152
151
  else
153
- nested_frame = txn.frame_stack.pop
154
-
155
- # Parent transaction inherits the name of the first child
156
- # to complete, if they are both/neither web transactions.
157
- nested_is_web_category = transaction_category_is_web?(nested_frame.category)
158
- txn_is_web_category = transaction_category_is_web?(txn.category)
159
-
160
- if (nested_is_web_category == txn_is_web_category)
161
- # first child to finish wins
162
- txn.name_from_child ||= nested_frame.name
163
- end
164
-
165
152
  nested_name = nested_transaction_name(nested_frame.name)
166
153
 
167
154
  if nested_name.start_with?(MIDDLEWARE_PREFIX)
@@ -195,6 +182,88 @@ module NewRelic
195
182
  end
196
183
  end
197
184
 
185
+ # Indicate that you don't want to keep the currently saved transaction
186
+ # information
187
+ def self.abort_transaction! #THREAD_LOCAL_ACCESS
188
+ state = NewRelic::Agent::TransactionState.tl_get
189
+ txn = state.current_transaction
190
+ txn.abort_transaction!(state) if txn
191
+ end
192
+
193
+ # If we have an active transaction, notice the error and increment the error metric.
194
+ # Options:
195
+ # * <tt>:request</tt> => Request object to get the uri and referer
196
+ # * <tt>:uri</tt> => The request path, minus any request params or query string.
197
+ # * <tt>:referer</tt> => The URI of the referer
198
+ # * <tt>:metric</tt> => The metric name associated with the transaction
199
+ # * <tt>:request_params</tt> => Request parameters, already filtered if necessary
200
+ # * <tt>:custom_params</tt> => Custom parameters
201
+ # Anything left over is treated as custom params
202
+
203
+ def self.notice_error(e, options={}) #THREAD_LOCAL_ACCESS
204
+ options = extract_request_options(options)
205
+ state = NewRelic::Agent::TransactionState.tl_get
206
+ txn = state.current_transaction
207
+ if txn
208
+ txn.notice_error(e, options)
209
+ else
210
+ NewRelic::Agent.instance.error_collector.notice_error(e, options)
211
+ end
212
+ end
213
+
214
+ def self.extract_request_options(options)
215
+ req = options.delete(:request)
216
+ if req
217
+ options[:uri] = uri_from_request(req)
218
+ options[:referer] = referer_from_request(req)
219
+ end
220
+ options
221
+ end
222
+
223
+ # Returns truthy if the current in-progress transaction is considered a
224
+ # a web transaction (as opposed to, e.g., a background transaction).
225
+ #
226
+ # @api public
227
+ #
228
+ def self.recording_web_transaction? #THREAD_LOCAL_ACCESS
229
+ txn = tl_current
230
+ txn && txn.recording_web_transaction?
231
+ end
232
+
233
+ # Make a safe attempt to get the referer from a request object, generally successful when
234
+ # it's a Rack request.
235
+ def self.referer_from_request(req)
236
+ if req && req.respond_to?(:referer)
237
+ req.referer.to_s.split('?').first
238
+ end
239
+ end
240
+
241
+ # Make a safe attempt to get the URI, without the host and query string.
242
+ def self.uri_from_request(req)
243
+ approximate_uri = case
244
+ when req.respond_to?(:fullpath ) then req.fullpath
245
+ when req.respond_to?(:path ) then req.path
246
+ when req.respond_to?(:request_uri) then req.request_uri
247
+ when req.respond_to?(:uri ) then req.uri
248
+ when req.respond_to?(:url ) then req.url
249
+ end
250
+ return approximate_uri[%r{^(https?://.*?)?(/[^?]*)}, 2] || '/' if approximate_uri
251
+ end
252
+
253
+
254
+ def self.apdex_bucket(duration, failed, apdex_t)
255
+ case
256
+ when failed
257
+ :apdex_f
258
+ when duration <= apdex_t
259
+ :apdex_s
260
+ when duration <= 4 * apdex_t
261
+ :apdex_t
262
+ else
263
+ :apdex_f
264
+ end
265
+ end
266
+
198
267
  @@java_classes_loaded = false
199
268
 
200
269
  if defined? JRuby
@@ -207,16 +276,13 @@ module NewRelic
207
276
  end
208
277
  end
209
278
 
210
- attr_reader :frame_stack, :cat_path_hashes
211
- attr_accessor :http_response_code
212
-
213
279
  def initialize(category, options)
214
280
  @frame_stack = []
281
+ @has_children = false
215
282
 
216
- @default_name = Helper.correctly_encoded(options[:transaction_name])
217
- @name_from_child = nil
218
- @name_from_api = nil
219
- @frozen_name = nil
283
+ self.default_name = options[:transaction_name]
284
+ @overridden_name = nil
285
+ @frozen_name = nil
220
286
 
221
287
  @category = category
222
288
  @start_time = Time.now
@@ -240,57 +306,67 @@ module NewRelic
240
306
  @noticed_error_ids ||= []
241
307
  end
242
308
 
243
- def default_name=(name)
244
- @default_name = Helper.correctly_encoded(name)
309
+ def overridden_name=(name)
310
+ @overridden_name = Helper.correctly_encoded(name)
245
311
  end
246
312
 
247
- def name_from_child=(name)
248
- @name_from_child = Helper.correctly_encoded(name)
313
+ def default_name=(name)
314
+ @default_name = Helper.correctly_encoded(name)
249
315
  end
250
316
 
251
- def name_from_api=(name)
252
- if @frozen_name
253
- NewRelic::Agent.logger.warn("Attempted to rename transaction to '#{name}' after transaction name was already frozen as '#{@frozen_name}'.")
317
+ def create_nested_frame(state, category, options)
318
+ @has_children = true
319
+ if options[:filtered_params] && !options[:filtered_params].empty?
320
+ @filtered_params = options[:filtered_params]
254
321
  end
255
322
 
256
- @name_from_api = Helper.correctly_encoded(name)
323
+ frame_stack.push NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped_header(state, Time.now.to_f)
324
+ name_last_frame(options[:transaction_name])
325
+
326
+ set_default_transaction_name(options[:transaction_name], category)
257
327
  end
258
328
 
259
- def set_default_transaction_name(name, options)
260
- self.default_name = name
261
- @category = options[:category] if options[:category]
329
+ def make_transaction_name(name, category=nil)
330
+ namer = Instrumentation::ControllerInstrumentation::TransactionNamer
331
+ "#{namer.prefix_for_category(self, category)}#{name}"
262
332
  end
263
333
 
264
- def set_overriding_transaction_name(name, options)
265
- self.name_from_api = name
266
- set_default_transaction_name(name, options)
334
+ def set_default_transaction_name(name, category)
335
+ return log_frozen_name(name) if name_frozen?
336
+ if influences_transaction_name?(category)
337
+ self.default_name = name
338
+ @category = category if category
339
+ end
267
340
  end
268
341
 
269
- def best_category
270
- if frame_stack.empty?
271
- category
272
- else
273
- frame_stack.last.category
342
+ def set_overriding_transaction_name(name, category)
343
+ return log_frozen_name(name) if name_frozen?
344
+ if influences_transaction_name?(category)
345
+ self.overridden_name = name
346
+ @category = category if category
274
347
  end
275
348
  end
276
349
 
277
- def best_name
278
- return @frozen_name if @frozen_name
279
- return @name_from_api if @name_from_api
350
+ def name_last_frame(name)
351
+ frame_stack.last.name = name
352
+ end
280
353
 
281
- if @name_from_child
282
- return @name_from_child
283
- elsif !@frame_stack.empty?
284
- return @frame_stack.last.name
285
- end
354
+ def log_frozen_name(name)
355
+ NewRelic::Agent.logger.warn("Attempted to rename transaction to '#{name}' after transaction name was already frozen as '#{@frozen_name}'.")
356
+ nil
357
+ end
286
358
 
287
- return @default_name if @default_name
359
+ def influences_transaction_name?(category)
360
+ !category || frame_stack.size == 1 || similar_category?(category)
361
+ end
288
362
 
289
- NewRelic::Agent::UNKNOWN_METRIC
363
+ def best_name
364
+ @frozen_name || @overridden_name ||
365
+ @default_name || NewRelic::Agent::UNKNOWN_METRIC
290
366
  end
291
367
 
292
368
  def name_set?
293
- (@name_from_api || @name_from_child || @default_name) ? true : false
369
+ (@overridden_name || @default_name) ? true : false
294
370
  end
295
371
 
296
372
  def promoted_transaction_name(name)
@@ -324,8 +400,6 @@ module NewRelic
324
400
  @frozen_name ? true : false
325
401
  end
326
402
 
327
- # Indicate that we are entering a measured controller action or task.
328
- # Make sure you unwind every push with a pop call.
329
403
  def start(state)
330
404
  return if !state.is_execution_traced?
331
405
 
@@ -334,16 +408,8 @@ module NewRelic
334
408
  NewRelic::Agent.instance.events.notify(:start_transaction)
335
409
  NewRelic::Agent::BusyCalculator.dispatcher_start(start_time)
336
410
 
337
- @trace_options = { :metric => true, :scoped_metric => false }
338
- @expected_scope = NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped_header(state, start_time.to_f)
339
- end
340
-
341
- # Indicate that you don't want to keep the currently saved transaction
342
- # information
343
- def self.abort_transaction! #THREAD_LOCAL_ACCESS
344
- state = NewRelic::Agent::TransactionState.tl_get
345
- txn = state.current_transaction
346
- txn.abort_transaction!(state) if txn
411
+ frame_stack.push NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped_header(state, start_time.to_f)
412
+ name_last_frame @default_name
347
413
  end
348
414
 
349
415
  # For the current web transaction, return the path of the URI minus the host part and query string, or nil.
@@ -385,16 +451,17 @@ module NewRelic
385
451
  name.start_with?(MIDDLEWARE_PREFIX)
386
452
  end
387
453
 
388
- def stop(state, end_time)
454
+ def stop(state, end_time, outermost_frame)
389
455
  return if !state.is_execution_traced?
390
456
  freeze_name_and_execute_if_not_ignored
391
457
  ignore! if user_defined_rules_ignore?
392
458
 
393
- if @name_from_child
394
- name = Transaction.nested_transaction_name(@default_name)
395
- @trace_options[:scoped_metric] = true
459
+ if @has_children
460
+ name = Transaction.nested_transaction_name(outermost_frame.name)
461
+ trace_options = TRACE_OPTIONS_SCOPED
396
462
  else
397
463
  name = @frozen_name
464
+ trace_options = TRACE_OPTIONS_UNSCOPED
398
465
  end
399
466
 
400
467
  # These metrics are recorded here instead of in record_summary_metrics
@@ -411,8 +478,8 @@ module NewRelic
411
478
  start_time.to_f,
412
479
  name,
413
480
  summary_metrics_with_exclusive_time,
414
- @expected_scope,
415
- @trace_options,
481
+ outermost_frame,
482
+ trace_options,
416
483
  end_time.to_f)
417
484
 
418
485
  NewRelic::Agent::BusyCalculator.dispatcher_finish(end_time)
@@ -606,37 +673,6 @@ module NewRelic
606
673
  end
607
674
  end
608
675
 
609
- # If we have an active transaction, notice the error and increment the error metric.
610
- # Options:
611
- # * <tt>:request</tt> => Request object to get the uri and referer
612
- # * <tt>:uri</tt> => The request path, minus any request params or query string.
613
- # * <tt>:referer</tt> => The URI of the referer
614
- # * <tt>:metric</tt> => The metric name associated with the transaction
615
- # * <tt>:request_params</tt> => Request parameters, already filtered if necessary
616
- # * <tt>:custom_params</tt> => Custom parameters
617
- # Anything left over is treated as custom params
618
-
619
- def self.notice_error(e, options={}) #THREAD_LOCAL_ACCESS
620
- options = extract_request_options(options)
621
- state = NewRelic::Agent::TransactionState.tl_get
622
- txn = state.current_transaction
623
- if txn
624
- txn.notice_error(e, options)
625
- else
626
- NewRelic::Agent.instance.error_collector.notice_error(e, options)
627
- end
628
- end
629
-
630
- def self.extract_request_options(options)
631
- req = options.delete(:request)
632
- if req
633
- options[:uri] = uri_from_request(req)
634
- options[:referer] = referer_from_request(req)
635
- end
636
- options
637
- end
638
-
639
-
640
676
  # Do not call this. Invoke the class method instead.
641
677
  def notice_error(error, options={}) # :nodoc:
642
678
  options[:uri] ||= uri if uri
@@ -746,56 +782,16 @@ module NewRelic
746
782
  alias_method :user_attributes, :custom_parameters
747
783
  alias_method :set_user_attributes, :add_custom_parameters
748
784
 
749
- # Returns truthy if the current in-progress transaction is considered a
750
- # a web transaction (as opposed to, e.g., a background transaction).
751
- #
752
- # @api public
753
- #
754
- def self.recording_web_transaction? #THREAD_LOCAL_ACCESS
755
- txn = tl_current
756
- txn && txn.recording_web_transaction?
757
- end
758
-
759
- def self.transaction_category_is_web?(category)
760
- WEB_TRANSACTION_CATEGORIES.include?(category)
761
- end
762
-
763
785
  def recording_web_transaction?
764
- self.class.transaction_category_is_web?(@category)
765
- end
766
-
767
- # Make a safe attempt to get the referer from a request object, generally successful when
768
- # it's a Rack request.
769
- def self.referer_from_request(req)
770
- if req && req.respond_to?(:referer)
771
- req.referer.to_s.split('?').first
772
- end
786
+ web_category?(@category)
773
787
  end
774
788
 
775
- # Make a safe attempt to get the URI, without the host and query string.
776
- def self.uri_from_request(req)
777
- approximate_uri = case
778
- when req.respond_to?(:fullpath ) then req.fullpath
779
- when req.respond_to?(:path ) then req.path
780
- when req.respond_to?(:request_uri) then req.request_uri
781
- when req.respond_to?(:uri ) then req.uri
782
- when req.respond_to?(:url ) then req.url
783
- end
784
- return approximate_uri[%r{^(https?://.*?)?(/[^?]*)}, 2] || '/' if approximate_uri
789
+ def web_category?(category)
790
+ WEB_TRANSACTION_CATEGORIES.include?(category)
785
791
  end
786
792
 
787
-
788
- def self.apdex_bucket(duration, failed, apdex_t)
789
- case
790
- when failed
791
- :apdex_f
792
- when duration <= apdex_t
793
- :apdex_s
794
- when duration <= 4 * apdex_t
795
- :apdex_t
796
- else
797
- :apdex_f
798
- end
793
+ def similar_category?(category)
794
+ web_category?(@category) == web_category?(category)
799
795
  end
800
796
 
801
797
  def cpu_burn