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
@@ -48,7 +48,7 @@ module NewRelic
48
48
  clazz.extend ClassMethods
49
49
  clazz.extend InstanceMethods
50
50
  end
51
-
51
+
52
52
  # Defines modules used at instrumentation runtime, to do the
53
53
  # actual tracing of time spent
54
54
  module InstanceMethods
@@ -93,27 +93,29 @@ module NewRelic
93
93
  end
94
94
 
95
95
  alias trace_method_execution_no_scope trace_execution_unscoped #:nodoc:
96
-
96
+
97
97
  # Refactored out of the previous trace_execution_scoped
98
98
  # method, most methods in this module relate to code used in
99
99
  # the #trace_execution_scoped method in this module
100
100
  module TraceExecutionScoped
101
+ extend self
102
+
101
103
  # Shorthand to return the NewRelic::Agent.instance
102
104
  def agent_instance
103
105
  NewRelic::Agent.instance
104
106
  end
105
-
107
+
106
108
  # Shorthand to return the status of tracing
107
109
  def traced?
108
110
  NewRelic::Agent.is_execution_traced?
109
111
  end
110
-
112
+
111
113
  # Tracing is disabled if we are not in a traced context and
112
114
  # no force option is supplied
113
115
  def trace_disabled?(options)
114
116
  !(traced? || options[:force])
115
117
  end
116
-
118
+
117
119
  # Shorthand to return the current statistics engine
118
120
  def stat_engine
119
121
  agent_instance.stats_engine
@@ -128,24 +130,13 @@ module NewRelic
128
130
  stat_engine.get_stats_no_scope(name)
129
131
  end
130
132
 
131
- def get_metric_specs(first_name, other_names, scope, options)
132
- specs = other_names.map { |name| NewRelic::MetricSpec.new(name) }
133
- if options[:metric]
134
- if scope && scope != first_name
135
- specs << NewRelic::MetricSpec.new(first_name, scope)
136
- end
137
- specs << NewRelic::MetricSpec.new(first_name) unless options[:scoped_metric_only]
138
- end
139
- specs
140
- end
141
-
142
133
  # Helper for setting a hash key if the hash key is nil,
143
134
  # instead of the default ||= behavior which sets if it is
144
135
  # false as well
145
136
  def set_if_nil(hash, key)
146
137
  hash[key] = true if hash[key].nil?
147
138
  end
148
-
139
+
149
140
  # delegates to #agent_instance to push a trace execution
150
141
  # flag, only if execution of this metric is forced.
151
142
  #
@@ -154,7 +145,7 @@ module NewRelic
154
145
  def push_flag!(forced)
155
146
  agent_instance.push_trace_execution_flag(true) if forced
156
147
  end
157
-
148
+
158
149
  # delegates to #agent_instance to pop the trace execution
159
150
  # flag, only if execution of this metric is
160
151
  # forced. otherwise this is taken care of for us
@@ -165,15 +156,15 @@ module NewRelic
165
156
  def pop_flag!(forced)
166
157
  agent_instance.pop_trace_execution_flag if forced
167
158
  end
168
-
159
+
169
160
  # helper for logging errors to the newrelic_agent.log
170
161
  # properly. Logs the error at error level
171
- def log_errors(code_area, metric)
162
+ def log_errors(code_area)
172
163
  yield
173
164
  rescue => e
174
- ::NewRelic::Agent.logger.error("Caught exception in #{code_area}. Metric name = #{metric}", e)
165
+ ::NewRelic::Agent.logger.error("Caught exception in #{code_area}.", e)
175
166
  end
176
-
167
+
177
168
  # provides the header for our traced execution scoped
178
169
  # method - gets the initial time, sets the tracing flag if
179
170
  # needed, and pushes the scope onto the metric stack
@@ -181,16 +172,66 @@ module NewRelic
181
172
  # the scope so that we can check for it later, to maintain
182
173
  # sanity. If the scope stack becomes unbalanced, this
