newrelic_rpm 3.0.1 → 3.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of newrelic_rpm might be problematic. Click here for more details.

Files changed (74) hide show
  1. data/CHANGELOG +2 -3
  2. data/README.rdoc +3 -3
  3. data/lib/new_relic/agent.rb +19 -7
  4. data/lib/new_relic/agent/agent.rb +83 -19
  5. data/lib/new_relic/agent/beacon_configuration.rb +8 -12
  6. data/lib/new_relic/agent/browser_monitoring.rb +8 -8
  7. data/lib/new_relic/agent/error_collector.rb +13 -13
  8. data/lib/new_relic/agent/instrumentation.rb +9 -0
  9. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +10 -2
  10. data/lib/new_relic/agent/instrumentation/metric_frame.rb +41 -35
  11. data/lib/new_relic/agent/instrumentation/metric_frame/pop.rb +92 -0
  12. data/lib/new_relic/agent/method_tracer.rb +0 -2
  13. data/lib/new_relic/agent/shim_agent.rb +2 -0
  14. data/lib/new_relic/agent/stats_engine/metric_stats.rb +89 -60
  15. data/lib/new_relic/agent/stats_engine/transactions.rb +1 -1
  16. data/lib/new_relic/agent/worker_loop.rb +1 -1
  17. data/lib/new_relic/collection_helper.rb +0 -2
  18. data/lib/new_relic/control/class_methods.rb +25 -12
  19. data/lib/new_relic/control/logging_methods.rb +30 -17
  20. data/lib/new_relic/data_serialization.rb +81 -0
  21. data/lib/new_relic/local_environment.rb +1 -1
  22. data/lib/new_relic/metric_data.rb +9 -5
  23. data/lib/new_relic/metric_spec.rb +7 -1
  24. data/lib/new_relic/rack/browser_monitoring.rb +1 -7
  25. data/lib/new_relic/stats.rb +4 -0
  26. data/lib/new_relic/transaction_analysis.rb +45 -88
  27. data/lib/new_relic/transaction_analysis/segment_summary.rb +47 -0
  28. data/lib/new_relic/transaction_sample.rb +15 -332
  29. data/lib/new_relic/transaction_sample/composite_segment.rb +27 -0
  30. data/lib/new_relic/transaction_sample/fake_segment.rb +9 -0
  31. data/lib/new_relic/transaction_sample/segment.rb +250 -0
  32. data/lib/new_relic/transaction_sample/summary_segment.rb +21 -0
  33. data/lib/new_relic/version.rb +3 -3
  34. data/newrelic.yml +3 -3
  35. data/newrelic_rpm.gemspec +27 -4
  36. data/test/active_record_fixtures.rb +31 -13
  37. data/test/new_relic/agent/agent/start_worker_thread_test.rb +1 -3
  38. data/test/new_relic/agent/agent_test.rb +73 -28
  39. data/test/new_relic/agent/agent_test_controller_test.rb +11 -10
  40. data/test/new_relic/agent/beacon_configuration_test.rb +37 -20
  41. data/test/new_relic/agent/browser_monitoring_test.rb +17 -28
  42. data/test/new_relic/agent/error_collector/notice_error_test.rb +9 -7
  43. data/test/new_relic/agent/error_collector_test.rb +6 -7
  44. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +12 -5
  45. data/test/new_relic/agent/instrumentation/metric_frame/pop_test.rb +195 -0
  46. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +60 -58
  47. data/test/new_relic/agent/instrumentation/queue_time_test.rb +14 -0
  48. data/test/new_relic/agent/instrumentation/rack_test.rb +35 -0
  49. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +0 -1
  50. data/test/new_relic/agent/method_tracer_test.rb +8 -8
  51. data/test/new_relic/agent/sampler_test.rb +19 -0
  52. data/test/new_relic/agent/shim_agent_test.rb +20 -0
  53. data/test/new_relic/agent/stats_engine/metric_stats/harvest_test.rb +150 -0
  54. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +1 -0
  55. data/test/new_relic/agent/stats_engine/samplers_test.rb +4 -3
  56. data/test/new_relic/agent/{stats_engine/stats_engine_test.rb → stats_engine_test.rb} +8 -8
  57. data/test/new_relic/agent/transaction_sampler_test.rb +1 -1
  58. data/test/new_relic/agent/worker_loop_test.rb +2 -2
  59. data/test/new_relic/control/class_methods_test.rb +62 -0
  60. data/test/new_relic/control/logging_methods_test.rb +157 -0
  61. data/test/new_relic/control_test.rb +10 -10
  62. data/test/new_relic/data_serialization_test.rb +50 -0
  63. data/test/new_relic/local_environment_test.rb +13 -13
  64. data/test/new_relic/metric_data_test.rb +125 -0
  65. data/test/new_relic/metric_spec_test.rb +8 -0
  66. data/test/new_relic/transaction_analysis/segment_summary_test.rb +77 -0
  67. data/test/new_relic/transaction_analysis_test.rb +121 -0
  68. data/test/new_relic/transaction_sample/composite_segment_test.rb +35 -0
  69. data/test/new_relic/transaction_sample/fake_segment_test.rb +17 -0
  70. data/test/new_relic/transaction_sample/segment_test.rb +454 -0
  71. data/test/new_relic/transaction_sample/summary_segment_test.rb +31 -0
  72. data/test/new_relic/transaction_sample_test.rb +51 -0
  73. data/test/test_helper.rb +4 -14
  74. metadata +32 -7
