wd_newrelic_rpm 3.5.6 → 3.5.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/CHANGELOG +43 -3
  2. data/Gemfile +6 -2
  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 +239 -0
  9. data/lib/new_relic/agent/cross_app_tracing.rb +281 -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 +58 -39
  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/queue_time.rb +50 -192
  17. data/lib/new_relic/agent/instrumentation/rails4/action_controller.rb +145 -0
  18. data/lib/new_relic/agent/instrumentation/rails4/errors.rb +45 -0
  19. data/lib/new_relic/agent/instrumentation/resque.rb +10 -10
  20. data/lib/new_relic/agent/instrumentation/sinatra.rb +19 -9
  21. data/lib/new_relic/agent/new_relic_service.rb +63 -9
  22. data/lib/new_relic/agent/pipe_service.rb +8 -12
  23. data/lib/new_relic/agent/rules_engine.rb +72 -0
  24. data/lib/new_relic/agent/shim_agent.rb +0 -1
  25. data/lib/new_relic/agent/sql_sampler.rb +3 -2
  26. data/lib/new_relic/agent/stats.rb +149 -0
  27. data/lib/new_relic/agent/stats_engine.rb +9 -0
  28. data/lib/new_relic/agent/stats_engine/gc_profiler.rb +1 -24
  29. data/lib/new_relic/agent/stats_engine/metric_stats.rb +84 -185
  30. data/lib/new_relic/agent/stats_engine/stats_hash.rb +58 -0
  31. data/lib/new_relic/agent/stats_engine/transactions.rb +10 -2
  32. data/lib/new_relic/agent/transaction_info.rb +31 -6
  33. data/lib/new_relic/agent/transaction_sample_builder.rb +19 -8
  34. data/lib/new_relic/agent/transaction_sampler.rb +17 -10
  35. data/lib/new_relic/helper.rb +32 -0
  36. data/lib/new_relic/local_environment.rb +24 -32
  37. data/lib/new_relic/okjson.rb +599 -0
  38. data/lib/new_relic/transaction_sample.rb +2 -1
  39. data/lib/new_relic/transaction_sample/segment.rb +2 -1
  40. data/lib/new_relic/version.rb +1 -1
  41. data/newrelic.yml +27 -41
  42. data/test/multiverse/suites/agent_only/Envfile +5 -1
  43. data/test/multiverse/suites/agent_only/audit_log_test.rb +2 -4
  44. data/test/multiverse/suites/agent_only/config/newrelic.yml +1 -2
  45. data/test/multiverse/suites/agent_only/{cross_process_test.rb → cross_application_tracing_test.rb} +3 -3
  46. data/test/multiverse/suites/agent_only/key_transactions_test.rb +66 -0
  47. data/test/multiverse/suites/agent_only/marshaling_test.rb +9 -22
  48. data/test/multiverse/suites/agent_only/rename_rule_test.rb +57 -0
  49. data/test/multiverse/suites/agent_only/start_up_test.rb +1 -1
  50. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +17 -6
  51. data/test/multiverse/suites/rails/error_tracing_test.rb +20 -8
  52. data/test/multiverse/suites/rails/queue_time_test.rb +2 -2
  53. data/test/multiverse/suites/resque/instrumentation_test.rb +4 -3
  54. data/test/multiverse/suites/sinatra/Envfile +2 -0
  55. data/test/multiverse/suites/sinatra/config/newrelic.yml +1 -0
  56. data/test/multiverse/suites/sinatra/sinatra_metric_explosion_test.rb +5 -5
  57. data/test/multiverse/suites/sinatra/sinatra_test.rb +77 -10
  58. data/test/new_relic/agent/agent/connect_test.rb +45 -1
  59. data/test/new_relic/agent/agent/start_worker_thread_test.rb +0 -3
  60. data/test/new_relic/agent/agent_test.rb +20 -40
  61. data/test/new_relic/agent/agent_test_controller_test.rb +27 -60
  62. data/test/new_relic/agent/busy_calculator_test.rb +1 -1
  63. data/test/new_relic/agent/configuration/server_source_test.rb +8 -3
  64. data/test/new_relic/agent/cross_app_monitor_test.rb +237 -0
  65. data/test/new_relic/agent/database_test.rb +60 -16
  66. data/test/new_relic/agent/error_collector_test.rb +28 -4
  67. data/test/new_relic/agent/event_listener_test.rb +23 -2
  68. data/test/new_relic/agent/instrumentation/browser_monitoring_timings_test.rb +1 -1
  69. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +85 -12
  70. data/test/new_relic/agent/instrumentation/metric_frame_test.rb +95 -0
  71. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +436 -59
  72. data/test/new_relic/agent/instrumentation/queue_time_test.rb +58 -357
  73. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -5
  74. data/test/new_relic/agent/method_tracer_test.rb +4 -2
  75. data/test/new_relic/agent/new_relic_service_test.rb +108 -6
  76. data/test/new_relic/agent/pipe_channel_manager_test.rb +1 -1
  77. data/test/new_relic/agent/pipe_service_test.rb +9 -9
  78. data/test/new_relic/agent/rpm_agent_test.rb +0 -11
  79. data/test/new_relic/agent/rules_engine_test.rb +82 -0
  80. data/test/new_relic/agent/shim_agent_test.rb +0 -4
  81. data/test/new_relic/agent/sql_sampler_test.rb +7 -0
  82. data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +85 -0
  83. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +110 -23
  84. data/test/new_relic/agent/stats_engine_test.rb +1 -46
  85. data/test/new_relic/agent/stats_hash_test.rb +93 -0
  86. data/test/new_relic/agent/stats_test.rb +197 -0
  87. data/test/new_relic/agent/transaction_info_test.rb +63 -11
  88. data/test/new_relic/agent/transaction_sample_builder_test.rb +10 -3
  89. data/test/new_relic/agent/transaction_sampler_test.rb +92 -80
  90. data/test/new_relic/agent/worker_loop_test.rb +1 -1
  91. data/test/new_relic/agent_test.rb +35 -5
  92. data/test/new_relic/control_test.rb +1 -1
  93. data/test/new_relic/fake_collector.rb +87 -9
  94. data/test/new_relic/helper_test.rb +24 -0
  95. data/test/new_relic/metric_data_test.rb +11 -11
  96. data/test/new_relic/metric_spec_test.rb +1 -1
  97. data/test/script/ci.sh +1 -1
  98. data/test/test_contexts.rb +0 -1
  99. data/test/test_helper.rb +21 -3
  100. metadata +34 -41
  101. data/lib/new_relic/agent/cross_process_monitoring.rb +0 -187
  102. data/lib/new_relic/stats.rb +0 -337
  103. data/test/new_relic/agent/cross_process_monitoring_test.rb +0 -190
  104. data/test/new_relic/agent/stats_engine/metric_stats/harvest_test.rb +0 -133
  105. data/test/new_relic/fakes_sending_data.rb +0 -30
  106. data/test/new_relic/stats_test.rb +0 -421