183
174
  # transaction loses meaning.
184
- def trace_execution_scoped_header(metric, options, t0=Time.now.to_f)
185
- scope = log_errors("trace_execution_scoped header", metric) do
175
+ def trace_execution_scoped_header(options, t0=Time.now.to_f)
176
+ scope = log_errors("trace_execution_scoped header") do
186
177
  push_flag!(options[:force])
187
- scope = stat_engine.push_scope(metric, t0, options[:deduct_call_time_from_parent])
178
+ scope = stat_engine.push_scope(:method_tracer, t0, options[:deduct_call_time_from_parent])
188
179
  end
189
180
  # needed in case we have an error, above, to always return
190
181
  # the start time.
191
182
  [t0, scope]
192
183
  end
193
-
184
+
185
+ def metrics_for_current_transaction(first_name, other_names, options)
186
+ metrics = []
187
+
188
+ if !options[:scoped_metric_only]
189
+ metrics += other_names.map { |n| NewRelic::MetricSpec.new(n) }
190
+ end
191
+
192
+ if options[:metric]
193
+ if !options[:scoped_metric_only]
194
+ metrics << NewRelic::MetricSpec.new(first_name)
195
+ end
196
+ if in_transaction? && !options[:transaction]
197
+ metrics << NewRelic::MetricSpec.new(first_name, StatsEngine::MetricStats::SCOPE_PLACEHOLDER)
198
+ end
199
+ end
200
+
201
+ metrics
202
+ end
203
+
204
+ def in_transaction?
205
+ NewRelic::Agent::Transaction.in_transaction?
206
+ end
207
+
208
+ def has_parent?
209
+ NewRelic::Agent::Transaction.current &&
210
+ NewRelic::Agent::Transaction.current.has_parent?
211
+ end
212
+
213
+ def metrics_for_parent_transaction(first_name, options)
214
+ if has_parent? && options[:metric] && options[:transaction]
215
+ [NewRelic::MetricSpec.new(first_name, StatsEngine::MetricStats::SCOPE_PLACEHOLDER)]
216
+ else
217
+ []
218
+ end
219
+ end
220
+
221
+ def record_metrics(first_name, other_names, duration, exclusive, options)
222
+ metrics = metrics_for_current_transaction(first_name, other_names, options)
223
+ stat_engine.record_metrics(metrics) do |stat|
224
+ stat.record_data_point(duration, exclusive)
225
+ end
226
+
227
+ parent_metrics = metrics_for_parent_transaction(first_name, options)
228
+ parent_metrics.each do |metric|
229
+ stat_engine.transaction_stats_stack[-2].record(metric) do |stats|
230
+ stats.record_data_point(duration, exclusive)
231
+ end
232
+ end
233
+ end
234
+
194
235
  # Handles the end of the #trace_execution_scoped method -
195
236
  # calculating the time taken, popping the tracing flag if
196
237
  # needed, deducting time taken by children, and tracing the
@@ -199,17 +240,14 @@ module NewRelic
199
240
  # this method fails safely if the header does not manage to
200
241
  # push the scope onto the stack - it simply does not trace
201
242
  # any metrics.
202
- def trace_execution_scoped_footer(t0, first_name, metric_specs, expected_scope, forced, t1=Time.now.to_f)
203
- log_errors("trace_method_execution footer", first_name) do
204
- duration = t1 - t0
205
-
206
- pop_flag!(forced)
243
+ def trace_execution_scoped_footer(t0, first_name, metric_names, expected_scope, scope, options, t1=Time.now.to_f)
244
+ log_errors("trace_method_execution footer") do
245
+ pop_flag!(options[:force])
207
246
  if expected_scope
208
- scope = stat_engine.pop_scope(expected_scope, duration, t1)
247
+ scope = stat_engine.pop_scope(expected_scope, first_name, t1)
248
+ duration = t1 - t0
209
249
  exclusive = duration - scope.children_time
