newrelic_rpm 3.5.7.59 → 3.5.8.64.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data.tar.gz.sig +3 -2
  2. data/CHANGELOG +34 -3
  3. data/LICENSE +23 -0
  4. data/lib/new_relic/agent.rb +50 -3
  5. data/lib/new_relic/agent/agent.rb +40 -60
  6. data/lib/new_relic/agent/configuration/defaults.rb +9 -3
  7. data/lib/new_relic/agent/configuration/server_source.rb +4 -0
  8. data/lib/new_relic/agent/cross_app_monitor.rb +230 -0
  9. data/lib/new_relic/agent/cross_app_tracing.rb +274 -0
  10. data/lib/new_relic/agent/database.rb +28 -10
  11. data/lib/new_relic/agent/error_collector.rb +5 -0
  12. data/lib/new_relic/agent/event_listener.rb +4 -0
  13. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +53 -34
  14. data/lib/new_relic/agent/instrumentation/metric_frame.rb +16 -3
  15. data/lib/new_relic/agent/instrumentation/net.rb +13 -11
  16. data/lib/new_relic/agent/instrumentation/resque.rb +10 -10
  17. data/lib/new_relic/agent/instrumentation/sinatra.rb +19 -9
  18. data/lib/new_relic/agent/new_relic_service.rb +63 -9
  19. data/lib/new_relic/agent/pipe_service.rb +8 -12
  20. data/lib/new_relic/agent/rules_engine.rb +72 -0
  21. data/lib/new_relic/agent/shim_agent.rb +0 -1
  22. data/lib/new_relic/agent/sql_sampler.rb +3 -2
  23. data/lib/new_relic/agent/stats.rb +149 -0
  24. data/lib/new_relic/agent/stats_engine.rb +9 -0
  25. data/lib/new_relic/agent/stats_engine/gc_profiler.rb +1 -24
  26. data/lib/new_relic/agent/stats_engine/metric_stats.rb +84 -185
  27. data/lib/new_relic/agent/stats_engine/stats_hash.rb +58 -0
  28. data/lib/new_relic/agent/stats_engine/transactions.rb +10 -2
  29. data/lib/new_relic/agent/transaction_info.rb +31 -6
  30. data/lib/new_relic/agent/transaction_sample_builder.rb +19 -8
  31. data/lib/new_relic/agent/transaction_sampler.rb +17 -10
  32. data/lib/new_relic/helper.rb +32 -0
  33. data/lib/new_relic/local_environment.rb +24 -32
  34. data/lib/new_relic/okjson.rb +599 -0
  35. data/lib/new_relic/transaction_sample.rb +2 -1
  36. data/lib/new_relic/transaction_sample/segment.rb +2 -1
  37. data/lib/new_relic/version.rb +1 -1
  38. data/newrelic.yml +27 -41
  39. data/test/multiverse/suites/agent_only/audit_log_test.rb +2 -4
  40. data/test/multiverse/suites/agent_only/config/newrelic.yml +1 -2
  41. data/test/multiverse/suites/agent_only/{cross_process_test.rb → cross_application_tracing_test.rb} +3 -3
  42. data/test/multiverse/suites/agent_only/key_transactions_test.rb +66 -0
  43. data/test/multiverse/suites/agent_only/marshaling_test.rb +9 -22
  44. data/test/multiverse/suites/agent_only/rename_rule_test.rb +57 -0
  45. data/test/multiverse/suites/agent_only/start_up_test.rb +1 -1
  46. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +17 -6
  47. data/test/multiverse/suites/rails/error_tracing_test.rb +20 -8
  48. data/test/multiverse/suites/resque/instrumentation_test.rb +2 -2
  49. data/test/multiverse/suites/sinatra/Envfile +2 -0
  50. data/test/multiverse/suites/sinatra/config/newrelic.yml +1 -0
  51. data/test/multiverse/suites/sinatra/sinatra_metric_explosion_test.rb +5 -5
  52. data/test/multiverse/suites/sinatra/sinatra_test.rb +75 -4
  53. data/test/new_relic/agent/agent/connect_test.rb +45 -1
  54. data/test/new_relic/agent/agent/start_worker_thread_test.rb +0 -3
  55. data/test/new_relic/agent/agent_test.rb +20 -40
  56. data/test/new_relic/agent/agent_test_controller_test.rb +24 -19
  57. data/test/new_relic/agent/busy_calculator_test.rb +1 -1
  58. data/test/new_relic/agent/configuration/server_source_test.rb +8 -3
  59. data/test/new_relic/agent/cross_app_monitor_test.rb +237 -0
  60. data/test/new_relic/agent/database_test.rb +60 -16
  61. data/test/new_relic/agent/error_collector_test.rb +28 -4
  62. data/test/new_relic/agent/event_listener_test.rb +23 -2
  63. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +53 -0
  64. data/test/new_relic/agent/instrumentation/metric_frame_test.rb +95 -0
  65. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +414 -59
  66. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -5
  67. data/test/new_relic/agent/method_tracer_test.rb +4 -2
  68. data/test/new_relic/agent/new_relic_service_test.rb +108 -6
  69. data/test/new_relic/agent/pipe_channel_manager_test.rb +1 -1
  70. data/test/new_relic/agent/pipe_service_test.rb +9 -9
  71. data/test/new_relic/agent/rpm_agent_test.rb +0 -11
  72. data/test/new_relic/agent/rules_engine_test.rb +82 -0
  73. data/test/new_relic/agent/shim_agent_test.rb +0 -4
  74. data/test/new_relic/agent/sql_sampler_test.rb +7 -0
  75. data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +85 -0
  76. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +110 -23
  77. data/test/new_relic/agent/stats_engine_test.rb +1 -46
  78. data/test/new_relic/agent/stats_hash_test.rb +93 -0
  79. data/test/new_relic/agent/stats_test.rb +197 -0
  80. data/test/new_relic/agent/transaction_info_test.rb +63 -11
  81. data/test/new_relic/agent/transaction_sample_builder_test.rb +10 -3
  82. data/test/new_relic/agent/transaction_sampler_test.rb +92 -80
  83. data/test/new_relic/agent_test.rb +35 -5
  84. data/test/new_relic/control_test.rb +1 -1
  85. data/test/new_relic/fake_collector.rb +87 -9
  86. data/test/new_relic/helper_test.rb +24 -0
  87. data/test/new_relic/metric_data_test.rb +11 -11
  88. data/test/new_relic/metric_spec_test.rb +1 -1
  89. data/test/script/ci.sh +1 -1
  90. data/test/test_contexts.rb +0 -1
  91. data/test/test_helper.rb +21 -3
  92. metadata +32 -16
  93. metadata.gz.sig +0 -0
  94. data/lib/new_relic/agent/cross_process_monitoring.rb +0 -187
  95. data/lib/new_relic/stats.rb +0 -337
  96. data/test/new_relic/agent/cross_process_monitoring_test.rb +0 -190
  97. data/test/new_relic/agent/stats_engine/metric_stats/harvest_test.rb +0 -133
  98. data/test/new_relic/fakes_sending_data.rb +0 -30
  99. data/test/new_relic/stats_test.rb +0 -421
