newrelic_rpm 2.8.0

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 (107) hide show
  1. data/LICENSE +37 -0
  2. data/README +93 -0
  3. data/Rakefile +38 -0
  4. data/install.rb +37 -0
  5. data/lib/new_relic/agent.rb +26 -0
  6. data/lib/new_relic/agent/agent.rb +762 -0
  7. data/lib/new_relic/agent/chained_call.rb +13 -0
  8. data/lib/new_relic/agent/collection_helper.rb +81 -0
  9. data/lib/new_relic/agent/error_collector.rb +105 -0
  10. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +95 -0
  11. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +151 -0
  12. data/lib/new_relic/agent/instrumentation/data_mapper.rb +90 -0
  13. data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +105 -0
  14. data/lib/new_relic/agent/instrumentation/memcache.rb +18 -0
  15. data/lib/new_relic/agent/instrumentation/merb/controller.rb +17 -0
  16. data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +15 -0
  17. data/lib/new_relic/agent/instrumentation/merb/errors.rb +6 -0
  18. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +35 -0
  19. data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +27 -0
  20. data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +30 -0
  21. data/lib/new_relic/agent/instrumentation/rails/errors.rb +23 -0
  22. data/lib/new_relic/agent/instrumentation/rails/rails.rb +6 -0
  23. data/lib/new_relic/agent/method_tracer.rb +171 -0
  24. data/lib/new_relic/agent/patch_const_missing.rb +31 -0
  25. data/lib/new_relic/agent/samplers/cpu.rb +29 -0
  26. data/lib/new_relic/agent/samplers/memory.rb +55 -0
  27. data/lib/new_relic/agent/samplers/mongrel.rb +26 -0
  28. data/lib/new_relic/agent/stats_engine.rb +241 -0
  29. data/lib/new_relic/agent/synchronize.rb +40 -0
  30. data/lib/new_relic/agent/transaction_sampler.rb +281 -0
  31. data/lib/new_relic/agent/worker_loop.rb +128 -0
  32. data/lib/new_relic/api/deployments.rb +92 -0
  33. data/lib/new_relic/config.rb +194 -0
  34. data/lib/new_relic/config/merb.rb +35 -0
  35. data/lib/new_relic/config/rails.rb +113 -0
  36. data/lib/new_relic/config/ruby.rb +9 -0
  37. data/lib/new_relic/local_environment.rb +108 -0
  38. data/lib/new_relic/merbtasks.rb +6 -0
  39. data/lib/new_relic/metric_data.rb +26 -0
  40. data/lib/new_relic/metric_spec.rb +39 -0
  41. data/lib/new_relic/metrics.rb +7 -0
  42. data/lib/new_relic/noticed_error.rb +21 -0
  43. data/lib/new_relic/shim_agent.rb +95 -0
  44. data/lib/new_relic/stats.rb +359 -0
  45. data/lib/new_relic/transaction_analysis.rb +122 -0
  46. data/lib/new_relic/transaction_sample.rb +499 -0
  47. data/lib/new_relic/version.rb +111 -0
  48. data/lib/new_relic_api.rb +275 -0
  49. data/lib/newrelic_rpm.rb +27 -0
  50. data/lib/tasks/agent_tests.rake +14 -0
  51. data/lib/tasks/all.rb +4 -0
  52. data/lib/tasks/install.rake +7 -0
  53. data/newrelic.yml +137 -0
  54. data/recipes/newrelic.rb +46 -0
  55. data/test/config/newrelic.yml +26 -0
  56. data/test/config/test_config.rb +9 -0
  57. data/test/new_relic/agent/mock_ar_connection.rb +40 -0
  58. data/test/new_relic/agent/mock_scope_listener.rb +23 -0
  59. data/test/new_relic/agent/model_fixture.rb +17 -0
  60. data/test/new_relic/agent/tc_active_record.rb +91 -0
  61. data/test/new_relic/agent/tc_agent.rb +112 -0
  62. data/test/new_relic/agent/tc_collection_helper.rb +104 -0
  63. data/test/new_relic/agent/tc_controller.rb +98 -0
  64. data/test/new_relic/agent/tc_dispatcher_instrumentation.rb +52 -0
  65. data/test/new_relic/agent/tc_error_collector.rb +127 -0
  66. data/test/new_relic/agent/tc_method_tracer.rb +306 -0
  67. data/test/new_relic/agent/tc_stats_engine.rb +218 -0
  68. data/test/new_relic/agent/tc_synchronize.rb +37 -0
  69. data/test/new_relic/agent/tc_transaction_sample.rb +175 -0
  70. data/test/new_relic/agent/tc_transaction_sample_builder.rb +200 -0
  71. data/test/new_relic/agent/tc_transaction_sampler.rb +305 -0
  72. data/test/new_relic/agent/tc_worker_loop.rb +101 -0
  73. data/test/new_relic/agent/testable_agent.rb +13 -0
  74. data/test/new_relic/tc_config.rb +36 -0
  75. data/test/new_relic/tc_deployments_api.rb +37 -0
  76. data/test/new_relic/tc_environment.rb +94 -0
  77. data/test/new_relic/tc_metric_spec.rb +150 -0
  78. data/test/new_relic/tc_shim_agent.rb +9 -0
  79. data/test/new_relic/tc_stats.rb +141 -0
  80. data/test/test_helper.rb +39 -0
  81. data/test/ui/tc_newrelic_helper.rb +44 -0
  82. data/ui/controllers/newrelic_controller.rb +200 -0
  83. data/ui/helpers/google_pie_chart.rb +55 -0
  84. data/ui/helpers/newrelic_helper.rb +286 -0
  85. data/ui/views/layouts/newrelic_default.rhtml +49 -0
  86. data/ui/views/newrelic/_explain_plans.rhtml +27 -0
  87. data/ui/views/newrelic/_sample.rhtml +12 -0
  88. data/ui/views/newrelic/_segment.rhtml +28 -0
  89. data/ui/views/newrelic/_segment_row.rhtml +14 -0
  90. data/ui/views/newrelic/_show_sample_detail.rhtml +22 -0
  91. data/ui/views/newrelic/_show_sample_sql.rhtml +19 -0
  92. data/ui/views/newrelic/_show_sample_summary.rhtml +3 -0
  93. data/ui/views/newrelic/_sql_row.rhtml +11 -0
  94. data/ui/views/newrelic/_stack_trace.rhtml +30 -0
  95. data/ui/views/newrelic/_table.rhtml +12 -0
  96. data/ui/views/newrelic/explain_sql.rhtml +45 -0
  97. data/ui/views/newrelic/images/arrow-close.png +0 -0
  98. data/ui/views/newrelic/images/arrow-open.png +0 -0
  99. data/ui/views/newrelic/images/blue_bar.gif +0 -0
  100. data/ui/views/newrelic/images/gray_bar.gif +0 -0
  101. data/ui/views/newrelic/index.rhtml +37 -0
  102. data/ui/views/newrelic/javascript/transaction_sample.js +107 -0
  103. data/ui/views/newrelic/sample_not_found.rhtml +2 -0
  104. data/ui/views/newrelic/show_sample.rhtml +62 -0
  105. data/ui/views/newrelic/show_source.rhtml +3 -0
  106. data/ui/views/newrelic/stylesheets/style.css +394 -0
  107. metadata +180 -0
