newrelic_rpm 3.6.2.96 → 3.6.3.103.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGELOG +33 -1
  3. data/README.md +7 -7
  4. data/lib/new_relic/agent/agent.rb +51 -22
  5. data/lib/new_relic/agent/agent_logger.rb +22 -11
  6. data/lib/new_relic/agent/configuration/defaults.rb +6 -1
  7. data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +1 -6
  8. data/lib/new_relic/agent/instrumentation/action_view_subscriber.rb +1 -1
  9. data/lib/new_relic/agent/instrumentation/active_record.rb +8 -48
  10. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +9 -1
  11. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +4 -3
  12. data/lib/new_relic/agent/instrumentation/data_mapper.rb +4 -4
  13. data/lib/new_relic/agent/instrumentation/metric_frame.rb +10 -8
  14. data/lib/new_relic/agent/instrumentation/padrino.rb +32 -0
  15. data/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb +52 -0
  16. data/lib/new_relic/agent/instrumentation/sinatra/transaction_namer.rb +56 -0
  17. data/lib/new_relic/agent/instrumentation/sinatra.rb +113 -48
  18. data/lib/new_relic/agent/new_relic_service.rb +6 -0
  19. data/lib/new_relic/agent/pipe_channel_manager.rb +13 -8
  20. data/lib/new_relic/agent/request_sampler.rb +205 -0
  21. data/lib/new_relic/agent/sampler.rb +0 -1
  22. data/lib/new_relic/agent/stats_engine/samplers.rb +0 -1
  23. data/lib/new_relic/agent/stats_engine/transactions.rb +12 -16
  24. data/lib/new_relic/agent/transaction.rb +27 -4
  25. data/lib/new_relic/agent/transaction_sample_builder.rb +47 -6
  26. data/lib/new_relic/agent/transaction_sampler.rb +0 -5
  27. data/lib/new_relic/agent.rb +17 -0
  28. data/lib/new_relic/build.rb +2 -2
  29. data/lib/new_relic/coerce.rb +3 -1
  30. data/lib/new_relic/rack/agent_hooks.rb +17 -3
  31. data/lib/new_relic/rack/browser_monitoring.rb +8 -3
  32. data/lib/new_relic/rack/error_collector.rb +2 -0
  33. data/lib/new_relic/transaction_sample/segment.rb +0 -23
  34. data/lib/new_relic/transaction_sample.rb +0 -9
  35. data/lib/new_relic/version.rb +1 -1
  36. data/test/agent_helper.rb +204 -0
  37. data/test/config/newrelic.yml +0 -1
  38. data/test/config/test_control.rb +3 -1
  39. data/test/multiverse/suites/agent_only/key_transactions_test.rb +8 -5
  40. data/test/multiverse/suites/agent_only/logging_test.rb +1 -1
  41. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +7 -8
  42. data/test/multiverse/suites/datamapper/Envfile +7 -0
  43. data/test/multiverse/suites/datamapper/datamapper_test.rb +105 -0
  44. data/test/multiverse/suites/padrino/Envfile +16 -0
  45. data/test/multiverse/suites/padrino/config/newrelic.yml +24 -0
  46. data/test/multiverse/suites/padrino/padrino_test.rb +54 -0
  47. data/test/multiverse/suites/rails/Envfile +5 -5
  48. data/test/multiverse/suites/rails/app.rb +1 -0
  49. data/test/multiverse/suites/rails/request_statistics_test.rb +118 -0
  50. data/test/multiverse/suites/sinatra/Envfile +8 -2
  51. data/test/multiverse/suites/sinatra/ignoring_test.rb +185 -0
  52. data/test/multiverse/suites/sinatra/sinatra_classic_test.rb +92 -0
  53. data/test/multiverse/suites/sinatra/sinatra_metric_explosion_test.rb +0 -3
  54. data/test/multiverse/suites/sinatra/sinatra_modular_test.rb +89 -0
  55. data/test/multiverse/suites/sinatra/sinatra_test_cases.rb +120 -0
  56. data/test/new_relic/agent/agent_logger_test.rb +149 -56
  57. data/test/new_relic/agent/agent_test.rb +23 -0
  58. data/test/new_relic/agent/agent_test_controller_test.rb +8 -1
  59. data/test/new_relic/agent/autostart_test.rb +10 -6
  60. data/test/new_relic/agent/instrumentation/action_controller_subscriber_test.rb +36 -31
  61. data/test/new_relic/agent/instrumentation/action_view_subscriber_test.rb +7 -0
  62. data/test/new_relic/agent/instrumentation/active_record_helper_test.rb +20 -4
  63. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +20 -9
  64. data/test/new_relic/agent/instrumentation/active_record_subscriber_test.rb +23 -19
  65. data/test/new_relic/agent/instrumentation/sequel_test.rb +118 -79
  66. data/test/new_relic/agent/instrumentation/sinatra/transaction_namer_test.rb +55 -0
  67. data/test/new_relic/agent/instrumentation/sinatra_test.rb +60 -11
  68. data/test/new_relic/agent/method_tracer_test.rb +7 -4
  69. data/test/new_relic/agent/new_relic_service_test.rb +6 -0
  70. data/test/new_relic/agent/pipe_channel_manager_test.rb +6 -2
  71. data/test/new_relic/agent/request_sampler_test.rb +159 -0
  72. data/test/new_relic/agent/stats_engine/samplers_test.rb +1 -5
  73. data/test/new_relic/agent/stats_engine_test.rb +14 -0
  74. data/test/new_relic/agent/transaction_sample_builder_test.rb +43 -6
  75. data/test/new_relic/agent/transaction_sampler_test.rb +31 -1
  76. data/test/new_relic/agent/transaction_test.rb +29 -0
  77. data/test/new_relic/agent_test.rb +7 -0
  78. data/test/new_relic/coerce_test.rb +13 -0
  79. data/test/new_relic/fake_collector.rb +31 -1
  80. data/test/new_relic/metric_spec_test.rb +14 -10
  81. data/test/new_relic/rack/agent_hooks_test.rb +9 -2
  82. data/test/new_relic/rack/browser_monitoring_test.rb +16 -7
  83. data/test/new_relic/rack/developer_mode_test.rb +7 -0
  84. data/test/new_relic/rack/error_collector_test.rb +10 -6
  85. data/test/new_relic/transaction_sample/segment_test.rb +0 -61
  86. data/test/new_relic/transaction_sample_subtest_test.rb +0 -19
  87. data/test/script/ci.sh +14 -0
  88. data/test/test_helper.rb +79 -203
  89. data.tar.gz.sig +0 -0
  90. metadata +50 -18
  91. metadata.gz.sig +0 -0
  92. data/test/multiverse/suites/datamapper/encoding_test.rb +0 -40
  93. data/test/multiverse/suites/sinatra/sinatra_test.rb +0 -143