@@ -1,3 +1,5 @@
1
+ require 'new_relic/agent/instrumentation/metric_frame/pop'
2
+
1
3
  # A struct holding the information required to measure a controller
2
4
  # action. This is put on the thread local. Handles the issue of
3
5
  # re-entrancy, or nested action calls.
@@ -8,6 +10,9 @@ module NewRelic
8
10
  module Agent
9
11
  module Instrumentation
10
12
  class MetricFrame
13
+ # helper module refactored out of the `pop` method
14
+ include Pop
15
+
11
16
  attr_accessor :start # A Time instance for the start time, never nil
12
17
  attr_accessor :apdex_start # A Time instance used for calculating the apdex score, which
13
18
  # might end up being @start, or it might be further upstream if
@@ -35,7 +40,7 @@ module NewRelic
35
40
  # Reconnect to the server if necessary. This is only done
36
41
  # for old versions of passenger that don't implement an explicit after_fork
37
42
  # event.
38
- NewRelic::Agent.instance.after_fork(:keep_retrying => false) if @@check_server_connection
43
+ agent.after_fork(:keep_retrying => false) if @@check_server_connection
39
44
 
40
45
  Thread.current[:newrelic_metric_frame] = new
41
46
  end
@@ -50,6 +55,10 @@ module NewRelic
50
55
  current && current.referer
51
56
  end
52
57
 
58
+ def self.agent
59
+ NewRelic::Agent.instance
60
+ end
61
+
53
62
  @@java_classes_loaded = false
54
63
 
55
64
  if defined? JRuby
@@ -71,10 +80,22 @@ module NewRelic
71
80
  @process_cpu_start = process_cpu
72
81
  end
73
82
 
83
+ def agent
84
+ NewRelic::Agent.instance
85
+ end
86
+
87
+ def transaction_sampler
88
+ agent.transaction_sampler
89
+ end
90
+
91
+ private :agent
92
+ private :transaction_sampler
93
+
94
+
74
95
  # Indicate that we are entering a measured controller action or task.
75
96
  # Make sure you unwind every push with a pop call.
76
97
  def push(m)
77
- NewRelic::Agent.instance.transaction_sampler.notice_first_scope_push(start)
98
+ transaction_sampler.notice_first_scope_push(start)
78
99
  @path_stack.push NewRelic::MetricParser::MetricParser.for_metric_named(m)
79
100
  end
80
101
 
@@ -96,15 +117,16 @@ module NewRelic
96
117
 
97
118
  # Call this to ensure that the current transaction is not saved
98
119
  def abort_transaction!
99
- NewRelic::Agent.instance.transaction_sampler.ignore_transaction
120
+ transaction_sampler.ignore_transaction
100
121
  end
101
- # This needs to be called after entering the call to trace the controller action, otherwise
102
- # the controller action blames itself. It gets reset in the normal #pop call.
122
+ # This needs to be called after entering the call to trace the
123
+ # controller action, otherwise the controller action blames
124
+ # itself. It gets reset in the normal #pop call.
103
125
  def start_transaction