210
- stat_engine.record_metrics(metric_specs) do |stat|
211
- stat.record_data_point(duration, exclusive)
212
- end
250
+ record_metrics(first_name, metric_names, duration, exclusive, options)
213
251
  end
214
252
  end
215
253
  end
@@ -230,12 +268,11 @@ module NewRelic
230
268
  metric_names = Array(metric_names)
231
269
  first_name = metric_names.shift
232
270
  scope = stat_engine.scope_name
233
- start_time, expected_scope = trace_execution_scoped_header(first_name, options)
234
- begin
271
+ start_time, expected_scope = trace_execution_scoped_header(options)
272
+ begin
235
273
  yield
236
274
  ensure
237
- metric_specs = get_metric_specs(first_name, metric_names, scope, options)
238
- trace_execution_scoped_footer(start_time, first_name, metric_specs, expected_scope, options[:force])
275
+ trace_execution_scoped_footer(start_time, first_name, metric_names, expected_scope, scope, options)
239
276
  end
240
277
  end
241
278
 
@@ -243,20 +280,20 @@ module NewRelic
243
280
  include TraceExecutionScoped
244
281
 
245
282
  end
246
-
283
+
247
284
  # Defines methods used at the class level, for adding instrumentation
248
285
  module ClassMethods
249
286
  # contains methods refactored out of the #add_method_tracer method
250
287
  module AddMethodTracer
251
288
  ALLOWED_KEYS = [:force, :metric, :push_scope, :deduct_call_time_from_parent, :code_header, :code_footer, :scoped_metric_only].freeze
252
-
289
+
253
290
  # used to verify that the keys passed to
254
291
  # NewRelic::Agent::MethodTracer::ClassMethods#add_method_tracer
255
292
  # are valid. Returns a list of keys that were unexpected
256
293
  def unrecognized_keys(expected, given)
257
294
  given.keys - expected
258
295
  end
259
-
296
+
260
297
  # used to verify that the keys passed to
261
298
  # NewRelic::Agent::MethodTracer::ClassMethods#add_method_tracer
262
299
  # are valid. checks the expected list against the list
@@ -264,7 +301,7 @@ module NewRelic
264
301
  def any_unrecognized_keys?(expected, given)
265
302
  unrecognized_keys(expected, given).any?
266
303
  end
267
-
304
+
268
305
  # raises an error when the
269
306
  # NewRelic::Agent::MethodTracer::ClassMethods#add_method_tracer
270
307
  # method is called with improper keys. This aids in
@@ -274,7 +311,7 @@ module NewRelic
274
311
  raise "Unrecognized options in add_method_tracer_call: #{unrecognized_keys(ALLOWED_KEYS, options).join(', ')}"
275
312
  end
276
313
  end
277
-
314
+
278
315
  # Sets the options for deducting call time from
279
316
  # parents. This defaults to true if we are recording a metric, but
280
317
  # can be overridden by the user if desired.
@@ -284,7 +321,7 @@ module NewRelic
284
321
  def set_deduct_call_time_based_on_metric(options)
285
322
  {:deduct_call_time_from_parent => !!options[:metric]}.merge(options)
286
323
  end
287
-
324
+
288
325
  # validity checking - add_method_tracer must receive either
289
326
  # push scope or metric, or else it would record no
290
327
  # data. Raises an error if this is the case
@@ -295,7 +332,7 @@ module NewRelic
295
332
  end
296
333
 
297
334
  DEFAULT_SETTINGS = {:push_scope => true, :metric => true, :force => false, :code_header => "", :code_footer => "", :scoped_metric_only => false}.freeze
298
-
335
+
299
336
  # Checks the provided options to make sure that they make
300
337
  # sense. Raises an error if the options are incorrect to
301
338
  # assist with debugging, so that errors occur at class
@@ -315,7 +352,7 @@ module NewRelic
315
352
  def default_metric_name_code(method_name)
316
353
  "Custom/#{self.name}/#{method_name.to_s}"