@@ -0,0 +1,205 @@
1
+ # -*- ruby -*-
2
+ # encoding: utf-8
3
+ # This file is distributed under New Relic's license terms.
4
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
5
+
6
+ require 'monitor'
7
+
8
+ require 'newrelic_rpm' unless defined?( NewRelic )
9
+ require 'new_relic/agent' unless defined?( NewRelic::Agent )
10
+
11
+ class NewRelic::Agent::RequestSampler
12
+ include NewRelic::Coerce,
13
+ MonitorMixin
14
+
15
+ # The amount of time between samples, in milliseconds
16
+ DEFAULT_SAMPLE_RATE_MS = 50
17
+
18
+ # The minimum amount of time between samples, in milliseconds
19
+ MIN_SAMPLE_RATE_MS = 25
20
+
21
+ # The number of seconds between harvests
22
+ # :TODO: Get this from the agent instead?
23
+ DEFAULT_REPORT_FREQUENCY = 60
24
+
25
+ # The namespace and keys of config values
26
+ CONFIG_NAMESPACE = 'request_sampler'
27
+ SAMPLE_RATE_KEY = "#{CONFIG_NAMESPACE}.sample_rate_ms".to_sym
28
+ ENABLED_KEY = "#{CONFIG_NAMESPACE}.enabled".to_sym
29
+
30
+ # The type field of the sample
31
+ SAMPLE_TYPE = 'Transaction'
32
+
33
+ # Strings for static keys of the sample structure
34
+ TYPE_KEY = 'type'
35
+ TIMESTAMP_KEY = 'timestamp'
36
+ NAME_KEY = 'name'
37
+ DURATION_KEY = 'duration'
38
+
39
+
40
+ # Create a new RequestSampler that will keep samples added to it every
41
+ # +sample_rate_ms+ milliseconds.
42
+ def initialize( event_listener )
43
+ super()
44
+
45
+ @enabled = false
46
+ @sample_rate_ms = DEFAULT_SAMPLE_RATE_MS
47
+ @normal_sample_rate_ms = @sample_rate_ms
48
+ @last_sample_taken = nil
49
+ @last_harvest = nil
50
+ @samples = []
51
+
52
+ event_listener.subscribe( :transaction_finished, &method(:on_transaction_finished) )
53
+ self.register_config_callbacks
54
+ end
55
+
56
+
57
+ ######
58
+ public
59
+ ######
60
+
61
+ # The sample rate, in milliseconds between samples, that the sampler uses
62
+ # under normal circumstances
63
+ attr_reader :normal_sample_rate_ms
64
+
65
+ # The current sample rate, which may be different from the #normal_sample_rate_ms
66
+ # if the sampler is throttled.
67
+ attr_reader :sample_rate_ms
68
+
69
+ # The Time when the last sample was kept
70
+ attr_reader :last_sample_taken
71
+
72
+
73
+ ### Fetch a copy of the sampler's gathered samples. (Synchronized)
74
+ def samples
75
+ return self.synchronize { @samples.dup }
76
+ end
77
+
78
+
79
+ # Clear any existing samples and reset the last sample time. (Synchronized)
80
+ def reset
81
+ NewRelic::Agent.logger.debug "Resetting RequestSampler"
82
+
83
+ self.synchronize do
84
+ @samples.clear
85
+ @sample_rate_ms = @normal_sample_rate_ms
86
+ @last_sample_taken = Time.now
87
+ end
88
+ end
89
+
90
+
91
+ #
92
+ # :group: Event handlers
93
+ #
94
+
95
+ def register_config_callbacks
96
+ NewRelic::Agent.config.register_callback(SAMPLE_RATE_KEY) do |rate_ms|
97
+ NewRelic::Agent.logger.debug "RequestSampler sample rate to %dms" % [ rate_ms ]
98
+
99
+ if rate_ms < MIN_SAMPLE_RATE_MS
100
+ NewRelic::Agent.logger.warn " limiting RequestSampler frequency to %dms (was %dms)" %
101
+ [ MIN_SAMPLE_RATE_MS, rate_ms ]
102
+ rate_ms = MIN_SAMPLE_RATE_MS
103
+ end
104
+
105
+ @normal_sample_rate_ms = rate_ms
106
+ self.reset
107
+ end
108
+
109
+ NewRelic::Agent.config.register_callback(ENABLED_KEY) do |enabled|
110
+ NewRelic::Agent.logger.info "%sabling the Request Sampler." % [ enabled ? 'En' : 'Dis' ]
111
+ @enabled = enabled
112
+ end
113
+ end
114
+
115
+
116
+ # Event handler for the :transaction_finished event.
117
+ def on_transaction_finished( metric, duration, options={} )
118
+ return unless @enabled
119
+ self << {
120
+ NAME_KEY => string(metric),
121
+ DURATION_KEY => float(duration)
122
+ }.merge(options)
123
+ end
124
+
125
+
126
+
127
+ #
128
+ # :group: Sample API
129
+ # These methods are synchronized.
130
+ #
131
+
132
+ # Add a datapoint to the sampler if a sample is due. The +sample+ should be
133
+ # of the form:
134
+ #
135
+ # {
136
+ # 'name' => '<transaction/metric name>',
137
+ # 'duration' => <duration in seconds as a Float>,
138
+ # }
139
+ #
140
+ # This method is synchronized.
141
+ def <<( sample )
142
+ self.synchronize do
143
+ self.add_sample( sample ) if should_sample?
144
+ end
145
+
146
+ return self
147
+ end
148
+
149
+
150
+ # Downsample the gathered data and reduce the sampling rate to conserve memory. The amount
151
+ # the sampler is throttled is proportional to +resolution+, which defaults to the number of
152
+ # normal report periods which have elapsed. E.g., if three sessions with the agent have failed,
153
+ # the sampler downsamples its data to include one out of even three samples, and only samples
154
+ # a third of the time it normally would.
155
+ #
156
+ # This method is synchronized.
157
+ def throttle( resolution=nil )
158
+
159
+ # Only throttle if the sampler was running
160
+ self.synchronize do
161
+ if @last_sample_taken && !@samples.empty?
162
+ resolution ||= (Time.now - @last_sample_taken) / DEFAULT_REPORT_FREQUENCY
163
+ @sample_rate_ms = @normal_sample_rate_ms * resolution
164
+ self.downsample_data( resolution )
165
+ end
166
+ end
167
+
168
+ if resolution
169
+ NewRelic::Agent.logger.debug " resolution is now: %d -> 1 sample every %dms" %
170
+ [ resolution, @sample_rate_ms ]
171
+ end
172
+ end
173
+
174
+
175
+ #########
176
+ protected
177
+ #########
178
+
179
+ # Returns +true+ if a sample added now should be kept based on the sample
180
+ # frequency.
181
+ def should_sample?
182
+ return false unless @last_sample_taken
183
+ return ((Time.now - @last_sample_taken) * 1000).ceil >= @sample_rate_ms
184
+ end
185
+
186
+
187
+ # Add the given +sample+ to the sampler (unconditionally).
188
+ def add_sample( sample )
189
+ @last_sample_taken = Time.now
190
+
191
+ sample[TYPE_KEY] = SAMPLE_TYPE
192
+ sample[TIMESTAMP_KEY] = @last_sample_taken
193
+
194
+ @samples << sample
195
+ end
196
+
197
+
198
+ # Downsample the current data to match the specified +resolution+.
199
+ def downsample_data( resolution )
200
+ goalsize = @samples.length * ( (resolution - 1) / resolution.to_f )
201
+ 0.step( goalsize.ceil, resolution - 1 ) {|i| @samples.slice!(i+1) }
202
+ end
203
+
204
+ end # class NewRelic::Agent::RequestSampler
205
+
@@ -17,7 +17,6 @@ module NewRelic
17
17
  # Exception denotes a sampler is not available and it will not be registered.