104
- NewRelic::Agent.instance.stats_engine.start_transaction metric_name
126
+ agent.stats_engine.start_transaction metric_name
105
127
  # Only push the transaction context info once, on entry:
106
128
  if @path_stack.size == 1
107
- NewRelic::Agent.instance.transaction_sampler.notice_transaction(metric_name, uri, filtered_params)
129
+ transaction_sampler.notice_transaction(metric_name, uri, filtered_params)
108
130
  end
109
131
  end
110
132
 
@@ -121,28 +143,11 @@ module NewRelic
121
143
  # does the appropriate wrapup of the context.
122
144
  def pop
123
145
  metric = @path_stack.pop
124
- if metric.nil?
125
- NewRelic::Agent.logger.error "Underflow in metric frames: #{caller.join("\n ")}"
126
- end
146
+ log_underflow if metric.nil?
127
147
  if @path_stack.empty?
128
- if NewRelic::Agent.is_execution_traced?
129
- cpu_burn = nil
130
- if @process_cpu_start
131
- cpu_burn = process_cpu - @process_cpu_start
132
- elsif @jruby_cpu_start
133
- cpu_burn = jruby_cpu_time - @jruby_cpu_start
134
- NewRelic::Agent.get_stats_no_scope(NewRelic::Metrics::USER_TIME).record_data_point(cpu_burn)
135
- end
136
- NewRelic::Agent.instance.transaction_sampler.notice_transaction_cpu_time(cpu_burn) if cpu_burn
137
- NewRelic::Agent.instance.histogram.process((Time.now - start).to_f) if metric.is_web_transaction?
138
- NewRelic::Agent.instance.transaction_sampler.notice_scope_empty
139
- end
140
- NewRelic::Agent.instance.stats_engine.end_transaction
141
- Thread.current[:newrelic_start_time] = (Thread.current[:newrelic_metric_frame].start rescue nil)
142
- Thread.current[:newrelic_metric_frame] = nil
143
- else # path stack not empty
144
- # change the transaction name back to whatever was on the stack.
145
- NewRelic::Agent.instance.stats_engine.scope_name = metric_name
148
+ handle_empty_path_stack(metric)
149
+ else
150
+ set_new_scope!(current_stack_metric)
146
151
  end
147
152
  end
148
153
 
@@ -157,14 +162,15 @@ module NewRelic
157
162
  # Anything left over is treated as custom params
158
163
 
159
164
  def self.notice_error(e, options={})
160
- if request = options.delete(:request)
165
+ request = options.delete(:request)
166
+ if request
161
167
  options[:referer] = referer_from_request(request)
162
168
  options[:uri] = uri_from_request(request)
163
169
  end
164
170
  if current
165
171
  current.notice_error(e, options)
166
172
  else
167
- NewRelic::Agent.instance.error_collector.notice_error(e, options)
173
+ agent.error_collector.notice_error(e, options)
168
174
  end
169
175
  end
170
176
 
@@ -177,7 +183,7 @@ module NewRelic
177
183
  options[:metric] = metric_name
178
184
  options.merge!(custom_parameters)
179
185
  if exception != e
180
- result = NewRelic::Agent.instance.error_collector.notice_error(e, options)
186
+ result = agent.error_collector.notice_error(e, options)
181
187
  self.exception = result if result
182
188
  end
183
189
  end
@@ -242,7 +248,8 @@ module NewRelic
242
248
  end
243
249
 
244
250
  def self.recording_web_transaction?
245
- if c = Thread.current[:newrelic_metric_frame]
251
+ c = Thread.current[:newrelic_metric_frame]
252
+ if c
246
253
  c.recording_web_transaction?
247
254
  end
248
255
  end
@@ -276,8 +283,8 @@ module NewRelic
276
283
  end
277
284
 
278
285
  def self.record_apdex(current_metric, action_duration, total_duration, is_error)
279
- summary_stat = NewRelic::Agent.instance.stats_engine.get_custom_stats("Apdex", NewRelic::ApdexStats)
280
- controller_stat = NewRelic::Agent.instance.stats_engine.get_custom_stats(current_metric.apdex_metric_path, NewRelic::ApdexStats)
286
+ summary_stat = agent.stats_engine.get_custom_stats("Apdex", NewRelic::ApdexStats)
287
+ controller_stat = agent.stats_engine.get_custom_stats(current_metric.apdex_metric_path, NewRelic::ApdexStats)
281
288
  update_apdex(summary_stat, total_duration, is_error)