@@ -0,0 +1,274 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ module NewRelic
5
+ module Agent
6
+ module CrossAppTracing
7
+ extend NewRelic::Agent::CrossAppMonitor::EncodingFunctions
8
+
9
+ # Exception raised if there is a problem with cross app transactions.
10
+ class Error < RuntimeError; end
11
+
12
+
13
+ # The cross app response header for "outgoing" calls
14
+ NR_APPDATA_HEADER = 'X-NewRelic-App-Data'
15
+
16
+ # The cross app id header for "outgoing" calls
17
+ NR_ID_HEADER = 'X-NewRelic-ID'
18
+
19
+ # The cross app transaction header for "outgoing" calls
20
+ NR_TXN_HEADER = 'X-NewRelic-Transaction'
21
+
22
+ # The index of the transaction GUID in the appdata header of responses
23
+ APPDATA_TXN_GUID_INDEX = 5
24
+
25
+
26
+ ###############
27
+ module_function
28
+ ###############
29
+
30
+ # Send the given +request+, adding metrics appropriate to the
31
+ # response when it comes back.
32
+ def trace_http_request( http, request )
33
+ return yield unless NewRelic::Agent.is_execution_traced?
34
+
35
+ t0, segment = start_trace( http, request )
36
+ response = yield
37
+ finish_trace( t0, segment, request, response, http ) if t0
38
+
39
+ return response
40
+ end
41
+
42
+
43
+ # Set up the necessary state for cross-application tracing before the
44
+ # given +request+ goes out on the specified +http+ connection.
45
+ def start_trace( http, request )
46
+ inject_request_headers( request ) if cross_app_enabled?
47
+
48
+ # Create a segment and time the call
49
+ t0 = Time.now
50
+ segment = stats_engine.push_scope( "External/#{http.address}/all", t0 )
51
+
52
+ return t0, segment
53
+ rescue => err
54
+ NewRelic::Agent.logger.error "Uncaught exception while tracing HTTP request", err
55
+ return nil
56
+ end
57
+
58
+
59
+ # Finish tracing the HTTP +request+ that started at +t0+ with the information in
60
+ # +response+ and the given +http+ connection.
61
+ def finish_trace( t0, segment, request, response, http )
62
+ t1 = Time.now
63
+
64
+ # Figure out which metrics we need to report based on the request and response
65
+ # The last (most-specific) one is scoped.
66
+ metrics = metrics_for( http, request, response )
67
+ scoped_metric = metrics.pop
68
+
69
+ # Report the metrics
70
+ duration = t1.to_f - t0.to_f
71
+ metrics.each { |metric| get_metric(metric).trace_call(duration) }
72
+ get_scoped_metric( scoped_metric ).trace_call( duration )
73
+
74
+ # Add TT custom parameters
75
+ stats_engine.rename_scope_segment( scoped_metric )
76
+ extract_custom_parameters( response ) if response_is_crossapp?( response )
77
+
78
+ # Change the name of the segment to the scoped metric and then pop it.
79
+ stats_engine.pop_scope( segment, duration, t1 )
80
+
81
+ rescue NewRelic::Agent::CrossAppTracing::Error => err
82
+ NewRelic::Agent.logger.debug "while cross app tracing", err
83
+ rescue => err
84
+ NewRelic::Agent.logger.error "Uncaught exception while finishing an HTTP request trace", err
85
+ end
86
+
87
+
88
+ # Return +true+ if cross app tracing is enabled in the config.
89
+ def cross_app_enabled?
90
+ NewRelic::Agent.config[:cross_process_id] &&
91
+ (NewRelic::Agent.config[:"cross_application_tracer.enabled"] ||
92
+ NewRelic::Agent.config[:cross_application_tracing])
93
+ end
94
+
95
+
96
+ # Memoized fetcher for the cross app encoding key. Raises a
97
+ # NewRelic::Agent::CrossAppTracing::Error if the key isn't configured.
98
+ def cross_app_encoding_key
99
+ NewRelic::Agent.config[:encoding_key] or
100
+ raise NewRelic::Agent::CrossAppTracing::Error, "No encoding_key set."
101
+ end
102
+
103
+
104
+ # Inject the X-Process header into the outgoing +request+.
105
+ def inject_request_headers( request )
106
+ key = cross_app_encoding_key()
107
+ cross_app_id = NewRelic::Agent.config[:cross_process_id] or
108
+ raise NewRelic::Agent::CrossAppTracing::Error, "no cross app ID configured"
109
+ txn_guid = NewRelic::Agent::TransactionInfo.get.guid
110
+ txn_data = NewRelic.json_dump([ txn_guid, false ])
111
+
112
+ request[ NR_ID_HEADER ] = obfuscate_with_key( key, cross_app_id )
113
+ request[ NR_TXN_HEADER ] = obfuscate_with_key( key, txn_data )
114
+
115
+ rescue NewRelic::Agent::CrossAppTracing::Error => err
116
+ NewRelic::Agent.logger.debug "Not injecting x-process header", err
117
+ end
118
+
119
+
120
+ # Extract any custom parameters from +response+ if it's cross-application and
121
+ # add them to the current TT node.
122
+ def extract_custom_parameters( response )
123
+
124
+ appdata = extract_appdata( response )
125
+ sampler = NewRelic::Agent.instance.transaction_sampler
126
+ sampler.add_segment_parameters( :transaction_guid => appdata[APPDATA_TXN_GUID_INDEX] )
127
+
128
+ end
129
+
130
+
131
+ # Return the set of metrics (NewRelic::MethodTraceStats objects) that correspond to
132
+ # the given +request+ and +response+.
133
+ def metrics_for( http, request, response )
134
+ metrics = common_metrics( http )
135
+
136
+ if response_is_crossapp?( response )
137
+ begin
138
+ metrics.concat metrics_for_crossapp_response( http, response )
139
+ rescue => err
140
+ # Fall back to regular metrics if there's a problem with x-process metrics
141
+ NewRelic::Agent.logger.debug "%p while fetching x-process metrics: %s" %
142
+ [ err.class, err.message ]
143
+ metrics.concat metrics_for_regular_response( http, request, response )
144
+ end
145
+ else
146
+ NewRelic::Agent.logger.debug "Response doesn't have CAT headers."
147
+ metrics.concat metrics_for_regular_response( http, request, response )
148
+ end
149
+
150
+ return metrics
151
+ end
152
+
153
+
154
+ # Return an Array of metrics used for every response.
155
+ def common_metrics( http )
156
+ metrics = [ "External/all" ]
157
+ metrics << "External/#{http.address}/all"
158
+
159
+ if NewRelic::Agent::Instrumentation::MetricFrame.recording_web_transaction?
160
+ metrics << "External/allWeb"
161
+ else
162
+ metrics << "External/allOther"
163
+ end
164
+
165
+ return metrics
166
+ end
167
+
168
+
169
+ # Returns +true+ if Cross Application Tracing is enabled, and the given +response+
170
+ # has the appropriate headers.
171
+ def response_is_crossapp?( response )
172
+ return false unless cross_app_enabled?
173
+ unless response[NR_APPDATA_HEADER]
174
+ NewRelic::Agent.logger.debug "Response doesn't have the %p header: %p" %
175
+ [ NR_APPDATA_HEADER, response.to_hash ]
176
+ return false
177
+ end
178
+
179
+ return true
180
+ end
181
+
182
+
183
+ # Return the set of metric objects appropriate for the given cross app
184
+ # +response+.
185
+ def metrics_for_crossapp_response( http, response )
186
+ xp_id, txn_name, q_time, r_time, req_len, _ = extract_appdata( response )
187
+
188
+ check_crossapp_id( xp_id )
189
+ check_transaction_name( txn_name )
190
+
191
+ NewRelic::Agent.logger.debug "CAT xp_id: %p, txn_name: %p." % [ xp_id, txn_name ]
192
+
193
+ metrics = []
194
+ metrics << "ExternalApp/#{http.address}/#{xp_id}/all"
195
+ metrics << "ExternalTransaction/#{http.address}/#{xp_id}/#{txn_name}"
196
+
197
+ return metrics
198
+ end
199
+
200
+
201
+ # Extract x-process application data from the specified +response+ and return
202
+ # it as an array of the form:
203
+ #
204
+ # [
205
+ # <cross app ID>,
206
+ # <transaction name>,
207
+ # <queue time in seconds>,
208
+ # <response time in seconds>,
209
+ # <request content length in bytes>,
210
+ # <transaction GUID>
211
+ # ]
212
+ def extract_appdata( response )
213
+ appdata = response[NR_APPDATA_HEADER] or
214
+ raise NewRelic::Agent::CrossAppTracing::Error,
215
+ "Can't derive metrics for response: no #{NR_APPDATA_HEADER} header!"
216
+
217
+ key = cross_app_encoding_key()
218
+ decoded_appdata = decode_with_key( key, appdata )
219
+ decoded_appdata.set_encoding( ::Encoding::UTF_8 ) if
220
+ decoded_appdata.respond_to?( :set_encoding )
221
+
222
+ return NewRelic.json_load( decoded_appdata )
223
+ end
224
+
225
+
226
+ # Return the set of metric objects appropriate for the given (non-cross app)
227
+ # +response+.
228
+ def metrics_for_regular_response( http, request, response )
229
+ metrics = []
230
+ metrics << "External/#{http.address}/Net::HTTP/#{request.method}"
231
+
232
+ return metrics
233
+ end
234
+
235
+
236
+ # Convenience function for fetching the metric associated with +metric_name+.
237
+ def get_metric( metric_name )
238
+ stats_engine.get_stats_no_scope( metric_name )
239
+ end
240
+
241
+
242
+ # Convenience function for fetching the scoped metric associated with +metric_name+.
243
+ def get_scoped_metric( metric_name )
244
+ # Default is to use the metric_name itself as the scope, which is what we want
245
+ stats_engine.get_stats( metric_name )
246
+ end
247
+
248
+
249
+ # Fetch a reference to the stats engine.
250
+ def stats_engine
251
+ NewRelic::Agent.instance.stats_engine
252
+ end
253
+
254
+
255
+ # Check the given +id+ to ensure it conforms to the format of a cross-application
256
+ # ID. Raises an NewRelic::Agent::CrossAppTracing::Error if it doesn't.
257
+ def check_crossapp_id( id )
258
+ id =~ /\A\d+#\d+\z/ or
259
+ raise NewRelic::Agent::CrossAppTracing::Error,
260
+ "malformed cross application ID %p" % [ id ]
261
+ end
262
+
263
+
264
+ # Check the given +name+ to ensure it conforms to the format of a valid transaction
265
+ # name.
266
+ def check_transaction_name( name )
267
+ # No-op -- apparently absolutely anything is a valid transaction name?
268
+ # This is here for when that inevitably comes back to haunt us.
269
+ end
270
+
271
+ end
272
+ end
273
+ end
274
+
@@ -62,20 +62,30 @@ module NewRelic
62
62
  def explain_sql(sql, connection_config)