18
18
  class Unsupported < StandardError; end
19
19
 
20
- attr_accessor :stats_engine
21
20
  attr_reader :id
22
21
  @sampler_classes = []
23
22
 
@@ -77,7 +77,6 @@ module Agent
77
77
  NewRelic::Agent.logger.warn "Ignoring addition of #{sampler.inspect} because it is already registered."
78
78
  else
79
79
  sampler_array << sampler
80
- sampler.stats_engine = self
81
80
  end
82
81
  end
83
82
 
@@ -34,19 +34,6 @@ module Agent
34
34
  def pop_scope(*args); end
35
35
  end
36
36
 
37
- attr_reader :transaction_sampler
38
-
39
- # add a new transaction sampler, unless we're currently in a
40
- # transaction (then we fail)
41
- def transaction_sampler= sampler
42
- fail "Can't add a scope listener midflight in a transaction" if scope_stack.any?
43
- @transaction_sampler = sampler
44
- end
45
-
46
- # removes a transaction sampler
47
- def remove_transaction_sampler(l)
48
- @transaction_sampler = nil
49
- end
50
37
 
51
38
  # Pushes a scope onto the transaction stack - this generates a
52
39
  # TransactionSample::Segment at the end of transaction execution
@@ -56,7 +43,7 @@ module Agent
56
43
  # identify this scope if the stack gets corrupted.