317
354
  end
318
-
355
+
319
356
  # Checks to see if the method we are attempting to trace
320
357
  # actually exists or not. #add_method_tracer can't do
321
358
  # anything if the method doesn't exist.
@@ -324,7 +361,7 @@ module NewRelic
324
361
  ::NewRelic::Agent.logger.error("Did not trace #{self.name}##{method_name} because that method does not exist") unless exists
325
362
  exists
326
363
  end
327
-
364
+
328
365
  # Checks to see if we have already traced a method with a
329
366
  # given metric by checking to see if the traced method
330
367
  # exists. Warns the user if methods are being double-traced
@@ -334,7 +371,7 @@ module NewRelic
334
371
  ::NewRelic::Agent.logger.error("Attempt to trace a method twice with the same metric: Method = #{method_name}, Metric Name = #{metric_name_code}") if exists
335
372
  exists
336
373
  end
337
-
374
+
338
375
  # Returns a code snippet to be eval'd that skips tracing
339
376
  # when the agent is not tracing execution. turns
340
377
  # instrumentation into effectively one method call overhead
@@ -344,7 +381,7 @@ module NewRelic
344
381
  "return #{_untraced_method_name(method_name, metric_name_code)}(*args, &block) unless NewRelic::Agent.is_execution_traced?\n"
345
382
  end.to_s + options[:code_header].to_s
346
383
  end
347
-
384
+
348
385
  # returns an eval-able string that contains the traced
349
386
  # method code used if the agent is not creating a scope for
350
387
  # use in scoped metrics.
@@ -382,7 +419,7 @@ module NewRelic
382
419
  result
383
420
  end"
384
421
  end
385
-
422
+
386
423
  # Decides which code snippet we should be eval'ing in this
387
424
  # context, based on the options.
388
425
  def code_to_eval(method_name, metric_name_code, options)
@@ -397,7 +434,7 @@ module NewRelic
397
434
  include AddMethodTracer
398
435
 
399
436
 
400
-
437
+
401
438
  # Add a method tracer to the specified method.
402
439
  #
403
440
  # === Common Options
@@ -500,22 +537,22 @@ module NewRelic
500
537
  end
501
538
  end
502
539
  private
503
-
540
+
504
541
  # given a method and a metric, this method returns the
505
542
  # untraced alias of the method name
506
543
  def _untraced_method_name(method_name, metric_name)
507
544
  "#{_sanitize_name(method_name)}_without_trace_#{_sanitize_name(metric_name)}"
508
545
  end
509
-
546
+
510
547
  # given a method and a metric, this method returns the traced
511
548
  # alias of the method name
512
549
  def _traced_method_name(method_name, metric_name)
513
550
  "#{_sanitize_name(method_name)}_with_trace_#{_sanitize_name(metric_name)}"
514
551
  end
515
-
552
+
516
553
  # makes sure that method names do not contain characters that
517
554
  # might break the interpreter, for example ! or ? characters
518
- # that are not allowed in the middle of method names
555
+ # that are not allowed in the middle of method names
519
556
  def _sanitize_name(name)
520
557
  name.to_s.tr_s('^a-zA-Z0-9', '_')
521
558
  end
@@ -7,6 +7,12 @@
7
7
  module NewRelic
8
8
  module Agent
9
9
  class NullLogger
10
+ def fatal(*args); end
11
+ def error(*args); end
12
+ def warn(*args); end
13
+ def info(*args); end
14
+ def debug(*args); end
15
+
10
16
  def method_missing(method, *args, &blk)
11
17
  nil
12
18
  end
@@ -11,6 +11,11 @@ module NewRelic
11
11
  attr_reader :last_time
12
12
  def initialize
13
13
  super :cpu
14
+ @processor_count = NewRelic::Agent::SystemInfo.processor_count
15
+ if @processor_count.nil?
16
+ NewRelic::Agent.logger.warn("Failed to determine processor count, assuming 1")
17
+ @processor_count = 1
18
+ end
14
19
  poll