63
63
  return nil unless sql && connection_config
64
64
  statement = sql.split(";\n")[0] # only explain the first
65
- explain_sql = explain_statement(statement, connection_config)
66
- return explain_sql || []
65
+ explain_plan = explain_statement(statement, connection_config)
66
+ return explain_plan || []
67
67
  end
68
68
 
69
69
  def explain_statement(statement, config)
70
- if is_select?(statement)
71
- handle_exception_in_explain do
72
- connection = get_connection(config)
73
- plan = nil
74
- if connection
75
- plan = process_resultset(connection.execute("EXPLAIN #{statement}"))
76
- end
77
- return plan
70
+ return unless is_select?(statement)
71
+
72
+ if statement[-3,3] == '...'
73
+ NewRelic::Agent.logger.debug('Unable to collect explain plan for truncated query.')
74
+ return
75
+ end
76
+
77
+ if parameterized?(statement)
78
+ NewRelic::Agent.logger.debug('Unable to collect explain plan for parameterized query.')
79
+ return
80
+ end
81
+
82
+ handle_exception_in_explain do
83
+ connection = get_connection(config)
84
+ plan = nil
85
+ if connection
86
+ plan = process_resultset(connection.execute("EXPLAIN #{statement}"))
78
87
  end
88
+ return plan
79
89
  end