@@ -0,0 +1,281 @@
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
+ begin
37
+ response = yield
38
+ ensure
39
+ finish_trace( t0, segment, request, response, http ) if t0
40
+ end
41
+
42
+ return response
43
+ end
44
+
45
+
46
+ # Set up the necessary state for cross-application tracing before the
47
+ # given +request+ goes out on the specified +http+ connection.
48
+ def start_trace( http, request )
49
+ inject_request_headers( request ) if cross_app_enabled?
50
+
51
+ # Create a segment and time the call
52
+ t0 = Time.now
53
+ segment = stats_engine.push_scope( "External/#{http.address}/all", t0 )
54
+
55
+ return t0, segment
56
+ rescue => err
57
+ NewRelic::Agent.logger.error "Uncaught exception while tracing HTTP request", err
58
+ return nil
59
+ end
60
+
61
+
62
+ # Finish tracing the HTTP +request+ that started at +t0+ with the information in
63
+ # +response+ and the given +http+ connection.
64
+ def finish_trace( t0, segment, request, response, http )
65
+ t1 = Time.now
66
+ duration = t1.to_f - t0.to_f
67
+
68
+ begin
69
+ if request && response && http
70
+ # Figure out which metrics we need to report based on the request and response
71
+ # The last (most-specific) one is scoped.
72
+ metrics = metrics_for( http, request, response )
73
+ scoped_metric = metrics.pop
74
+
75
+ # Report the metrics
76
+ metrics.each { |metric| get_metric(metric).trace_call(duration) }
77
+ get_scoped_metric( scoped_metric ).trace_call( duration )
78
+
79
+ # Add TT custom parameters
80
+ stats_engine.rename_scope_segment( scoped_metric )
81
+ extract_custom_parameters( response ) if response_is_crossapp?( response )
82
+ end
83
+ ensure
84
+ # We always need to pop the scope stack to avoid an inconsistent
85
+ # state, which will prevent tracing of the whole transaction.
86
+ stats_engine.pop_scope( segment, duration, t1 )
87
+ end
88
+ rescue NewRelic::Agent::CrossAppTracing::Error => err
89
+ NewRelic::Agent.logger.debug "while cross app tracing", err
90
+ rescue => err
91
+ NewRelic::Agent.logger.error "Uncaught exception while finishing an HTTP request trace", err
92
+ end
93
+
94
+
95
+ # Return +true+ if cross app tracing is enabled in the config.
96
+ def cross_app_enabled?
97
+ NewRelic::Agent.config[:cross_process_id] &&
98
+ (NewRelic::Agent.config[:"cross_application_tracer.enabled"] ||
99
+ NewRelic::Agent.config[:cross_application_tracing])
100
+ end
101
+
102
+
103
+ # Memoized fetcher for the cross app encoding key. Raises a
104
+ # NewRelic::Agent::CrossAppTracing::Error if the key isn't configured.
105
+ def cross_app_encoding_key
106
+ NewRelic::Agent.config[:encoding_key] or
107
+ raise NewRelic::Agent::CrossAppTracing::Error, "No encoding_key set."
108
+ end
109
+
110
+
111
+ # Inject the X-Process header into the outgoing +request+.
112
+ def inject_request_headers( request )
113
+ key = cross_app_encoding_key()
114
+ cross_app_id = NewRelic::Agent.config[:cross_process_id] or
115
+ raise NewRelic::Agent::CrossAppTracing::Error, "no cross app ID configured"
116
+ txn_guid = NewRelic::Agent::TransactionInfo.get.guid
117
+ txn_data = NewRelic.json_dump([ txn_guid, false ])
118
+
119
+ request[ NR_ID_HEADER ] = obfuscate_with_key( key, cross_app_id )
120
+ request[ NR_TXN_HEADER ] = obfuscate_with_key( key, txn_data )
121
+
122
+ rescue NewRelic::Agent::CrossAppTracing::Error => err
123
+ NewRelic::Agent.logger.debug "Not injecting x-process header", err
124
+ end
125
+
126
+
127
+ # Extract any custom parameters from +response+ if it's cross-application and
128
+ # add them to the current TT node.
129
+ def extract_custom_parameters( response )
130
+
131
+ appdata = extract_appdata( response )
132
+ sampler = NewRelic::Agent.instance.transaction_sampler
133
+ sampler.add_segment_parameters( :transaction_guid => appdata[APPDATA_TXN_GUID_INDEX] )
134
+
135
+ end
136
+
137
+
138
+ # Return the set of metrics (NewRelic::MethodTraceStats objects) that correspond to
139
+ # the given +request+ and +response+.
140
+ def metrics_for( http, request, response )
141
+ metrics = common_metrics( http )
142
+
143
+ if response_is_crossapp?( response )
144
+ begin
145
+ metrics.concat metrics_for_crossapp_response( http, response )
146
+ rescue => err
147
+ # Fall back to regular metrics if there's a problem with x-process metrics
148
+ NewRelic::Agent.logger.debug "%p while fetching x-process metrics: %s" %
149
+ [ err.class, err.message ]
150
+ metrics.concat metrics_for_regular_response( http, request, response )
151
+ end
152
+ else
153
+ NewRelic::Agent.logger.debug "Response doesn't have CAT headers."
154
+ metrics.concat metrics_for_regular_response( http, request, response )
155
+ end
156
+
157
+ return metrics
158
+ end
159
+
160
+
161
+ # Return an Array of metrics used for every response.
162
+ def common_metrics( http )
163
+ metrics = [ "External/all" ]
164
+ metrics << "External/#{http.address}/all"
165
+
166
+ if NewRelic::Agent::Instrumentation::MetricFrame.recording_web_transaction?
167
+ metrics << "External/allWeb"
168
+ else
169
+ metrics << "External/allOther"
170
+ end
171
+
172
+ return metrics
173
+ end
174
+
175
+
176
+ # Returns +true+ if Cross Application Tracing is enabled, and the given +response+
177
+ # has the appropriate headers.
178
+ def response_is_crossapp?( response )
179
+ return false unless cross_app_enabled?
180
+ unless response[NR_APPDATA_HEADER]
181
+ NewRelic::Agent.logger.debug "Response doesn't have the %p header: %p" %
182
+ [ NR_APPDATA_HEADER, response.to_hash ]
183
+ return false
184
+ end
185
+
186
+ return true
187
+ end
188
+
189
+
190
+ # Return the set of metric objects appropriate for the given cross app
191
+ # +response+.
192
+ def metrics_for_crossapp_response( http, response )
193
+ xp_id, txn_name, q_time, r_time, req_len, _ = extract_appdata( response )
194
+
195
+ check_crossapp_id( xp_id )
196
+ check_transaction_name( txn_name )
197
+
198
+ NewRelic::Agent.logger.debug "CAT xp_id: %p, txn_name: %p." % [ xp_id, txn_name ]
199
+
200
+ metrics = []
201
+ metrics << "ExternalApp/#{http.address}/#{xp_id}/all"
202
+ metrics << "ExternalTransaction/#{http.address}/#{xp_id}/#{txn_name}"
203
+
204
+ return metrics
205
+ end
206
+
207
+
208
+ # Extract x-process application data from the specified +response+ and return
209
+ # it as an array of the form:
210
+ #
211
+ # [
212
+ # <cross app ID>,
213
+ # <transaction name>,
214
+ # <queue time in seconds>,
215
+ # <response time in seconds>,
216
+ # <request content length in bytes>,
217
+ # <transaction GUID>
218
+ # ]
219
+ def extract_appdata( response )
220
+ appdata = response[NR_APPDATA_HEADER] or
221
+ raise NewRelic::Agent::CrossAppTracing::Error,
222
+ "Can't derive metrics for response: no #{NR_APPDATA_HEADER} header!"
223
+
224
+ key = cross_app_encoding_key()
225
+ decoded_appdata = decode_with_key( key, appdata )
226
+ decoded_appdata.set_encoding( ::Encoding::UTF_8 ) if
227
+ decoded_appdata.respond_to?( :set_encoding )
228
+
229
+ return NewRelic.json_load( decoded_appdata )
230
+ end
231
+
232
+
233
+ # Return the set of metric objects appropriate for the given (non-cross app)
234
+ # +response+.
235
+ def metrics_for_regular_response( http, request, response )
236
+ metrics = []
237
+ metrics << "External/#{http.address}/Net::HTTP/#{request.method}"
238
+
239
+ return metrics
240
+ end
241
+
242
+
243
+ # Convenience function for fetching the metric associated with +metric_name+.
244
+ def get_metric( metric_name )
245
+ stats_engine.get_stats_no_scope( metric_name )
246
+ end
247
+
248
+
249
+ # Convenience function for fetching the scoped metric associated with +metric_name+.
250
+ def get_scoped_metric( metric_name )
251
+ # Default is to use the metric_name itself as the scope, which is what we want
252
+ stats_engine.get_stats( metric_name )
253
+ end
254
+
255
+
256
+ # Fetch a reference to the stats engine.
257
+ def stats_engine
258
+ NewRelic::Agent.instance.stats_engine
259
+ end
260
+
261
+
262
+ # Check the given +id+ to ensure it conforms to the format of a cross-application
263
+ # ID. Raises an NewRelic::Agent::CrossAppTracing::Error if it doesn't.
264
+ def check_crossapp_id( id )
265
+ id =~ /\A\d+#\d+\z/ or
266
+ raise NewRelic::Agent::CrossAppTracing::Error,
267
+ "malformed cross application ID %p" % [ id ]
268
+ end
269
+
270
+
271
+ # Check the given +name+ to ensure it conforms to the format of a valid transaction
272
+ # name.
273
+ def check_transaction_name( name )
274
+ # No-op -- apparently absolutely anything is a valid transaction name?
275
+ # This is here for when that inevitably comes back to haunt us.
276
+ end
277
+
278
+ end
279
+ end
280
+ end
281
+
@@ -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,45 @@ 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
392
+ if (self.is_a?(Class) || self.is_a?(Module))
393
+ metric_class = self.name
394
+ else
395
+ metric_class = self.class.name
396
+ end
397
+ end
398
+
399
+ [ metric_class, options[:name] ].compact.join('/')
400
+ end
401
+
363
402
  # Write a metric frame onto a thread local if there isn't already one there.