57
44
  def push_scope(tag, time = Time.now.to_f, deduct_call_time_from_parent = true)
58
45
  stack = scope_stack
59
- @transaction_sampler.notice_push_scope(time) if sampler_enabled?
46
+ transaction_sampler.notice_push_scope(time) if sampler_enabled?
60
47
  scope = ScopeStackElement.new(tag, time, deduct_call_time_from_parent)
61
48
  stack.push scope
62
49
  scope
@@ -80,13 +67,22 @@ module Agent
80
67
  stack.last.children_time += scope.children_time
81
68
  end
82
69
  end
83
- @transaction_sampler.notice_pop_scope(name, time) if sampler_enabled?
70
+ transaction_sampler.notice_pop_scope(name, time) if sampler_enabled?
84
71
  scope.name = name
85
72
  scope
86
73
  end
87
74
 
88
75
  def sampler_enabled?
89
- @transaction_sampler && Agent.config[:'transaction_tracer.enabled']
76
+ Agent.config[:'transaction_tracer.enabled'] || Agent.config[:developer_mode]
77
+ end
78
+
79
+ def transaction_sampler
80
+ Agent.instance.transaction_sampler
81
+ end
82
+
83
+ # deprecated--used to add transaction sampler, now we always look to the agent
84
+ def transaction_sampler= sampler
85
+ NewRelic::Agent.logger.warn("NewRelic::Agent::StatsEngine#transaction_sampler is deprecated")
90
86
  end