@@ -0,0 +1,122 @@
1
+ # Add these methods to TransactionSample that enable performance analysis in the user interface.
2
+ module NewRelic::TransactionAnalysis
3
+ def database_time
4
+ time_percentage(/^Database\/.*/)
5
+ end
6
+
7
+ def render_time
8
+ time_percentage(/^View\/.*/)
9
+ end
10
+
11
+ # summarizes performance data for all calls to segments
12
+ # with the same metric_name
13
+ class SegmentSummary
14
+ attr_accessor :metric_name, :total_time, :exclusive_time, :call_count
15
+ def initialize(metric_name, sample)
16
+ @metric_name = metric_name
17
+ @total_time, @exclusive_time, @call_count = 0,0,0
18
+ @sample = sample
19
+ end
20
+
21
+ def <<(segment)
22
+ if metric_name != segment.metric_name
23
+ raise ArgumentError, "Metric Name Mismatch: #{segment.metric_name} != #{metric_name}"
24
+ end
25
+
26
+ @total_time += segment.duration
27
+ @exclusive_time += segment.exclusive_duration
28
+ @call_count += 1
29
+ end
30
+
31
+ def average_time
32
+ @total_time / @call_count
33
+ end
34
+
35
+ def average_exclusive_time
36
+ @exclusive_time / @call_count
37
+ end
38
+
39
+ def exclusive_time_percentage
40
+ @exclusive_time / @sample.duration
41
+ end
42
+
43
+ def total_time_percentage
44
+ @total_time / @sample.duration
45
+ end
46
+
47
+ def developer_name
48
+ return @metric_name if @metric_name == 'Remainder'
49
+ # This drags all of the metric parser into the agent which I would prefer
50
+ # not to do. We could do a webservice that centralizes this but that might
51
+ # be expensive and not well received.
52
+ # MetricParser.parse(@metric_name).developer_name
53
+ @metric_name
54
+ end
55
+ end
56
+
57
+ # return the data that breaks down the performance of the transaction
58
+ # as an array of SegmentSummary objects. If a limit is specified, then
59
+ # limit the data set to the top n
60
+ def breakdown_data(limit = nil)
61
+ metric_hash = {}
62
+ each_segment do |segment|
63
+ unless segment == root_segment
64
+ metric_name = segment.metric_name
65
+ metric_hash[metric_name] ||= SegmentSummary.new(metric_name, self)
66
+ metric_hash[metric_name] << segment
67
+ end
68
+ end
69
+
70
+ data = metric_hash.values
71
+
72
+ data.sort! do |x,y|
73
+ y.exclusive_time <=> x.exclusive_time
74
+ end
75
+
76
+ if limit && data.length > limit
77
+ data = data[0..limit - 1]
78
+ end
79
+
80
+ # add one last segment for the remaining time if any
81
+ remainder = duration
82
+ data.each do |segment|
83
+ remainder -= segment.exclusive_time
84
+ end
85
+
86
+ if remainder.to_ms > 0.1
87
+ remainder_summary = SegmentSummary.new('Remainder', self)
88
+ remainder_summary.total_time = remainder_summary.exclusive_time = remainder
89
+ remainder_summary.call_count = 1
90
+ data << remainder_summary
91
+ end
92
+
93
+ data
94
+ end
95
+
96
+ # return an array of sql statements executed by this transaction
97
+ # each element in the array contains [sql, parent_segment_metric_name, duration]
98
+ def sql_segments
99
+ segments = []
100
+ each_segment do |segment|
101
+ if segment[:sql]
102
+ segments << segment
103
+ end
104
+ if segment[:sql_obfuscated]
105
+ segments << segment
106
+ end
107
+ end
108
+ segments
109
+ end
110
+
111
+ private
112
+ def time_percentage(regex)
113
+ total = 0
114
+ each_segment do |segment|
115
+ if regex =~ segment.metric_name
116
+ total += segment.duration
117
+ end
118
+ end
119
+
120
+ return (total / duration).to_percentage
121
+ end
122
+ end
@@ -0,0 +1,499 @@
1
+
2
+ module NewRelic
3
+ COLLAPSE_SEGMENTS_THRESHOLD = 2
4
+
5
+ MYSQL_EXPLAIN_COLUMNS = [
6
+ "Id",
7
+ "Select Type",
8
+ "Table",
9
+ "Type",
10
+ "Possible Keys",
11
+ "Key",
12
+ "Key Length",
13
+ "Ref",
14
+ "Rows",
15
+ "Extra"
16
+ ].freeze
17
+
18
+ class TransactionSample
19
+
20
+ include TransactionAnalysis
21
+ class Segment
22
+
23
+ class << self
24
+ @@empty_array = []
25
+ end
26
+
27
+ attr_reader :entry_timestamp
28
+ attr_reader :exit_timestamp
29
+ attr_reader :parent_segment
30
+ attr_reader :metric_name
31
+ attr_reader :segment_id
32
+
33
+ def initialize(timestamp, metric_name, segment_id)
34
+ @entry_timestamp = timestamp
35
+ @metric_name = metric_name
36
+ @segment_id = segment_id || object_id
37
+ end
38
+
39
+ def end_trace(timestamp)
40
+ @exit_timestamp = timestamp
41
+ end
42
+
43
+ def add_called_segment(s)
44
+ @called_segments ||= []
45
+ @called_segments << s
46
+ s.parent_segment = self
47
+ end
48
+
49
+ def to_s
50
+ to_debug_str(0)
51
+ end
52
+
53
+ def path_string
54
+ "#{metric_name}[#{called_segments.collect {|segment| segment.path_string }.join('')}]"
55
+ end
56
+
57
+ def to_debug_str(depth)
58
+ tab = ""
59
+ depth.times {tab << " "}
60
+
61
+ s = tab.clone
62
+ s << ">> #{metric_name}: #{@entry_timestamp.to_ms}\n"
63
+ unless params.empty?
64
+ s << "#{tab}#{tab}{\n"
65
+ params.each do |k,v|
66
+ s << "#{tab}#{tab}#{k}: #{v}\n"
67
+ end
68
+ s << "#{tab}#{tab}}\n"
69
+ end
70
+ called_segments.each do |cs|
71
+ s << cs.to_debug_str(depth + 1)
72
+ end
73
+ s << tab
74
+ s << "<< #{metric_name}: #{@exit_timestamp ? @exit_timestamp.to_ms : 'n/a'}\n"
75
+ s
76
+ end
77
+
78
+ def called_segments
79
+ @called_segments || @@empty_array
80
+ end
81
+
82
+ def freeze
83
+ params.freeze
84
+ if @called_segments
85
+ @called_segments.each do |s|
86
+ s.freeze
87
+ end
88
+ end
89
+ super
90
+ end
91
+
92
+ # return the total duration of this segment
93
+ def duration
94
+ @exit_timestamp - @entry_timestamp
95
+ end
96
+
97
+ # return the duration of this segment without
98
+ # including the time in the called segments
99
+ def exclusive_duration
100
+ d = duration
101
+
102
+ if @called_segments
103
+ @called_segments.each do |segment|
104
+ d -= segment.duration
105
+ end
106
+ end
107
+ d
108
+ end
109
+
110
+ def []=(key, value)
111
+ # only create a parameters field if a parameter is set; this will save
112
+ # bandwidth etc as most segments have no parameters
113
+ params[key] = value
114
+ end
115
+
116
+ def [](key)
117
+ params[key]
118
+ end
119
+
120
+ def params
121
+ @params ||= {}
122
+ end
123
+
124
+ # call the provided block for this segment and each
125
+ # of the called segments
126
+ def each_segment(&block)
127
+ block.call self
128
+
129
+ if @called_segments
130
+ @called_segments.each do |segment|
131
+ segment.each_segment(&block)
132
+ end
133
+ end
134
+ end
135
+
136
+ def find_segment(id)
137
+ return self if @segment_id == id
138
+ called_segments.each do |segment|
139
+ found = segment.find_segment(id)
140
+ return found if found
141
+ end
142
+ nil
143
+ end
144
+
145
+ # perform this in the runtime environment of a managed application, to explain the sql
146
+ # statement(s) executed within a segment of a transaction sample.
147
+ # returns an array of explanations (which is an array rows consisting of
148
+ # an array of strings for each column returned by the the explain query)
149
+ # Note this happens only for statements whose execution time exceeds a threshold (e.g. 500ms)
150
+ # and only within the slowest transaction in a report period, selected for shipment to RPM
151
+ def explain_sql
152
+ sql = params[:sql]
153
+ return nil if sql.nil?
154
+ statements = sql.split(";\n")
155
+ explanations = []
156
+ statements.each do |statement|
157
+ if statement.split($;, 2)[0].upcase == 'SELECT'
158
+ explain_resultset = []
159
+ begin
160
+ connection = NewRelic::TransactionSample.get_connection(params[:connection_config])
161
+ if connection
162
+ # The resultset type varies for different drivers. Only thing you can count on is
163
+ # that it implements each. Also: can't use select_rows because the native postgres
164
+ # driver doesn't know that method.
165
+ explain_resultset = connection.execute("EXPLAIN #{statement}") if connection
166
+ rows = []
167
+ # Note: we can't use map.
168
+ # Note: have to convert from native column element types to string so we can
169
+ # serialize. Esp. for postgresql.
170
+ explain_resultset.each { | row | rows << row.map(&:to_s) } # Can't use map. Suck it up.
171
+ explanations << rows
172
+ # sleep for a very short period of time in order to yield to the main thread
173
+ # this is because a remote database call will likely hang the VM
174
+ sleep 0.05
175
+ end
176
+ rescue => e
177
+ x = 1 # this is here so that code coverage knows we've entered this block
178
+ # swallow failed attempts to run an explain. One example of a failure is the
179
+ # connection for the sql statement is to a different db than the default connection
180
+ # specified in AR::Base
181
+ end
182
+ end
183
+ end
184
+
185
+ explanations
186
+ end
187
+
188
+ def obfuscated_sql
189
+ TransactionSample.obfuscate_sql(params[:sql])
190
+ end
191
+
192
+ def called_segments=(segments)
193
+ @called_segments = segments
194
+ end
195
+
196
+ protected
197
+ def parent_segment=(s)
198
+ @parent_segment = s
199
+ end
200
+ end
201
+
202
+ class SummarySegment < Segment
203
+
204
+
205
+ def initialize(segment)
206
+ super segment.entry_timestamp, segment.metric_name, nil
207
+
208
+ add_segments segment.called_segments
209
+
210
+ end_trace segment.exit_timestamp
211
+ end
212
+
213
+ def add_segments(segments)
214
+ segments.collect do |segment|
215
+ SummarySegment.new(segment)
216
+ end.each {|segment| add_called_segment(segment)}
217
+ end
218
+
219
+ end
220
+
221
+ class CompositeSegment < Segment
222
+ attr_reader :detail_segments
223
+
224
+ def initialize(segments)
225
+ summary = SummarySegment.new(segments.first)
226
+ super summary.entry_timestamp, "Repeating pattern (#{segments.length} repeats)", nil
227
+
228
+ summary.end_trace(segments.last.exit_timestamp)
229
+
230
+ @detail_segments = segments.clone
231
+
232
+ add_called_segment(summary)
233
+ end_trace summary.exit_timestamp
234
+ end
235
+
236
+ def detail_segments=(segments)
237
+ @detail_segments = segments
238
+ end
239
+
240
+ end
241
+
242
+ class << self
243
+ def obfuscate_sql(sql)
244
+ NewRelic::Agent.instance.obfuscator.call(sql)
245
+ end
246
+
247
+
248
+ def get_connection(config)
249
+ @@connections ||= {}
250
+
251
+ connection = @@connections[config]
252
+
253
+ return connection if connection
254
+
255
+ begin
256
+ connection = ActiveRecord::Base.send("#{config[:adapter]}_connection", config)
257
+ @@connections[config] = connection
258
+ rescue => e
259
+ NewRelic::Agent.agent.log.error("Caught exception #{e} trying to get connection to DB for explain. Config: #{config}")
260
+ NewRelic::Agent.agent.log.error(e.backtrace.join("\n"))
261
+ nil
262
+ end
263
+ end
264
+
265
+ def close_connections
266
+ @@connections ||= {}
267
+ @@connections.values.each do |connection|
268
+ begin
269
+ connection.disconnect!
270
+ rescue
271
+ end
272
+ end
273
+
274
+ @@connections = {}
275
+ end
276
+
277
+ end
278
+
279
+
280
+ attr_reader :root_segment
281
+ attr_reader :params
282
+ attr_reader :sample_id
283
+
284
+ def initialize(time = Time.now.to_f, sample_id = nil)
285
+ @sample_id = sample_id || object_id
286
+ @start_time = time
287
+ @root_segment = create_segment 0.0, "ROOT"
288
+ @params = {}
289
+ @params[:request_params] = {}
290
+ end
291
+
292
+ def start_time
293
+ Time.at(@start_time)
294
+ end
295
+
296
+ def path_string
297
+ @root_segment.path_string
298
+ end
299
+
300
+ def create_segment (relative_timestamp, metric_name, segment_id = nil)
301
+ raise TypeError.new("Frozen Transaction Sample") if frozen?
302
+ NewRelic::TransactionSample::Segment.new(relative_timestamp, metric_name, segment_id)
303
+ end
304
+
305
+ def freeze
306
+ @root_segment.freeze
307
+ params.freeze
308
+ super
309
+ end
310
+
311
+ def duration
312
+ root_segment.duration
313
+ end
314
+
315
+ def each_segment(&block)
316
+ @root_segment.each_segment(&block)
317
+ end
318
+
319
+ def find_segment(id)
320
+ @root_segment.find_segment(id)
321
+ end
322
+
323
+ def to_s
324
+ s = "Transaction Sample collected at #{start_time}\n"
325
+ s << " {\n"
326
+ s << " Path: #{params[:path]} \n"
327
+
328
+ params.each do |k,v|
329
+ next if k == :path
330
+ s << " #{k}: " <<
331
+ case v
332
+ when Enumerable: v.map(&:to_s).sort.join("; ")
333
+ else
334
+ v
335
+ end << "\n"
336
+ end
337
+ s << " }\n\n"
338
+ s << @root_segment.to_debug_str(0)
339
+ end
340
+
341
+ # return a new transaction sample that treats segments
342
+ # with the given regular expression in their name as if they
343
+ # were never called at all. This allows us to strip out segments
344
+ # from traces captured in development environment that would not
345
+ # normally show up in production (like Rails/Application Code Loading)
346
+ def omit_segments_with(regex)
347
+ regex = Regexp.new(regex)
348
+
349
+ sample = TransactionSample.new(@start_time, sample_id)
350
+
351
+ params.each {|k,v| sample.params[k] = v}
352
+
353
+ delta = build_segment_with_omissions(sample, 0.0, @root_segment, sample.root_segment, regex)
354
+ sample.root_segment.end_trace(@root_segment.exit_timestamp - delta)
355
+ sample.freeze
356
+ end
357
+
358
+ # return a new transaction sample that can be sent to the RPM service.
359
+ # this involves potentially one or more of the following options
360
+ # :explain_sql : run EXPLAIN on all queries whose response times equal the value for this key
361
+ # (for example :explain_sql => 2.0 would explain everything over 2 seconds. 0.0 would explain everything.)
362
+ # :keep_backtraces : keep backtraces, significantly increasing size of trace (off by default)
363
+ # :obfuscate_sql : clear sql fields of potentially sensitive values (higher overhead, better security)
364
+ def prepare_to_send(options={})
365
+ sample = TransactionSample.new(@start_time, sample_id)
366
+
367
+ sample.params.merge! self.params
368
+
369
+ begin
370
+ build_segment_for_transfer(sample, @root_segment, sample.root_segment, options)
371
+ ensure
372
+ self.class.close_connections
373
+ end
374
+
375
+ sample.root_segment.end_trace(@root_segment.exit_timestamp)
376
+ sample.freeze
377
+ end
378
+
379
+ def analyze
380
+ sample = self
381
+ original_path_string = nil
382
+ loop do
383
+ original_path_string = sample.path_string.to_s
384
+ new_sample = sample.dup
385
+ new_sample.root_segment = sample.root_segment.dup
386
+ new_sample.root_segment.called_segments = analyze_called_segments(root_segment.called_segments)
387
+ sample = new_sample
388
+ return sample if sample.path_string.to_s == original_path_string
389
+ end
390
+
391
+ end
392
+
393
+ protected
394
+ def root_segment=(segment)
395
+ @root_segment = segment
396
+ end
397
+
398
+ private
399
+
400
+ def analyze_called_segments(called_segments)
401
+ path = nil
402
+ like_segments = []
403
+
404
+ segments = []
405
+
406
+ called_segments.each do |segment|
407
+ segment = segment.dup
408
+ segment.called_segments = analyze_called_segments(segment.called_segments)
409
+
410
+ current_path = segment.path_string
411
+ if path == current_path
412
+ like_segments << segment
413
+ else
414
+ segments += summarize_segments(like_segments)
415
+
416
+ like_segments.clear
417
+ like_segments << segment
418
+ path = current_path
419
+ end
420
+ end
421
+ segments += summarize_segments(like_segments)
422
+
423
+ segments
424
+ end
425
+
426
+ def summarize_segments(like_segments)
427
+ if like_segments.length > COLLAPSE_SEGMENTS_THRESHOLD
428
+ puts "#{like_segments.first.path_string} #{like_segments.length}"
429
+ [CompositeSegment.new(like_segments)]
430
+ else
431
+ like_segments
432
+ end
433
+ end
434
+
435
+ def build_segment_with_omissions(new_sample, time_delta, source_segment, target_segment, regex)
436
+ source_segment.called_segments.each do |source_called_segment|
437
+ # if this segment's metric name matches the given regular expression, bail
438
+ # here and increase the amount of time that we reduce the target sample with
439
+ # by this omitted segment's duration.
440
+ do_omit = regex =~ source_called_segment.metric_name
441
+
442
+ if do_omit
443
+ time_delta += source_called_segment.duration
444
+ else
445
+ target_called_segment = new_sample.create_segment(
446
+ source_called_segment.entry_timestamp - time_delta,
447
+ source_called_segment.metric_name,
448
+ source_called_segment.segment_id)
449
+
450
+ target_segment.add_called_segment target_called_segment
451
+ source_called_segment.params.each do |k,v|
452
+ target_called_segment[k]=v
453
+ end
454
+
455
+ time_delta = build_segment_with_omissions(
456
+ new_sample, time_delta, source_called_segment, target_called_segment, regex)
457
+ target_called_segment.end_trace(source_called_segment.exit_timestamp - time_delta)
458
+ end
459
+ end
460
+
461
+ return time_delta
462
+ end
463
+
464
+ # see prepare_to_send for what we do with options
465
+ def build_segment_for_transfer(new_sample, source_segment, target_segment, options)
466
+ source_segment.called_segments.each do |source_called_segment|
467
+ target_called_segment = new_sample.create_segment(
468
+ source_called_segment.entry_timestamp,
469
+ source_called_segment.metric_name,
470
+ source_called_segment.segment_id)
471
+
472
+ target_segment.add_called_segment target_called_segment
473
+ source_called_segment.params.each do |k,v|
474
+ case k
475
+ when :backtrace
476
+ target_called_segment[k]=v if options[:keep_backtraces]
477
+ when :sql
478
+ sql = v
479
+
480
+ # run an EXPLAIN on this sql if specified.
481
+ if options[:explain_enabled] && options[:explain_sql] && source_called_segment.duration > options[:explain_sql].to_f
482
+ target_called_segment[:explanation] = source_called_segment.explain_sql
483
+ end
484
+
485
+ target_called_segment[:sql]=sql if options[:record_sql] == :raw
486
+ target_called_segment[:sql_obfuscated] = TransactionSample.obfuscate_sql(sql) if options[:record_sql] == :obfuscated
487
+ when :connection_config
488
+ # don't copy it
489
+ else
490
+ target_called_segment[k]=v
491
+ end
492
+ end
493
+
494
+ build_segment_for_transfer(new_sample, source_called_segment, target_called_segment, options)
495
+ target_called_segment.end_trace(source_called_segment.exit_timestamp)
496
+ end
497
+ end
498
+ end
499
+ end