80
90
  end
81
91
 
@@ -126,6 +136,10 @@ module NewRelic
126
136
  (first_word.upcase == 'SELECT')
127
137
  end
128
138
 
139
+ def parameterized?(statement)
140
+ Obfuscator.instance.obfuscate_single_quote_literals(statement) =~ /\$\d+/
141
+ end
142
+
129
143
  class ConnectionManager
130
144
  include Singleton
131
145
 
@@ -200,6 +214,10 @@ module NewRelic
200
214
  end
201
215
 
202
216
  def default_sql_obfuscator(sql)
217
+ if sql[-3,3] == '...'
218
+ return "Query too large (over 16k characters) to safely obfuscate"
219
+ end
220
+
203
221
  stmt = sql.kind_of?(Statement) ? sql : Statement.new(sql)
204
222
  adapter = stmt.adapter
205
223
  obfuscated = remove_escaped_quotes(stmt)
@@ -100,6 +100,11 @@ module NewRelic
100
100
  @seen_error_ids << exception.object_id
101
101
 
102
102
  NewRelic::Agent.get_stats("Errors/all").increment_count
103
+
104
+ txn_info = NewRelic::Agent::TransactionInfo.get
105
+ if (txn_info.transaction_name_set?)
106
+ NewRelic::Agent.get_stats("Errors/#{txn_info.transaction_name}").increment_count
107
+ end
103
108
  end