91
87
 
92
88
  # set the name of the transaction for the current thread, which will be used
@@ -43,9 +43,9 @@ module NewRelic
43
43
  return txn
44
44
  end
45
45
 
46
- def self.stop(metric_name=nil)
46
+ def self.stop(metric_name=nil, end_time=Time.now)
47
47
  txn = self.stack.pop
48
- txn.stop(metric_name) if txn
48
+ txn.stop(metric_name, end_time) if txn
49
49
  return txn
50
50
  end
51
51
 
@@ -105,7 +105,7 @@ module NewRelic
105
105
  if !@name_frozen
106
106
  @name = name
107
107
  else
108
- NewRelic::Agent.logger.warn("Attempted to rename transaction to #{name} after transaction name was already frozen.")
108
+ NewRelic::Agent.logger.warn("Attempted to rename transaction to '#{name}' after transaction name was already frozen as '#{@name}'.")
109
109
  end
110
110
  end
111
111
 
@@ -157,9 +157,10 @@ module NewRelic
157
157
  transaction_sampler.ignore_transaction
158
158
  end
159
159
 
160
+
160
161
  # Unwind one stack level. It knows if it's back at the outermost caller and
161
162
  # does the appropriate wrapup of the context.
162
- def stop(metric=::NewRelic::Agent::UNKNOWN_METRIC)
163
+ def stop(metric=::NewRelic::Agent::UNKNOWN_METRIC, end_time=Time.now)
163
164
  @name ||= metric unless name_frozen?
164
165
  freeze_name
165
166
  log_underflow if @type.nil?
@@ -175,6 +176,7 @@ module NewRelic
175
176
  end
176
177
  @transaction_trace = transaction_sampler.notice_scope_empty(self, Time.now, gc_time)
177
178
  sql_sampler.notice_scope_empty(@name)
179
+ overview_metrics = transaction_overview_metrics
178
180
  end
179
181
 
180
182
  record_exceptions
@@ -183,6 +185,7 @@ module NewRelic
183
185
  # these tear everything down so need to be done
184
186
  # after the pop
185
187
  if self.class.stack.empty?
188
+ agent.events.notify(:transaction_finished, @name, end_time.to_f - start_time.to_f, overview_metrics)
186
189
  agent.stats_engine.end_transaction
187
190
  end
188
191
  end
@@ -194,6 +197,24 @@ module NewRelic
194
197
  end
195
198
  end
196
199
 
200
+ OVERVIEW_SPECS = [
201
+ [:web_duration, MetricSpec.new('HttpDispatcher')],
202
+ [:queue_duration, MetricSpec.new('WebFrontend/QueueTime')],
203
+ [:external_duration, MetricSpec.new('External/allWeb')],
204
+ [:database_duration, MetricSpec.new('ActiveRecord/all')],
205
+ [:gc_cumulative, MetricSpec.new("GC/cumulative")],
206
+ [:memcache_duration, MetricSpec.new('Memcache/allWeb')]
207
+ ]
208
+
209
+ def transaction_overview_metrics
210
+ metrics = {}
211
+ stats = agent.stats_engine.transaction_stats_hash
212
+ OVERVIEW_SPECS.each do |(dest_key, spec)|
213
+ metrics[dest_key] = stats[spec].total_call_time if stats.key?(spec)
214
+ end
215
+ metrics
216
+ end
217
+
197
218
  # If we have an active transaction, notice the error and increment the error metric.