364
403
  # If there is one, just update it.
365
404
  def _push_metric_frame(args) # :nodoc:
@@ -367,49 +406,31 @@ module NewRelic
367
406
 
368
407
  frame_data.apdex_start ||= _detect_upstream_wait(frame_data.start)
369
408
  _record_queue_length
409
+
370
410
  # If a block was passed in, then the arguments represent options for the instrumentation,
371
411
  # not app method arguments.
412
+ options = {}
372
413
  if args.any?
373
414
  if args.last.is_a?(Hash)
374
- options = args.last
415
+ options = args.pop
375
416
  frame_data.force_flag = options[:force]
376
417
  frame_data.request = options[:request] if options[:request]
377
418
  end
378
- category, path, available_params = _convert_args_to_path(args)
419
+ available_params = options[:params] || {}
420
+ options[:name] ||= args.first
379
421
  else
380
- category = 'Controller'
381
- path = newrelic_metric_path
382
422
  available_params = self.respond_to?(:params) ? self.params : {}
383
423
  end
384
424
  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
425
+
426
+ txn_name = transaction_name(options || {})
427
+
428
+ frame_data.push(txn_name)
429
+ NewRelic::Agent::TransactionInfo.get.transaction_name = txn_name
388
430
  frame_data.filtered_params = (respond_to? :filter_parameters) ? filter_parameters(available_params) : available_params
