newrelic_rpm 4.3.0.335 → 4.4.0.336

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,12 +2,22 @@
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
- require 'new_relic/agent/aws_info'
5
+ require 'new_relic/agent/utilization/aws'
6
+ require 'new_relic/agent/utilization/gcp'
7
+ require 'new_relic/agent/utilization/azure'
8
+ require 'new_relic/agent/utilization/pcf'
6
9
 
7
10
  module NewRelic
8
11
  module Agent
9
12
  class UtilizationData
10
- METADATA_VERSION = 2
13
+ METADATA_VERSION = 3
14
+
15
+ VENDORS = {
16
+ Utilization::AWS => :'utilization.detect_aws',
17
+ Utilization::GCP => :'utilization.detect_gcp',
18
+ Utilization::Azure => :'utilization.detect_azure',
19
+ Utilization::PCF => :'utilization.detect_pcf'
20
+ }
11
21
 
12
22
  def hostname
13
23
  NewRelic::Agent::Hostname.get
@@ -55,21 +65,24 @@ module NewRelic
55
65
  :hostname => hostname
56
66
  }
57
67
 
58
- append_aws_info(result)
68
+ append_vendor_info(result)
59
69
  append_docker_info(result)
60
70
  append_configured_values(result)
71
+ append_boot_id(result)
61
72
 
62
73
  result
63
74
  end
64
75
 
65
- def append_aws_info(collector_hash)
66
- return unless Agent.config[:'utilization.detect_aws']
67
-
68
- aws_info = AWSInfo.new
76
+ def append_vendor_info(collector_hash)
77
+ VENDORS.each_pair do |klass, config_option|
78
+ next unless Agent.config[config_option]
79
+ vendor = klass.new
69
80
 
70
- if aws_info.loaded?
71
- collector_hash[:vendors] ||= {}
72
- collector_hash[:vendors][:aws] = aws_info.to_collector_hash
81
+ if vendor.detect
82
+ collector_hash[:vendors] ||= {}
83
+ collector_hash[:vendors][vendor.vendor_name.to_sym] = vendor.metadata
84
+ break
85
+ end
73
86
  end
74
87
  end
75
88
 
@@ -86,6 +99,12 @@ module NewRelic
86
99
  collector_hash[:config] = config_hash unless config_hash.empty?
87
100
  end
88
101
 
102
+ def append_boot_id(collector_hash)
103
+ if bid = ::NewRelic::Agent::SystemInfo.boot_id
104
+ collector_hash[:boot_id] = bid
105
+ end
106
+ end
107
+
89
108
  def config_hash
90
109
  config_hash = {}
91
110
 
@@ -11,7 +11,7 @@ module NewRelic
11
11
  end
12
12
 
13
13
  MAJOR = 4
14
- MINOR = 3
14
+ MINOR = 4
15
15
  TINY = 0
16
16
 
17
17
  begin
@@ -31,7 +31,7 @@ EOS
31
31
  "newrelic.yml"
32
32
  ]
33
33
 
34
- file_list = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
34
+ file_list = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/(?!agent_helper.rb)}) }
35
35
  build_file_path = 'lib/new_relic/build.rb'
36
36
  file_list << build_file_path if File.exist?(build_file_path)
37
37
  s.files = file_list