198
219
  # Options:
199
220
  # * <tt>:request</tt> => Request object to get the uri and referer
@@ -299,6 +320,8 @@ module NewRelic
299
320
  user_attributes.merge!(attributes)
300
321
  end
301
322
 
323
+ # Returns truthy if the current in-progress transaction is considered a
324
+ # a web transaction (as opposed to, e.g., a background transaction).
302
325
  def self.recording_web_transaction?
303
326
  self.current && self.current.recording_web_transaction?
304
327
  end
@@ -11,7 +11,36 @@ module NewRelic
11
11
  # a builder is created with every sampled transaction, to dynamically
12
12
  # generate the sampled data. It is a thread-local object, and is not
13
13
  # accessed by any other thread so no need for synchronization.
14
+ #
15
+ # @api private
14
16
  class TransactionSampleBuilder
17
+
18
+ # Once we hit the TT segment limit, we use this class to hold our place in
19
+ # the tree so that we can still get accurate names and times on the
20
+ # segments we've already created. The placeholder segment keeps a
21
+ # depth counter that's incremented on each segment entry, and decremented
22
+ # on exit, until it reaches zero, when we throw the placeholder away.
23
+ # There should only ever be zero or one placeholder segment at a time.
24
+ #
25
+ # @api private
26
+ class PlaceholderSegment
27
+ attr_reader :parent_segment
28
+ attr_accessor :depth
29
+
30
+ def initialize(parent_segment)
31
+ @parent_segment = parent_segment
32
+ @depth = 1
33
+ end
34
+
35
+ # No-op - some clients expect to be able to use this to attach params to
36
+ # TT segments.
37
+ def []=(key, value); end
38
+
39
+ # No-op - some clients expect to be able to use this to read params from
40
+ # TT segments.
41
+ def [](key); end
42
+ end
43
+
15
44
  attr_reader :current_segment, :sample
16
45
 
17
46
  include NewRelic::CollectionHelper
@@ -39,22 +68,34 @@ module NewRelic
39
68
  end
40
69
 
41
70
  def trace_entry(time)
42
- if @sample.count_segments < segment_limit()
71
+ if @sample.count_segments < segment_limit
43
72
  segment = @sample.create_segment(time.to_f - @sample_start)
44
73
  @current_segment.add_called_segment(segment)
45
74
  @current_segment = segment
46
75
  if @sample.count_segments == segment_limit()
47
76
  ::NewRelic::Agent.logger.debug("Segment limit of #{segment_limit} reached, ceasing collection.")
48
77
  end
49
- @current_segment
78
+ else
79
+ if @current_segment.is_a?(PlaceholderSegment)
80
+ @current_segment.depth += 1
81
+ else
82
+ @current_segment = PlaceholderSegment.new(@current_segment)
83
+ end
50
84
  end
85
+ @current_segment
51
86
  end
52
87
 
53
88
  def trace_exit(metric_name, time)
54
- return unless @sample.count_segments < segment_limit()
55
- @current_segment.metric_name = metric_name
56
- @current_segment.end_trace(time.to_f - @sample_start)
57
- @current_segment = @current_segment.parent_segment
89
+ if @current_segment.is_a?(PlaceholderSegment)
90
+ @current_segment.depth -= 1
91
+ if @current_segment.depth == 0
92
+ @current_segment = @current_segment.parent_segment
93
+ end
94
+ else
95
+ @current_segment.metric_name = metric_name
96
+ @current_segment.end_trace(time.to_f - @sample_start)
97
+ @current_segment = @current_segment.parent_segment
98
+ end
58
99
  end
59
100
 
60
101
  def finish_trace(time=Time.now.to_f, custom_params={})
@@ -400,12 +400,7 @@ module NewRelic
400
400
  end
401
401
 
402
402
  # Clamp the number of TTs we'll keep in memory and send
403
- #
404
403
  result = clamp_number_tts(result, 20) if result.length > 20
405
-
406
- # Truncate the samples at 2100 segments. The UI will clamp them at 2000 segments anyway.
407
- # This will save us memory and bandwidth.
408
- result.each { |sample| sample.truncate(Agent.config[:'transaction_tracer.limit_segments']) }
409
404
  result
410
405
  end
411
406