15
20
  end
16
21
 
@@ -42,7 +47,7 @@ module NewRelic
42
47
  if @last_time
43
48
  elapsed = now - @last_time
44
49
  return if elapsed < 1 # Causing some kind of math underflow
45
- num_processors = NewRelic::Control.instance.local_env.processors || 1
50
+
46
51
  usertime = t.utime - @last_utime
47
52
  systemtime = t.stime - @last_stime
48
53
 
@@ -50,10 +55,10 @@ module NewRelic
50
55
  record_usertime(usertime) if usertime >= 0
51
56
 
52
57
  # Calculate the true utilization by taking cpu times and dividing by
53
- # elapsed time X num_processors.
58
+ # elapsed time X processor_count.
54
59
 
55
- record_user_util(usertime / (elapsed * num_processors))
56
- record_system_util(systemtime / (elapsed * num_processors))
60
+ record_user_util(usertime / (elapsed * @processor_count))
61
+ record_system_util(systemtime / (elapsed * @processor_count))
57
62
  end
58
63
  @last_utime = t.utime
59
64
  @last_stime = t.stime
@@ -30,7 +30,7 @@ module NewRelic
30
30
  @sql_traces = {}
31
31
  clear_transaction_data
32
32
 
33
- # This lock is used to synchronize access to the @last_sample
33
+ # This lock is used to synchronize access to @sql_traces
34
34
  # and related variables. It can become necessary on JRuby or
35
35
  # any 'honest-to-god'-multithreaded system
36
36
  @samples_lock = Mutex.new
@@ -43,12 +43,12 @@ module NewRelic
43
43
  Agent.config[:'transaction_tracer.enabled']
44
44
  end
45
45
 
46
- def notice_transaction(path, uri=nil, params={})
46
+ def notice_transaction(uri=nil, params={})
47
47
  if NewRelic::Agent.instance.transaction_sampler.builder
48
48
  guid = NewRelic::Agent.instance.transaction_sampler.builder.sample.guid
49
49
  end
50
50
  if Agent.config[:'slow_sql.enabled'] && transaction_data
51
- transaction_data.set_transaction_info(path, uri, params, guid)
51
+ transaction_data.set_transaction_info(uri, params, guid)
52
52
  end
53
53
  end
54
54
 
@@ -69,8 +69,9 @@ module NewRelic
69
69
  end
70
70
 
71
71
  # This is called when we are done with the transaction.
72
- def notice_scope_empty(time=Time.now)
72
+ def notice_scope_empty(name, time=Time.now)
73
73
  data = transaction_data
74
+ data.set_transaction_name(name)
74
75
  clear_transaction_data
75
76
 
76
77
  if data.sql_data.size > 0
@@ -145,12 +146,15 @@ module NewRelic
145
146
  @sql_data = []
146
147
  end
147
148
 
148
- def set_transaction_info(path, uri, params, guid)
149
- @path = path
149
+ def set_transaction_info(uri, params, guid)
150
150
  @uri = uri
151
151
  @params = params
152
152
  @guid = guid
153
153
  end
154
+
155
+ def set_transaction_name(name)
156
+ @path = name
157
+ end
154
158
  end
155
159
 
156
160
  class SlowSql
@@ -9,6 +9,8 @@ module NewRelic
9
9
  class StatsEngine
10
10
  # Handles methods related to actual Metric collection
11
11
  module MetricStats
12
+ SCOPE_PLACEHOLDER = :__SCOPE__
13
+
12
14
  # Lookup and write to the named metric in a single call.
13
15
  #
14
16
  # This method is thead-safe, and is preferred to the lookup / modify
@@ -16,13 +18,19 @@ module NewRelic
16
18
  def record_metrics(metric_names_or_specs, value=nil, options={}, &blk)
17
19
  defaults = {
18
20
  :scoped => false,
19
- :scope => scope_name
21
+ :scope => in_transaction? ? SCOPE_PLACEHOLDER : nil
20
22
  }