@@ -0,0 +1,697 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
4
+
5
+ # These helpers should not have any gem dependencies except on newrelic_rpm
6
+ # itself, and should be usable from within any multiverse suite.
7
+
8
+ require 'json'
9
+
10
+ class ArrayLogDevice
11
+ def initialize( array=[] )
12
+ @array = array
13
+ end
14
+ attr_reader :array
15
+
16
+ def write( message )
17
+ @array << message
18
+ end
19
+
20
+ def close; end
21
+ end
22
+
23
+ def assert_between(floor, ceiling, value, message="expected #{floor} <= #{value} <= #{ceiling}")
24
+ assert((floor <= value && value <= ceiling), message)
25
+ end
26
+
27
+ def assert_in_delta(expected, actual, delta)
28
+ assert_between((expected - delta), (expected + delta), actual)
29
+ end
30
+
31
+ def harvest_error_traces!
32
+ NewRelic::Agent.instance.error_collector.error_trace_aggregator.harvest!
33
+ end
34
+
35
+ def reset_error_traces!
36
+ NewRelic::Agent.instance.error_collector.error_trace_aggregator.reset!
37
+ end
38
+
39
+ def assert_has_traced_error(error_class)
40
+ errors = harvest_error_traces!
41
+ assert \
42
+ errors.find {|e| e.exception_class_name == error_class.name} != nil, \
43
+ "Didn't find error of class #{error_class}"
44
+ end
45
+
46
+ def last_traced_error
47
+ harvest_error_traces!.last
48
+ end
49
+
50
+ def harvest_transaction_events!
51
+ NewRelic::Agent.instance.transaction_event_aggregator.harvest!
52
+ end
53
+
54
+ def last_transaction_event
55
+ harvest_transaction_events!.last.last
56
+ end
57
+
58
+ unless defined?( assert_block )
59
+ def assert_block(*msgs)
60
+ assert yield, *msgs
61
+ end
62
+ end
63
+
64
+ unless defined?( assert_includes )
65
+ def assert_includes( collection, member, msg=nil )
66
+ msg = "Expected #{collection.inspect} to include #{member.inspect}"
67
+ assert_block( msg ) { collection.include?(member) }
68
+ end
69
+ end
70
+
71
+ unless defined?( assert_not_includes )
72
+ def assert_not_includes( collection, member, msg=nil )
73
+ msg = "Expected #{collection.inspect} not to include #{member.inspect}"
74
+ assert !collection.include?(member), msg
75
+ end
76
+ end
77
+
78
+ unless defined?( assert_empty )
79
+ def assert_empty(collection, msg=nil)
80
+ assert collection.empty?, msg
81
+ end
82
+ end
83
+
84
+ def assert_equal_unordered(left, right)
85
+ assert_equal(left.length, right.length, "Lengths don't match. #{left.length} != #{right.length}")
86
+ left.each { |element| assert_includes(right, element) }
87
+ end
88
+
89
+ def assert_audit_log_contains(audit_log_contents, needle)
90
+ # Original request bodies dumped to the log have symbol keys, but once
91
+ # they go through a dump/load, they're strings again, so we strip
92
+ # double-quotes and colons from the log, and the strings we searching for.
93
+ regex = /[:"]/
94
+ needle = needle.gsub(regex, '')
95
+ haystack = audit_log_contents.gsub(regex, '')
96
+ assert(haystack.include?(needle), "Expected log to contain '#{needle}'")
97
+ end
98
+
99
+ # Because we don't generate a strictly machine-readable representation of
100
+ # request bodies for the audit log, the transformation into strings is
101
+ # effectively one-way. This, combined with the fact that Hash traversal order
102
+ # is arbitrary in Ruby 1.8.x means that it's difficult to directly assert that
103
+ # some object graph made it into the audit log (due to different possible
104
+ # orderings of the key/value pairs in Hashes that were embedded in the request
105
+ # body). So, this method traverses an object graph and only makes assertions
106
+ # about the terminal (non-Array-or-Hash) nodes therein.
107
+ def assert_audit_log_contains_object(audit_log_contents, o, format = :json)
108
+ case o
109
+ when Hash
110
+ o.each do |k,v|
111
+ assert_audit_log_contains_object(audit_log_contents, v, format)
112
+ assert_audit_log_contains_object(audit_log_contents, k, format)
113
+ end
114
+ when Array
115
+ o.each do |el|
116
+ assert_audit_log_contains_object(audit_log_contents, el, format)
117
+ end
118
+ when NilClass
119
+ assert_audit_log_contains(audit_log_contents, format == :json ? "null" : "nil")
120
+ else
121
+ assert_audit_log_contains(audit_log_contents, o.inspect)
122
+ end
123
+ end
124
+
125
+ def compare_metrics(expected, actual)
126
+ actual.delete_if {|a| a.include?('GC/Transaction/') }
127
+ assert_equal(expected.to_a.sort, actual.to_a.sort, "extra: #{(actual - expected).to_a.inspect}; missing: #{(expected - actual).to_a.inspect}")
128
+ end
129
+
130
+ def metric_spec_from_specish(specish)
131
+ spec = case specish
132
+ when String then NewRelic::MetricSpec.new(specish)
133
+ when Array then NewRelic::MetricSpec.new(*specish)
134
+ end
135
+ spec
136
+ end
137
+
138
+ def _normalize_metric_expectations(expectations)
139
+ case expectations
140
+ when Array
141
+ hash = {}
142
+ # Just assert that the metric is present, nothing about the attributes
143
+ expectations.each { |k| hash[k] = { } }
144
+ hash
145
+ when String
146
+ { expectations => {} }
147
+ else
148
+ expectations
149
+ end
150
+ end
151
+
152
+ def dump_stats(stats)
153
+ str = " Call count: #{stats.call_count}\n"
154
+ str << " Total call time: #{stats.total_call_time}\n"
155
+ str << " Total exclusive time: #{stats.total_exclusive_time}\n"
156
+ str << " Min call time: #{stats.min_call_time}\n"
157
+ str << " Max call time: #{stats.max_call_time}\n"
158
+ str << " Sum of squares: #{stats.sum_of_squares}\n"
159
+ str << " Apdex S: #{stats.apdex_s}\n"
160
+ str << " Apdex T: #{stats.apdex_t}\n"
161
+ str << " Apdex F: #{stats.apdex_f}\n"
162
+ str
163
+ end
164
+
165
+ def assert_stats_has_values(stats, expected_spec, expected_attrs)
166
+ expected_attrs.each do |attr, expected_value|
167
+ actual_value = stats.send(attr)
168
+ if attr == :call_count
169
+ assert_equal(expected_value, actual_value,
170
+ "Expected #{attr} for #{expected_spec} to be #{expected_value}, got #{actual_value}.\nActual stats:\n#{dump_stats(stats)}")
171
+ else
172
+ assert_in_delta(expected_value, actual_value, 0.0001,
173
+ "Expected #{attr} for #{expected_spec} to be ~#{expected_value}, got #{actual_value}.\nActual stats:\n#{dump_stats(stats)}")
174
+ end
175
+ end
176
+ end
177
+
178
+ def assert_metrics_recorded(expected)
179
+ expected = _normalize_metric_expectations(expected)
180
+ expected.each do |specish, expected_attrs|
181
+ expected_spec = metric_spec_from_specish(specish)
182
+ actual_stats = NewRelic::Agent.instance.stats_engine.to_h[expected_spec]
183
+ if !actual_stats
184
+ all_specs = NewRelic::Agent.instance.stats_engine.to_h.keys.sort
185
+ matches = all_specs.select { |spec| spec.name == expected_spec.name }
186
+ matches.map! { |m| " #{m.inspect}" }
187
+
188
+ msg = "Did not find stats for spec #{expected_spec.inspect}."
189
+ msg += "\nDid find specs: [\n#{matches.join(",\n")}\n]" unless matches.empty?
190
+ msg += "\nAll specs in there were: #{format_metric_spec_list(all_specs)}"
191
+
192
+ assert(actual_stats, msg)
193
+ end
194
+ assert_stats_has_values(actual_stats, expected_spec, expected_attrs)
195
+ end
196
+ end
197
+
198
+ # Use this to assert that *only* the given set of metrics has been recorded.
199
+ #
200
+ # If you want to scope the search for unexpected metrics to a particular
201
+ # namespace (e.g. metrics matching 'Controller/'), pass a Regex for the
202
+ # :filter option. Only metrics matching the regex will be searched when looking
203
+ # for unexpected metrics.
204
+ #
205
+ # If you want to *allow* unexpected metrics matching certain patterns, use
206
+ # the :ignore_filter option. This will allow you to specify a Regex that
207
+ # whitelists broad swathes of metric territory (e.g. 'Supportability/').
208
+ #
209
+ def assert_metrics_recorded_exclusive(expected, options={})
210
+ expected = _normalize_metric_expectations(expected)
211
+ assert_metrics_recorded(expected)
212
+
213
+ recorded_metrics = NewRelic::Agent.instance.stats_engine.to_h.keys
214
+
215
+ if options[:filter]
216
+ recorded_metrics = recorded_metrics.select { |m| m.name.match(options[:filter]) }
217
+ end
218
+ if options[:ignore_filter]
219
+ recorded_metrics.reject! { |m| m.name.match(options[:ignore_filter]) }
220
+ end
221
+
222
+ expected_metrics = expected.keys.map { |s| metric_spec_from_specish(s) }
223
+
224
+ unexpected_metrics = recorded_metrics - expected_metrics
225
+ unexpected_metrics.reject! { |m| m.name =~ /GC\/Transaction/ }
226
+
227
+ assert_equal(0, unexpected_metrics.size, "Found unexpected metrics: #{format_metric_spec_list(unexpected_metrics)}")
228
+ end
229
+
230
+ # The clear_metrics! method prevents metrics from "leaking" between tests by resetting
231
+ # the @stats_hash instance variable in the current instance of NewRelic::Agent::StatsEngine.
232
+
233
+ module NewRelic
234
+ module Agent
235
+ class StatsEngine
236
+ def reset_for_test!
237
+ @stats_hash = StatsHash.new
238
+ end
239
+ end
240
+ end
241
+ end
242
+
243
+ def clear_metrics!
244
+ NewRelic::Agent.instance.stats_engine.reset_for_test!
245
+ end
246
+
247
+ def assert_metrics_not_recorded(not_expected)
248
+ not_expected = _normalize_metric_expectations(not_expected)
249
+ found_but_not_expected = []
250
+ not_expected.each do |specish, _|
251
+ spec = metric_spec_from_specish(specish)
252
+ if NewRelic::Agent.instance.stats_engine.to_h[spec]
253
+ found_but_not_expected << spec
254
+ end
255
+ end
256
+ assert_equal([], found_but_not_expected, "Found unexpected metrics: #{format_metric_spec_list(found_but_not_expected)}")
257
+ end
258
+
259
+ alias :refute_metrics_recorded :assert_metrics_not_recorded
260
+
261
+ def assert_no_metrics_match(regex)
262
+ matching_metrics = []
263
+ NewRelic::Agent.instance.stats_engine.to_h.keys.map(&:to_s).each do |metric|
264
+ matching_metrics << metric if metric.match regex
265
+ end
266
+
267
+ assert_equal(
268
+ [],
269
+ matching_metrics,
270
+ "Found unexpected metrics:\n" + matching_metrics.map { |m| " '#{m}'"}.join("\n") + "\n\n"
271
+ )
272
+ end
273
+
274
+ alias :refute_metrics_match :assert_no_metrics_match
275
+
276
+ def format_metric_spec_list(specs)
277
+ spec_strings = specs.map do |spec|
278
+ "#{spec.name} (#{spec.scope.empty? ? '<unscoped>' : spec.scope})"
279
+ end
280
+ "[\n #{spec_strings.join(",\n ")}\n]"
281
+ end
282
+
283
+ def assert_truthy(expected, msg = nil)
284
+ msg ||= "Expected #{expected.inspect} to be truthy"
285
+ assert !!expected, msg
286
+ end
287
+
288
+ def assert_falsy(expected, msg = nil)
289
+ msg ||= "Expected #{expected.inspect} to be falsy"
290
+ assert !expected, msg
291
+ end
292
+
293
+ unless defined?( assert_false )
294
+ def assert_false(expected)
295
+ assert_equal false, expected
296
+ end
297
+ end
298
+
299
+ unless defined?(refute)
300
+ alias refute assert_false
301
+ end
302
+
303
+ # Mock up a transaction for testing purposes, optionally specifying a name and
304
+ # transaction category. The given block will be executed within the context of the
305
+ # dummy transaction.
306
+ #
307
+ # Examples:
308
+ #
309
+ # With default name ('dummy') and category (:other):
310
+ # in_transaction { ... }
311
+ #
312
+ # With an explicit transaction name and default category:
313
+ # in_transaction('foobar') { ... }
314
+ #
315
+ # With default name and explicit category:
316
+ # in_transaction(:category => :controller) { ... }
317
+ #
318
+ # With a transaction name plus category:
319
+ # in_transaction('foobar', :category => :controller) { ... }
320
+ #
321
+ def in_transaction(*args, &blk)
322
+ opts = (args.last && args.last.is_a?(Hash)) ? args.pop : {}
323
+ category = (opts && opts.delete(:category)) || :other
324
+
325
+ # At least one test passes `:transaction_name => nil`, so handle it gently
326
+ name = opts.key?(:transaction_name) ? opts.delete(:transaction_name) :
327
+ args.first || 'dummy'
328
+
329
+ state = NewRelic::Agent::TransactionState.tl_get
330
+ txn = nil
331
+
332
+ NewRelic::Agent::Transaction.wrap(state, name, category, opts) do
333
+ txn = state.current_transaction
334
+ yield state.current_transaction
335
+ end
336
+
337
+ txn
338
+ end
339
+
340
+ def stub_transaction_guid(guid)
341
+ NewRelic::Agent::Transaction.tl_current.instance_variable_set(:@guid, guid)
342
+ end
343
+
344
+ # Convenience wrapper around in_transaction that sets the category so that it
345
+ # looks like we are in a web transaction
346
+ def in_web_transaction(name='dummy')
347
+ in_transaction(name, :category => :controller, :request => stub(:path => '/')) do |txn|
348
+ yield txn
349
+ end
350
+ end
351
+
352
+ def in_background_transaction(name='silly')
353
+ in_transaction(name, :category => :task) do |txn|
354
+ yield txn
355
+ end
356
+ end
357
+
358
+ def refute_contains_request_params(attributes)
359
+ attributes.keys.each do |key|
360
+ refute_match(/^request\.parameters\./, key.to_s)
361
+ end
362
+ end
363
+
364
+ def last_transaction_trace
365
+ NewRelic::Agent.agent.transaction_sampler.last_sample
366
+ end
367
+
368
+ def last_transaction_trace_request_params
369
+ agent_attributes = attributes_for(last_transaction_trace, :agent)
370
+ agent_attributes.inject({}) do |memo, (key, value)|
371
+ memo[key] = value if key.to_s.start_with?("request.parameters.")
372
+ memo
373
+ end
374
+ end
375
+
376
+ def find_sql_trace(metric_name)
377
+ NewRelic::Agent.agent.sql_sampler.sql_traces.values.detect do |trace|
378
+ trace.database_metric_name == metric_name
379
+ end
380
+ end
381
+
382
+ def last_sql_trace
383
+ NewRelic::Agent.agent.sql_sampler.sql_traces.values.last
384
+ end
385
+
386
+ def find_last_transaction_node(transaction_sample=nil)
387
+ if transaction_sample
388
+ root_node = transaction_sample.root_node
389
+ else
390
+ builder = NewRelic::Agent.agent.transaction_sampler.tl_builder
391
+ root_node = builder.current_node
392
+ end
393
+
394
+ last_node = nil
395
+ root_node.each_node {|s| last_node = s }
396
+
397
+ return last_node
398
+ end
399
+
400
+ def find_node_with_name(transaction_sample, name)
401
+ transaction_sample.root_node.each_node do |node|
402
+ if node.metric_name == name
403
+ return node
404
+ end
405
+ end
406
+
407
+ nil
408
+ end
409
+
410
+ def find_node_with_name_matching(transaction_sample, regex)
411
+ transaction_sample.root_node.each_node do |node|
412
+ if node.metric_name.match regex
413
+ return node
414
+ end
415
+ end
416
+
417
+ nil
418
+ end
419
+
420
+ def find_all_nodes_with_name_matching(transaction_sample, regexes)
421
+ regexes = [regexes].flatten
422
+ matching_nodes = []
423
+
424
+ transaction_sample.root_node.each_node do |node|
425
+ regexes.each do |regex|
426
+ if node.metric_name.match regex
427
+ matching_nodes << node
428
+ end
429
+ end
430
+ end
431
+
432
+ matching_nodes
433
+ end
434
+
435
+ def with_config(config_hash, at_start=true)
436
+ config = NewRelic::Agent::Configuration::DottedHash.new(config_hash, true)
437
+ NewRelic::Agent.config.add_config_for_testing(config, at_start)
438
+ NewRelic::Agent.instance.refresh_attribute_filter
439
+ begin
440
+ yield
441
+ ensure
442
+ NewRelic::Agent.config.remove_config(config)
443
+ NewRelic::Agent.instance.refresh_attribute_filter
444
+ end
445
+ end
446
+
447
+ def with_config_low_priority(config_hash)
448
+ with_config(config_hash, false) do
449
+ yield
450
+ end
451
+ end
452
+
453
+ def with_transaction_renaming_rules(rule_specs)
454
+ original_engine = NewRelic::Agent.agent.instance_variable_get(:@transaction_rules)
455
+ begin
456
+ new_engine = NewRelic::Agent::RulesEngine.create_transaction_rules('transaction_name_rules' => rule_specs)
457
+ NewRelic::Agent.agent.instance_variable_set(:@transaction_rules, new_engine)
458
+ yield
459
+ ensure
460
+ NewRelic::Agent.agent.instance_variable_set(:@transaction_rules, original_engine)
461
+ end
462
+ end
463
+
464
+ # Need to guard against double-installing this patch because in 1.8.x the same
465
+ # file can be required multiple times under different non-canonicalized paths.
466
+ unless Time.respond_to?(:__original_now)
467
+ Time.instance_eval do
468
+ class << self
469
+ attr_accessor :__frozen_now
470
+ alias_method :__original_now, :now
471
+
472
+ def now
473
+ __frozen_now || __original_now
474
+ end
475
+ end
476
+ end
477
+ end
478
+
479
+ def freeze_time(now=Time.now)
480
+ Time.__frozen_now = now
481
+ end
482
+
483
+ def unfreeze_time
484
+ Time.__frozen_now = nil
485
+ end
486
+
487
+ def advance_time(seconds)
488
+ freeze_time(Time.now + seconds)
489
+ end
490
+
491
+ def with_constant_defined(constant_symbol, implementation=Module.new)
492
+ const_path = constant_path(constant_symbol.to_s)
493
+
494
+ if const_path
495
+ # Constant is already defined, nothing to do
496
+ return yield
497
+ else
498
+ const_path = constant_path(constant_symbol.to_s, :allow_partial => true)
499
+ parent = const_path[-1]
500
+ constant_symbol = constant_symbol.to_s.split('::').last.to_sym
501
+ end
502
+
503
+ begin
504
+ parent.const_set(constant_symbol, implementation)
505
+ yield
506
+ ensure
507
+ parent.send(:remove_const, constant_symbol)
508
+ end
509
+ end
510
+
511
+ def constant_path(name, opts={})
512
+ allow_partial = opts[:allow_partial]
513
+ path = [Object]
514
+ parts = name.gsub(/^::/, '').split('::')
515
+ parts.each do |part|
516
+ if !path.last.const_defined?(part)
517
+ return allow_partial ? path : nil
518
+ end
519
+ path << path.last.const_get(part)
520
+ end
521
+ path
522
+ end
523
+
524
+ def undefine_constant(constant_symbol)
525
+ const_path = constant_path(constant_symbol.to_s)
526
+ return yield unless const_path
527
+ parent = const_path[-2]
528
+ const_name = constant_symbol.to_s.gsub(/.*::/, '')
529
+ removed_constant = parent.send(:remove_const, const_name)
530
+ yield
531
+ ensure
532
+ parent.const_set(const_name, removed_constant) if removed_constant
533
+ end
534
+
535
+ def with_debug_logging
536
+ orig_logger = NewRelic::Agent.logger
537
+ $stderr.puts '', '---', ''
538
+ NewRelic::Agent.logger =
539
+ NewRelic::Agent::AgentLogger.new('', Logger.new($stderr) )
540
+
541
+ with_config(:log_level => 'debug') do
542
+ yield
543
+ end
544
+ ensure
545
+ NewRelic::Agent.logger = orig_logger
546
+ end
547
+
548
+ def create_agent_command(args = {})
549
+ NewRelic::Agent::Commands::AgentCommand.new([-1, { "name" => "command_name", "arguments" => args}])
550
+ end
551
+
552
+ def wait_for_backtrace_service_poll(opts={})
553
+ defaults = {
554
+ :timeout => 10.0,
555
+ :service => NewRelic::Agent.agent.agent_command_router.backtrace_service,
556
+ :iterations => 1
557
+ }
558
+ opts = defaults.merge(opts)
559
+ deadline = Time.now + opts[:timeout]
560
+
561
+ service = opts[:service]
562
+ worker_loop = service.worker_loop
563
+ worker_loop.setup(0, service.method(:poll))
564
+
565
+ until worker_loop.iterations > opts[:iterations]
566
+ sleep(0.01)
567
+ if Time.now > deadline
568
+ raise "Timed out waiting #{opts[:timeout]} s for backtrace service poll\n" +
569
+ "Worker loop ran for #{opts[:service].worker_loop.iterations} iterations\n\n" +
570
+ Thread.list.map { |t|
571
+ "#{t.to_s}: newrelic_label: #{t[:newrelic_label].inspect}\n\n" +
572
+ (t.backtrace || []).join("\n\t")
573
+ }.join("\n\n")
574
+ end
575
+ end
576
+ end
577
+
578
+ def with_array_logger(level=:info)
579
+ orig_logger = NewRelic::Agent.logger
580
+ config = { :log_level => level }
581
+ logdev = ArrayLogDevice.new
582
+ override_logger = Logger.new(logdev)
583
+
584
+ with_config(config) do
585
+ NewRelic::Agent.logger = NewRelic::Agent::AgentLogger.new("", override_logger)
586
+ yield
587
+ end
588
+
589
+ return logdev
590
+ ensure
591
+ NewRelic::Agent.logger = orig_logger
592
+ end
593
+
594
+ def with_environment(env)
595
+ old_env = {}
596
+ env.each do |key, val|
597
+ old_env[key] = ENV[key]
598
+ ENV[key] = val.to_s
599
+ end
600
+ begin
601
+ yield
602
+ ensure
603
+ old_env.each { |key, old_val| ENV[key] = old_val }
604
+ end
605
+ end
606
+
607
+ def with_argv(argv)
608
+ old_argv = ARGV.dup
609
+ ARGV.clear
610
+ ARGV.concat(argv)
611
+
612
+ begin
613
+ yield
614
+ ensure
615
+ ARGV.clear
616
+ ARGV.concat(old_argv)
617
+ end
618
+ end
619
+
620
+ def with_ignore_error_filter(filter, &blk)
621
+ original_filter = NewRelic::Agent.ignore_error_filter
622
+ NewRelic::Agent.ignore_error_filter(&filter)
623
+
624
+ yield
625
+ ensure
626
+ NewRelic::Agent::ErrorCollector.ignore_error_filter = original_filter
627
+ end
628
+
629
+ def json_dump_and_encode(object)
630
+ Base64.encode64(::JSON.dump(object))
631
+ end
632
+
633
+ def get_last_analytics_event
634
+ NewRelic::Agent.agent.transaction_event_aggregator.harvest![1].last
635
+ end
636
+
637
+ def swap_instance_method(target, method_name, new_method_implementation, &blk)
638
+ old_method_implementation = target.instance_method(method_name)
639
+ target.send(:define_method, method_name, new_method_implementation)
640
+ yield
641
+ rescue NameError => e
642
+ puts "Your target does not have the instance method #{method_name}"
643
+ puts e.inspect
644
+ ensure
645
+ target.send(:define_method, method_name, old_method_implementation)
646
+ end
647
+
648
+ def cross_agent_tests_dir
649
+ File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'cross_agent_tests'))
650
+ end
651
+
652
+ def load_cross_agent_test(name)
653
+ test_file_path = File.join(cross_agent_tests_dir, "#{name}.json")
654
+ data = File.read(test_file_path)
655
+ data.gsub!('callCount', 'call_count')
656
+ data = ::JSON.load(data)
657
+ data.each { |testcase| testcase['testname'].gsub! ' ', '_' if String === testcase['testname'] }
658
+ data
659
+ end
660
+
661
+ def each_cross_agent_test(options)
662
+ options = {:dir => nil, :pattern => "*"}.update(options)
663
+ path = File.join [cross_agent_tests_dir, options[:dir], options[:pattern]].compact
664
+ Dir.glob(path).each { |file| yield file}
665
+ end
666
+
667
+ def assert_event_attributes(event, test_name, expected_attributes, non_expected_attributes)
668
+ incorrect_attributes = []
669
+
670
+ event_attrs = event[0]
671
+
672
+ expected_attributes.each do |name, expected_value|
673
+ actual_value = event_attrs[name]
674
+ incorrect_attributes << name unless actual_value == expected_value
675
+ end
676
+
677
+ msg = "Found missing or incorrect attribute values in #{test_name}:\n"
678
+
679
+ incorrect_attributes.each do |name|
680
+ msg << " #{name}: expected = #{expected_attributes[name].inspect}, actual = #{event_attrs[name].inspect}\n"
681
+ end
682
+ msg << "\n"
683
+
684
+ msg << "All event values:\n"
685
+ event_attrs.each do |name, actual_value|
686
+ msg << " #{name}: #{actual_value.inspect}\n"
687
+ end
688
+ assert(incorrect_attributes.empty?, msg)
689
+
690
+ non_expected_attributes.each do |name|
691
+ assert_nil(event_attrs[name], "Found value '#{event_attrs[name]}' for attribute '#{name}', but expected nothing in #{test_name}")
692
+ end
693
+ end
694
+
695
+ def attributes_for(sample, type)
696
+ sample.attributes.instance_variable_get("@#{type}_attributes")
697
+ end