282
289
  update_apdex(controller_stat, action_duration, is_error)
283
290
  end
@@ -313,7 +320,6 @@ module NewRelic
313
320
  java_utime = threadMBean.getCurrentThreadUserTime() # ns
314
321
  -1 == java_utime ? 0.0 : java_utime/1e9
315
322
  end
316
-
317
323
  end
318
324
  end
319
325
  end
@@ -0,0 +1,92 @@
1
+ require 'new_relic/agent/instrumentation'
2
+ module NewRelic
3
+ module Agent
4
+ module Instrumentation
5
+ class MetricFrame
6
+ module Pop
7
+
8
+ def clear_thread_metric_frame!
9
+ Thread.current[:newrelic_metric_frame] = nil
10
+ end
11
+
12
+ def set_last_start_time!
13
+ frame = Thread.current[:newrelic_metric_frame]
14
+ if frame && frame.respond_to?(:start)
15
+ Thread.current[:newrelic_start_time] = Thread.current[:newrelic_metric_frame].start
16
+ end
17
+ end
18
+
19
+ def set_new_scope!(metric)
20
+ agent.stats_engine.scope_name = metric
21
+ end
22
+
23
+ def log_underflow
24
+ NewRelic::Agent.logger.error "Underflow in metric frames: #{caller.join("\n ")}"
25
+ end
26
+
27
+ def process_histogram_for_transaction(ending)
28
+ agent.histogram.process((ending - start).to_f)
29
+ end
30
+
31
+ def notice_scope_empty
32
+ transaction_sampler.notice_scope_empty
33
+ end
34
+
35
+ def record_transaction_cpu
36
+ burn = cpu_burn
37
+ transaction_sampler.notice_transaction_cpu_time(burn) if burn
38
+ end
39
+
40
+ def normal_cpu_burn
41
+ return unless @process_cpu_start
42
+ process_cpu - @process_cpu_start
43
+ end
44
+
45
+ def jruby_cpu_burn
46
+ return unless @jruby_cpu_start
47
+ burn = (jruby_cpu_time - @jruby_cpu_start)
48
+ record_jruby_cpu_burn(burn)
49
+ burn
50
+ end
51
+
52
+ # we need to do this here because the normal cpu sampler
53
+ # process doesn't work on JRuby. See the cpu_sampler.rb file
54
+ # to understand where cpu is recorded for non-jruby processes
55
+ def record_jruby_cpu_burn(burn)
56
+ NewRelic::Agent.get_stats_no_scope(NewRelic::Metrics::USER_TIME).record_data_point(burn)
57
+ end
58
+
59
+ def cpu_burn
60
+ normal_cpu_burn || jruby_cpu_burn
61
+ end
62
+
63
+ def end_transaction!
64
+ agent.stats_engine.end_transaction
65
+ end
66
+
67
+ def notify_transaction_sampler(web_transaction)
68
+ record_transaction_cpu
69
+ process_histogram_for_transaction(Time.now) if web_transaction
70
+ notice_scope_empty
71
+ end
72
+
73
+ def traced?
74
+ NewRelic::Agent.is_execution_traced?
75
+ end
76
+
77
+ def handle_empty_path_stack(metric)
78
+ raise 'path stack not empty' unless @path_stack.empty?
79
+ notify_transaction_sampler(metric.is_web_transaction?) if traced?
80
+ end_transaction!
81
+ set_last_start_time!
82
+ clear_thread_metric_frame!
83
+ end
84
+
85
+ def current_stack_metric
86
+ metric_name
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -81,8 +81,6 @@ module NewRelic
81
81
  end
82
82
  end
83
83
 
84
- EMPTY_ARRAY = [].freeze
85
-
86
84
  # Deprecated. Use #trace_execution_scoped, a version with an options hash.
87
85
  def trace_method_execution_with_scope(metric_names, produce_metric, deduct_call_time_from_parent, scoped_metric_only=false, &block) #:nodoc:
88
86
  trace_execution_scoped(metric_names,
@@ -18,6 +18,8 @@ module NewRelic
18
18
  def after_fork *args; end
19
19
  def start *args; end
20
20
  def shutdown; end
21
+ def serialize; end
22
+ def merge_data_from(*args); end
21
23
  def push_trace_execution_flag(*args); end
22
24
  def pop_trace_execution_flag(*args); end
23
25
  def browser_timing_header; "" end
@@ -39,98 +39,127 @@ module NewRelic
39
39
  end
40
40
  end
41
41
 
42
- # The stats hash hashes either a metric name for an unscoped metric,
43
- # or a metric_spec for a scoped metric value.
44
- def lookup_stat(metric_name)
45
- stats_hash[metric_name]
46
- end
47
-
48
42
  def metrics
49
43
  stats_hash.keys.map(&:to_s)
50
44
  end
51
45
 
52
46
  def get_stats_no_scope(metric_name)
53
- stats_hash[metric_name] ||= NewRelic::MethodTraceStats.new
47
+ stats_hash[NewRelic::MetricSpec.new(metric_name, '')] ||= NewRelic::MethodTraceStats.new
54
48
  end
55
49
 
56
50
  # This version allows a caller to pass a stat class to use
57
51
  #
58
52
  def get_custom_stats(metric_name, stat_class)
59
- stats_hash[metric_name] ||= stat_class.new
53
+ stats_hash[NewRelic::MetricSpec.new(metric_name)] ||= stat_class.new
60
54
  end
61
55
 
62
56
  # If use_scope is true, two chained metrics are created, one with scope and one without
63
57
  # If scoped_metric_only is true, only a scoped metric is created (used by rendering metrics which by definition are per controller only)
64
58
  def get_stats(metric_name, use_scope = true, scoped_metric_only = false, scope = nil)
65
- scope ||= scope_name if use_scope
66
- if scoped_metric_only
59
+ scope ||= scope_name if use_scope
60
+ if scoped_metric_only
61
+ spec = NewRelic::MetricSpec.new metric_name, scope
62
+ stats = stats_hash[spec] ||= NewRelic::MethodTraceStats.new
63
+ else
64
+ stats = stats_hash[NewRelic::MetricSpec.new(metric_name)] ||= NewRelic::MethodTraceStats.new
65
+ if scope && scope != metric_name
67
66
  spec = NewRelic::MetricSpec.new metric_name, scope
68
- stats = stats_hash[spec] ||= NewRelic::MethodTraceStats.new
69
- else
70
- stats = stats_hash[metric_name] ||= NewRelic::MethodTraceStats.new
71
- if scope && scope != metric_name
72
- spec = NewRelic::MetricSpec.new metric_name, scope
73
- stats = stats_hash[spec] ||= NewRelic::ScopedMethodTraceStats.new(stats)
74
- end
67
+ stats = stats_hash[spec] ||= NewRelic::ScopedMethodTraceStats.new(stats)
75
68
  end
76
- stats
69
+ end
70
+ stats
77
71
  end
78
72
 
79
- def lookup_stats(metric_name, scope_name = nil)
80
- stats_hash[NewRelic::MetricSpec.new(metric_name, scope_name)] ||
81
- stats_hash[metric_name]
73
+ def lookup_stats(metric_name, scope_name = '')
74
+ stats_hash[NewRelic::MetricSpec.new(metric_name, scope_name)]
82
75
  end
83
- # Harvest the timeslice data. First recombine current statss
84
- # with any previously
85
- # unsent metrics, clear out stats cache, and return the current
86
- # stats.
87
- # ---
88
- # Note: this is not synchronized. There is still some risk in this and
89
- # we will revisit later to see if we can make this more robust without
90
- # sacrificing efficiency.
91
- # +++
92
- def harvest_timeslice_data(previous_timeslice_data, metric_ids)
93
- timeslice_data = {}
94
- poll harvest_samplers
95
- stats_hash.keys.each do | metric_spec |
96
76
 
77
+ module Harvest
97
78
 
98
- # get a copy of the stats collected since the last harvest, and clear
99
- # the stats inside our hash table for the next time slice.
100
- stats = stats_hash[metric_spec]
101
-
102
- # we have an optimization for unscoped metrics
103
- if !(metric_spec.is_a? NewRelic::MetricSpec)
104
- metric_spec = NewRelic::MetricSpec.new metric_spec
79
+ def merge_data(metric_data_hash)
80
+ metric_data_hash.each do |metric_spec, metric_data|
81
+ new_data = lookup_stats(metric_spec.name, metric_spec.scope)
82
+ if new_data
83
+ new_data.merge!(metric_data.stats)
84
+ else
85
+ stats_hash[metric_spec] = metric_data.stats
105
86
  end
87
+ end
88
+ end
106
89
 
107
- if stats.nil?
108
- raise "Nil stats for #{metric_spec.name} (#{metric_spec.scope})"
109
- end
90
+ private
91
+ def get_stats_hash_from(engine_or_hash)
92
+ if engine_or_hash.is_a?(StatsEngine)
93
+ engine_or_hash.stats_hash
94
+ else
95
+ engine_or_hash
96
+ end
97
+ end
98
+
99
+ def coerce_to_metric_spec(metric_spec)
100
+ if metric_spec.is_a?(NewRelic::MetricSpec)
101
+ metric_spec
102
+ else
103
+ NewRelic::MetricSpec.new(metric_spec)
104
+ end
105
+ end
110
106
 
111
- stats_copy = stats.clone
112
- stats.reset
107
+ def clone_and_reset_stats(metric_spec, stats)
108
+ if stats.nil?
109
+ raise "Nil stats for #{metric_spec.name} (#{metric_spec.scope})"
110
+ end
113
111
 
114
- # if the previous timeslice data has not been reported (due to an error of some sort)
115
- # then we need to merge this timeslice with the previously accumulated - but not sent
116
- # data
117
- previous_metric_data = previous_timeslice_data[metric_spec]
118
- stats_copy.merge! previous_metric_data.stats unless previous_metric_data.nil?
112
+ stats_copy = stats.clone
113
+ stats.reset
114
+ stats_copy
115
+ end
119
116
 
120
- # don't bother collecting and reporting stats that have zero-values for this timeslice.
121
- # significant performance boost and storage savings.
122
- unless stats_copy.is_reset?
117
+ # if the previous timeslice data has not been reported (due to an error of some sort)
118
+ # then we need to merge this timeslice with the previously accumulated - but not sent
119
+ # data
120
+ def merge_old_data!(metric_spec, stats, old_data)
121
+ metric_data = old_data[metric_spec]
122
+ stats.merge!(metric_data.stats) unless metric_data.nil?
123
+ end
123
124
 
124
- id = metric_ids[metric_spec]
125
- metric_spec_for_transport = id ? nil : metric_spec
125
+ def add_data_to_send_unless_empty(data, stats, metric_spec, id)
126
+ # don't bother collecting and reporting stats that have
127
+ # zero-values for this timeslice. significant
128
+ # performance boost and storage savings.
129
+ return if stats.is_reset?
130
+ data[metric_spec] = NewRelic::MetricData.new((id ? nil : metric_spec), stats, id)
131
+ end
126
132
 
127
- metric_data = NewRelic::MetricData.new(metric_spec_for_transport, stats_copy, id)
133
+ def merge_stats(other_engine_or_hash, metric_ids)
134
+ old_data = get_stats_hash_from(other_engine_or_hash)
128
135
 
129
- timeslice_data[metric_spec] = metric_data
130
- end
131
- end
136
+ timeslice_data = {}
137
+ stats_hash.each do | metric_spec, stats |
132
138
 
139
+ metric_spec = coerce_to_metric_spec(metric_spec)
140
+ stats_copy = clone_and_reset_stats(metric_spec, stats)
141
+ merge_old_data!(metric_spec, stats_copy, old_data)
142
+ add_data_to_send_unless_empty(timeslice_data, stats_copy, metric_spec, metric_ids[metric_spec])
143
+ end
133
144
  timeslice_data
145
+ end
146
+
147
+ end
148
+ include Harvest
149
+
150
+ # Harvest the timeslice data. First recombine current statss
151
+ # with any previously
152
+ # unsent metrics, clear out stats cache, and return the current
153
+ # stats.
154
+ # ---
155
+ # Note: this is not synchronized. There is still some risk in this and
156
+ # we will revisit later to see if we can make this more robust without
157
+ # sacrificing efficiency.
158
+ # +++
159
+ def harvest_timeslice_data(previous_timeslice_data, metric_ids)
160
+
161
+ poll harvest_samplers
162
+ merge_stats(previous_timeslice_data, metric_ids)
134
163
  end
135
164
 
136
165
  # Remove all stats. For test code only.