21
23
  options = defaults.merge(options)
24
+
22
25
  effective_scope = options[:scoped] && options[:scope]
23
26
  specs = coerce_to_metric_spec_array(metric_names_or_specs, effective_scope)
24
- with_stats_lock do
25
- @stats_hash.record(specs, value, &blk)
27
+
28
+ if in_transaction?
29
+ transaction_stats_hash.record(specs, value, &blk)
30
+ else
31
+ with_stats_lock do
32
+ @stats_hash.record(specs, value, &blk)
33
+ end
26
34
  end
27
35
  end
28
36
 
@@ -155,6 +163,14 @@ module NewRelic
155
163
  @stats_hash.keys.map { |spec| spec.to_s }
156
164
  end
157
165
  end
166
+
167
+ def metric_specs
168
+ with_stats_lock { @stats_hash.keys }
169
+ end
170
+
171
+ def in_transaction?
172
+ !!transaction_stats_hash
173
+ end
158
174
  end
159
175
  end
160
176
  end
@@ -2,17 +2,17 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
4
 
5
- # -*- coding: utf-8 -*-
6
5
  module NewRelic
7
6
  module Agent
8
7
  class StatsEngine
9
8
  # A simple stack element that tracks the current name and length
10
9
  # of the executing stack
11
10
  class ScopeStackElement
12
- attr_reader :deduct_call_time_from_parent
13
- attr_accessor :name, :children_time
14
- def initialize(name, deduct_call_time)
15
- @name = name
11
+ attr_reader :deduct_call_time_from_parent, :tag
12
+ attr_accessor :name, :start_time, :children_time
13
+ def initialize(tag, start_time, deduct_call_time)
14
+ @tag = tag
15
+ @start_time = start_time
16
16
  @deduct_call_time_from_parent = deduct_call_time
17
17
  @children_time = 0
18
18
  end
@@ -50,29 +50,38 @@ module Agent
50
50
 
51
51
  # Pushes a scope onto the transaction stack - this generates a
52
52
  # TransactionSample::Segment at the end of transaction execution
53
- def push_scope(metric, time = Time.now.to_f, deduct_call_time_from_parent = true)
53
+ # The generated segment will not be named until the corresponding
54
+ # pop_scope call is made.
55
+ # +tag+ should be a Symbol, and is only used for debugging purposes to
56
+ # identify this scope if the stack gets corrupted.
57
+ def push_scope(tag, time = Time.now.to_f, deduct_call_time_from_parent = true)
54
58
  stack = scope_stack
55
- @transaction_sampler.notice_push_scope metric, time if sampler_enabled?
56
- scope = ScopeStackElement.new(metric, deduct_call_time_from_parent)
59
+ @transaction_sampler.notice_push_scope(time) if sampler_enabled?
60
+ scope = ScopeStackElement.new(tag, time, deduct_call_time_from_parent)
57
61
  stack.push scope
58
62
  scope
59
63
  end
60
64
 
61
65
  # Pops a scope off the transaction stack - this updates the
62
66
  # transaction sampler that we've finished execution of a traced method
63
- def pop_scope(expected_scope, duration, time=Time.now.to_f)
67
+ # +expected_scope+ should be the ScopeStackElement that was returned by
68
+ # the corresponding push_scope call.
69
+ # +name+ is the name that will be applied to the generated transaction
70
+ # trace segment.
71
+ def pop_scope(expected_scope, name, time=Time.now.to_f)
64
72
  stack = scope_stack
65
73
  scope = stack.pop
66
- fail "unbalanced pop from blame stack, got #{scope ? scope.name : 'nil'}, expected #{expected_scope ? expected_scope.name : 'nil'}" if scope != expected_scope
74
+ fail "unbalanced pop from blame stack, got #{scope ? scope.tag : 'nil'}, expected #{expected_scope ? expected_scope.tag : 'nil'}" if scope != expected_scope
67
75
 