104
109
 
105
110
  # whether we should return early from the notice_error process
@@ -24,6 +24,10 @@ module NewRelic::Agent
24
24
  NewRelic::Agent.logger.debug("Run-away event subscription on #{event}? Subscribed #{count}") if count > @runaway_threshold
25
25
  end
26
26
 
27
+ def clear
28
+ @events.clear
29
+ end
30
+
27
31
  def notify(event, *args)
28
32
  return unless @events.has_key?(event)
29
33
 
@@ -254,7 +254,7 @@ module NewRelic
254
254
  return perform_action_without_newrelic_trace(*args)
255
255
  end
256
256
  end
257
-
257
+
258
258
  control = NewRelic::Control.instance
259
259
  return perform_action_with_newrelic_profile(args, &block) if control.profiling?
260
260
 
@@ -286,7 +286,7 @@ module NewRelic
286
286
  # Clear the thread local when finished to ensure it only gets called once.
287
287
  frame_data.record_apdex unless ignore_apdex?
288
288
  frame_data.pop
289
-
289
+
290
290
  NewRelic::Agent::TransactionInfo.get.ignore_end_user = true if ignore_enduser?
291
291
  end
292
292
  end
@@ -314,14 +314,14 @@ module NewRelic
314
314
  def newrelic_request_headers