389
431
  frame_data
390
432
  end
391
433
 
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
434
  # Filter out a request if it matches one of our parameters for
414
435
  # ignoring it - the key is either 'do_not_trace' or 'ignore_apdex'
415
436
  def _is_filtered?(key)
@@ -438,23 +459,21 @@ module NewRelic
438
459
  end
439
460
  end
440
461
 
441
- include NewRelic::Agent::Instrumentation::QueueTime
442
-
443
462
  # Return a Time instance representing the upstream start time.
444
463
  # now is a Time instance to fall back on if no other candidate
445
464
  # for the start time is found.
446
465
  def _detect_upstream_wait(now)
447
- queue_start = nil
448
466
  if newrelic_request_headers
449
- queue_start = parse_frontend_headers(newrelic_request_headers)
467
+ queue_start = QueueTime.parse_frontend_timestamp(newrelic_request_headers, now)
468
+ QueueTime.record_frontend_metrics(queue_start, now) if queue_start
450
469
  end
451
470
  queue_start || now
452
471
  rescue => e
453
472
  ::NewRelic::Agent.logger.error("Error detecting upstream wait time:", e)
454
473
  now
455
474
  end
456
-
457
- # returns the NewRelic::MethodTraceStats object associated
475
+
476
+ # returns the NewRelic::Agent::Stats object associated
458
477
  # with the dispatcher time measurement
459
478
  def _dispatch_stat
460
479
  NewRelic::Agent.agent.stats_engine.get_stats_no_scope 'HttpDispatcher'