68
76
  if !stack.empty?
69
77
  if scope.deduct_call_time_from_parent
70
- stack.last.children_time += duration
78
+ stack.last.children_time += (time - scope.start_time)
71
79
  else
72
80
  stack.last.children_time += scope.children_time
73
81
  end
74
82
  end
75
- @transaction_sampler.notice_pop_scope(scope.name, time) if sampler_enabled?
83
+ @transaction_sampler.notice_pop_scope(name, time) if sampler_enabled?
84
+ scope.name = name
76
85
  scope
77
86
  end
78
87
 
@@ -80,17 +89,6 @@ module Agent
80
89
  @transaction_sampler && Agent.config[:'transaction_tracer.enabled']
81
90
  end
82
91
 
83
- # Rename the segment associated with the last pushed scope to +new_name+.
84
- def rename_scope_segment( new_name )
85
- self.peek_scope.name = new_name
86
- @transaction_sampler.rename_scope_segment( new_name ) if sampler_enabled?
87
- end
88
-
89
- # Returns the latest ScopeStackElement
90
- def peek_scope
91
- scope_stack.last
92
- end
93
-
94
92
  # set the name of the transaction for the current thread, which will be used
95
93
  # to define the scope of all traced methods called on this thread until the
96
94
  # scope stack is empty.
@@ -109,11 +107,8 @@ module Agent
109
107
  end
110
108
 
111
109
  # Start a new transaction, unless one is already in progress
112
- def start_transaction(name = nil)
113
- Thread::current[:newrelic_scope_stack] ||= []
114
- self.scope_name = name if name
115
- NewRelic::Agent.instance.events.notify(:start_transaction, name)
116
- GCProfiler.init
110
+ def start_transaction
111
+ NewRelic::Agent.instance.events.notify(:start_transaction)
117
112
  end
118
113
 
119
114
  # Try to clean up gracefully, otherwise we leave things hanging around on thread locals.
@@ -121,11 +116,6 @@ module Agent
121
116
  # and is ignored.
122
117
  #
123
118
  def end_transaction
124
- elapsed = GCProfiler.capture
125
- if @transaction_sampler && @transaction_sampler.last_sample
126
- @transaction_sampler.last_sample.params[:custom_params] ||= {}
127
- @transaction_sampler.last_sample.params[:custom_params][:gc_time] = elapsed
128
- end
129
119
  stack = scope_stack
130
120
 
131
121
  if stack && stack.empty?
@@ -134,12 +124,41 @@ module Agent
134
124
  end
135
125
  end
136
126
 
137
- private
127
+ def transaction_stats_hash
128
+ transaction_stats_stack.last
129
+ end
130
+
131
+ def push_transaction_stats
132
+ transaction_stats_stack << StatsHash.new
133
+ end
134
+
135
+ def pop_transaction_stats(transaction_name)
136
+ Thread::current[:newrelic_scope_stack] ||= []
137
+ stats = transaction_stats_stack.pop
138
+ merge!(apply_scopes(stats, transaction_name)) if stats
139
+ stats
140
+ end
141
+
142
+ def apply_scopes(stats_hash, resolved_name)
143
+ new_stats = StatsHash.new
144
+ stats_hash.each do |spec, stats|
145
+ if spec.scope != '' &&
146
+ spec.scope.to_sym == StatsEngine::SCOPE_PLACEHOLDER
147
+ spec.scope = resolved_name
148
+ end
149
+ new_stats[spec] = stats
150
+ end
151
+ return new_stats
152
+ end
138
153
 
139
154
  # Returns the current scope stack, memoized to a thread local variable
140
155
  def scope_stack
141
156
  Thread::current[:newrelic_scope_stack] ||= []
142
157
  end
158
+
159
+ def transaction_stats_stack
160
+ Thread.current[:newrelic_transaction_stack] ||= []
161
+ end
143
162
  end
144
163
  end
145
164
  end