315
315
  self.respond_to?(:request) && self.request.respond_to?(:headers) && self.request.headers
316
316
  end
317
-
317
+
318
318
  # overrideable method to determine whether to trace an action
319
319
  # or not - you may override this in your controller and supply
320
320
  # your own logic for ignoring transactions.
321
321
  def do_not_trace?
322
322
  _is_filtered?('do_not_trace')
323
323
  end
324
-
324
+
325
325
  # overrideable method to determine whether to trace an action
326
326
  # for purposes of apdex measurement - you can use this to
327
327
  # ignore things like api calls or other fast non-user-facing
@@ -329,7 +329,7 @@ module NewRelic
329
329
  def ignore_apdex?
330
330
  _is_filtered?('ignore_apdex')
331
331
  end
332
-
332
+
333
333
  def ignore_enduser?
334
334
  _is_filtered?('ignore_enduser')
335
335
  end
@@ -360,6 +360,43 @@ module NewRelic
360
360
  frame_data.pop
361
361
  end
362
362
 
363
+ def transaction_name(options={})
364
+ name = "#{category_name(options)}/#{path_name(options)}"
365
+ NewRelic::Agent.instance.transaction_rules.rename(name)
366
+ end
367
+
368
+ def category_name(options)
369
+ case options[:category]
370
+ when :controller, nil then 'Controller'
371
+ when :task then 'OtherTransaction/Background' # 'Task'
372
+ when :rack then 'Controller/Rack' #'WebTransaction/Rack'
373
+ when :uri then 'Controller' #'WebTransaction/Uri'
374
+ when :sinatra then 'Controller/Sinatra' #'WebTransaction/Uri'
375
+ # for internal use only
376
+ else options[:category].to_s
377
+ end
378
+ end
379
+
380
+ def path_name(options)
381
+ if options.any?
382
+ options[:path] || path_class_and_action(options)
383
+ else
384
+ newrelic_metric_path
385
+ end
386
+ end
387
+
388
+ def path_class_and_action(options)
389
+ metric_class = options[:class_name]
390
+
391
+ if !metric_class && (self.is_a?(Class) || self.is_a?(Module))
392
+ metric_class = self.name
393
+ else
394
+ metric_class = self.class.name
395
+ end
396
+
397
+ [ metric_class, options[:name] ].compact.join('/')
398
+ end
399
+
363
400
  # Write a metric frame onto a thread local if there isn't already one there.
364
401
  # If there is one, just update it.
365
402
  def _push_metric_frame(args) # :nodoc:
@@ -367,49 +404,31 @@ module NewRelic
367
404
 
368
405
  frame_data.apdex_start ||= _detect_upstream_wait(frame_data.start)
369
406
  _record_queue_length
407
+
370
408
  # If a block was passed in, then the arguments represent options for the instrumentation,
371
409
  # not app method arguments.
410
+ options = {}
372
411
  if args.any?
373
412
  if args.last.is_a?(Hash)
374
- options = args.last
413
+ options = args.pop
375
414
  frame_data.force_flag = options[:force]
376
415
  frame_data.request = options[:request] if options[:request]
377
416
  end
378
- category, path, available_params = _convert_args_to_path(args)
417
+ available_params = options[:params] || {}
418
+ options[:name] ||= args.first
379
419
  else
380
- category = 'Controller'
381
- path = newrelic_metric_path
382
420
  available_params = self.respond_to?(:params) ? self.params : {}
383
421
  end
384
422
  frame_data.request ||= self.request if self.respond_to? :request
385
- transaction_name = category + '/' + path
386
- frame_data.push(transaction_name)
387
- NewRelic::Agent::TransactionInfo.get.transaction_name = transaction_name
423
+
424
+ txn_name = transaction_name(options || {})
425
+
426
+ frame_data.push(txn_name)
427
+ NewRelic::Agent::TransactionInfo.get.transaction_name = txn_name
388
428
  frame_data.filtered_params = (respond_to? :filter_parameters) ? filter_parameters(available_params) : available_params
389
429
  frame_data
390
430
  end
391
431
 
392
- def _convert_args_to_path(args)
393
- options = args.last.is_a?(Hash) ? args.pop : {}
394
- params = options[:params] || {}
395
- category = case options[:category]
396
- when :controller, nil then 'Controller'
397
- when :task then 'OtherTransaction/Background' # 'Task'
398
- when :rack then 'Controller/Rack' #'WebTransaction/Rack'
399
- when :uri then 'Controller' #'WebTransaction/Uri'
400
- when :sinatra then 'Controller/Sinatra' #'WebTransaction/Uri'
401
- # for internal use only
402
- else options[:category].to_s
403
- end
404
- unless path = options[:path]
405
- action = options[:name] || args.first
406
- metric_class = options[:class_name] || ((self.is_a?(Class)||self.is_a?(Module)) ? self.name : self.class.name)
407
- path = metric_class
408
- path += ('/' + action) if action
409
- end
410
- [category, path, params]
411
- end
412
-
413
432
  # Filter out a request if it matches one of our parameters for
414
433
  # ignoring it - the key is either 'do_not_trace' or 'ignore_apdex'
415
434
  def _is_filtered?(key)
@@ -452,7 +471,7 @@ module NewRelic
452
471
  now
453
472
  end
454
473
 
455
- # returns the NewRelic::MethodTraceStats object associated
474
+ # returns the NewRelic::Agent::Stats object associated
456
475
  # with the dispatcher time measurement
457
476
  def _dispatch_stat
458
477
  NewRelic::Agent.agent.stats_engine.get_stats_no_scope 'HttpDispatcher'