newrelic_rpm 3.13.2.302 → 3.14.0.305

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +20 -0
  3. data/README.md +1 -13
  4. data/lib/new_relic/agent/agent.rb +40 -15
  5. data/lib/new_relic/agent/agent_logger.rb +1 -1
  6. data/lib/new_relic/agent/configuration/default_source.rb +16 -6
  7. data/lib/new_relic/agent/configuration/server_source.rb +2 -1
  8. data/lib/new_relic/agent/error_collector.rb +13 -21
  9. data/lib/new_relic/agent/error_event_aggregator.rb +124 -0
  10. data/lib/new_relic/agent/error_trace_aggregator.rb +15 -5
  11. data/lib/new_relic/agent/instrumentation/middleware_tracing.rb +16 -2
  12. data/lib/new_relic/agent/new_relic_service.rb +11 -10
  13. data/lib/new_relic/agent/new_relic_service/json_marshaller.rb +1 -1
  14. data/lib/new_relic/agent/payload_metric_mapping.rb +58 -0
  15. data/lib/new_relic/agent/sampled_buffer.rb +15 -1
  16. data/lib/new_relic/agent/supported_versions.rb +4 -4
  17. data/lib/new_relic/agent/transaction.rb +52 -44
  18. data/lib/new_relic/agent/transaction/request_attributes.rb +110 -0
  19. data/lib/new_relic/agent/transaction_event_aggregator.rb +4 -41
  20. data/lib/new_relic/noticed_error.rb +25 -9
  21. data/lib/new_relic/rack/agent_middleware.rb +5 -0
  22. data/lib/new_relic/version.rb +2 -2
  23. data/newrelic_rpm.gemspec +6 -0
  24. data/test/agent_helper.rb +16 -7
  25. data/test/environments/norails/Gemfile +1 -0
  26. data/test/multiverse/lib/multiverse/runner.rb +1 -1
  27. data/test/multiverse/lib/multiverse/suite.rb +6 -3
  28. data/test/multiverse/suites/agent_only/agent_attributes_test.rb +57 -0
  29. data/test/multiverse/suites/agent_only/audit_log_test.rb +1 -2
  30. data/test/multiverse/suites/agent_only/error_events_test.rb +82 -0
  31. data/test/multiverse/suites/agent_only/harvest_timestamps_test.rb +31 -6
  32. data/test/multiverse/suites/agent_only/logging_test.rb +2 -2
  33. data/test/multiverse/suites/agent_only/marshaling_test.rb +6 -0
  34. data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +2 -2
  35. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +0 -20
  36. data/test/multiverse/suites/grape/grape_test.rb +16 -1
  37. data/test/multiverse/suites/no_json/Envfile +12 -0
  38. data/test/multiverse/suites/no_json/config/newrelic.yml +27 -0
  39. data/test/multiverse/suites/no_json/marshal_config_test.rb +22 -0
  40. data/test/multiverse/suites/rack/example_app.rb +19 -0
  41. data/test/multiverse/suites/rack/response_content_type_test.rb +50 -0
  42. data/test/multiverse/suites/rails/error_tracing_test.rb +12 -7
  43. data/test/multiverse/suites/rails/parameter_capture_test.rb +21 -1
  44. data/test/multiverse/suites/sinatra/sinatra_error_tracing_test.rb +5 -2
  45. data/test/multiverse/suites/sinatra/sinatra_parameter_capture_test.rb +16 -1
  46. data/test/multiverse/suites/sinatra/sinatra_test_cases.rb +4 -2
  47. data/test/new_relic/agent/agent_logger_test.rb +9 -0
  48. data/test/new_relic/agent/agent_test.rb +6 -4
  49. data/test/new_relic/agent/audit_logger_test.rb +0 -7
  50. data/test/new_relic/agent/error_collector_test.rb +20 -250
  51. data/test/new_relic/agent/error_event_aggregator_test.rb +294 -0
  52. data/test/new_relic/agent/error_trace_aggregator_test.rb +273 -5
  53. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -2
  54. data/test/new_relic/agent/new_relic_service_test.rb +8 -32
  55. data/test/new_relic/agent/payload_metric_mapping_test.rb +74 -0
  56. data/test/new_relic/agent/pipe_channel_manager_test.rb +6 -5
  57. data/test/new_relic/agent/sampled_buffer_test.rb +36 -0
  58. data/test/new_relic/agent/sql_sampler_test.rb +4 -14
  59. data/test/new_relic/agent/stats_engine/stats_hash_test.rb +5 -4
  60. data/test/new_relic/agent/threading/thread_profile_test.rb +1 -2
  61. data/test/new_relic/agent/transaction/request_attributes_test.rb +76 -0
  62. data/test/new_relic/agent/transaction_event_aggregator_test.rb +12 -1
  63. data/test/new_relic/agent/transaction_test.rb +60 -11
  64. data/test/new_relic/data_container_tests.rb +17 -6
  65. data/test/new_relic/fake_collector.rb +16 -21
  66. data/test/new_relic/marshalling_test_cases.rb +1 -0
  67. data/test/new_relic/noticed_error_test.rb +59 -0
  68. data/test/new_relic/rack/agent_hooks_test.rb +1 -1
  69. data/test/new_relic/rack/error_collector_test.rb +7 -5
  70. data/test/performance/suites/error_collector.rb +28 -0
  71. data/test/performance/suites/marshalling.rb +0 -8
  72. metadata +14 -3
  73. data/lib/new_relic/agent/new_relic_service/pruby_marshaller.rb +0 -56
@@ -125,13 +125,6 @@ class AuditLoggerTest < Minitest::Test
125
125
  @logger.log_request(@uri, data, @marshaller)
126
126
  end
127
127
 
128
- def test_logs_inspect_with_pruby_marshaller
129
- setup_fake_logger
130
- pruby_marshaller = NewRelic::Agent::NewRelicService::PrubyMarshaller.new
131
- @logger.log_request(@uri, @dummy_data, pruby_marshaller)
132
- assert_log_contains_string(@dummy_data.inspect)
133
- end
134
-
135
128
  def test_logs_json_with_json_marshaller
136
129
  marshaller_cls = NewRelic::Agent::NewRelicService::JsonMarshaller
137
130
  if marshaller_cls.is_supported?
@@ -28,163 +28,31 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
28
28
  NewRelic::Agent.config.reset_to_defaults
29
29
  end
30
30
 
31
- # Helpers for DataContainerTests
32
-
33
- def create_container
34
- NewRelic::Agent::ErrorCollector.new
35
- end
36
-
37
- def populate_container(collector, n)
38
- n.times do |i|
39
- collector.notice_error('yay errors', :metric => 'path')
40
- end
41
- end
42
-
43
- include NewRelic::DataContainerTests
44
-
45
31
  # Tests
46
32
 
47
33
  def test_empty
48
- @error_collector.harvest!
49
34
  @error_collector.notice_error(nil, :metric=> 'path')
50
- errors = @error_collector.harvest!
51
-
35
+ errors = harvest_error_traces
52
36
  assert_equal 0, errors.length
53
-
54
- @error_collector.notice_error('Some error message', :metric=> 'path')
55
- errors = @error_collector.harvest!
56
-
57
- err = errors.first
58
- assert_equal 'Some error message', err.message
59
- assert_equal '', err.request_uri
60
- assert_equal 'path', err.path
61
- assert_equal 'Error', err.exception_class_name
62
- end
63
-
64
- def test_simple
65
- @error_collector.notice_error(StandardError.new("message"),
66
- :uri => '/myurl/',
67
- :metric => 'path')
68
-
69
- errors = @error_collector.harvest!
70
-
71
- assert_equal errors.length, 1
72
-
73
- err = errors.first
74
- assert_equal 'message', err.message
75
- assert_equal '/myurl/', err.request_uri
76
- assert_equal 'path', err.path
77
- assert_equal 'StandardError', err.exception_class_name
78
-
79
- # the collector should now return an empty array since nothing
80
- # has been added since its last harvest
81
- errors = @error_collector.harvest!
82
- assert_empty errors
83
37
  end
84
38
 
85
39
  def test_drops_deprecated_options
86
40
  expects_logging(:warn, any_parameters)
87
- @error_collector.notice_error(StandardError.new("message"),
41
+ error = @error_collector.create_noticed_error(StandardError.new("message"),
88
42
  :referer => "lalalalala",
89
43
  :request => stub('request'),
90
44
  :request_params => {:x => 'y'})
91
45
 
92
- errors = @error_collector.harvest!
93
-
94
- assert_empty errors.first.attributes_from_notice_error
95
- end
96
-
97
- def test_long_message
98
- #yes, times 500. it's a 5000 byte string. Assuming strings are
99
- #still 1 byte / char.
100
- @error_collector.notice_error(StandardError.new("1234567890" * 500), :uri => '/myurl/', :metric => 'path')
101
-
102
- errors = @error_collector.harvest!
103
-
104
- assert_equal errors.length, 1
105
-
106
- err = errors.first
107
- assert_equal 4096, err.message.length
108
- assert_equal ('1234567890' * 500)[0..4095], err.message
109
- end
110
-
111
- def test_collect_failover
112
- @error_collector.notice_error(StandardError.new("message"), :metric => 'first')
113
-
114
- errors = @error_collector.harvest!
115
-
116
- @error_collector.notice_error(StandardError.new("message"), :metric => 'second')
117
- @error_collector.notice_error(StandardError.new("message"), :metric => 'path')
118
- @error_collector.notice_error(StandardError.new("message"), :metric => 'last')
119
-
120
- @error_collector.merge!(errors)
121
- errors = @error_collector.harvest!
122
-
123
- assert_equal 4, errors.length
124
- assert_equal_unordered(%w(first second path last), errors.map { |e| e.path })
125
-
126
- @error_collector.notice_error(StandardError.new("message"), :metric => 'first')
127
- @error_collector.notice_error(StandardError.new("message"), :metric => 'last')
128
-
129
- errors = @error_collector.harvest!
130
- assert_equal 2, errors.length
131
- assert_equal 'first', errors.first.path
132
- assert_equal 'last', errors.last.path
133
- end
134
-
135
- def test_queue_overflow
136
- max_q_length = NewRelic::Agent::ErrorCollector::MAX_ERROR_QUEUE_LENGTH
137
-
138
- silence_stream(::STDOUT) do
139
- (max_q_length + 5).times do |n|
140
- @error_collector.notice_error(StandardError.new("exception #{n}"),
141
- :metric => "path",
142
- :custom_params => {:x => n})
143
- end
144
- end
145
-
146
- errors = @error_collector.harvest!
147
- assert errors.length == max_q_length
148
- errors.each_index do |i|
149
- error = errors.shift
150
- actual = error.to_collector_array.last["userAttributes"]["x"]
151
- assert_equal i.to_s, actual
152
- end
153
- end
154
-
155
- # Why would anyone undef these methods?
156
- class TestClass
157
- undef to_s
158
- undef inspect
159
- end
160
-
161
-
162
- def test_supported_param_types
163
- types = [[1, '1'],
164
- [1.1, '1.1'],
165
- ['hi', 'hi'],
166
- [:hi, 'hi'],
167
- [StandardError.new("test"), "#<StandardError>"],
168
- [TestClass.new, "#<NewRelic::Agent::ErrorCollectorTest::TestClass>"]
169
- ]
170
46
 
171
- types.each do |test|
172
- @error_collector.notice_error(StandardError.new("message"),
173
- :metric => 'path',
174
- :custom_params => {:x => test[0]})
175
- error = @error_collector.harvest![0].to_collector_array
176
- actual = error.last["userAttributes"]["x"]
177
- assert_equal test[1], actual
178
- end
47
+ assert_empty error.attributes_from_notice_error
179
48
  end
180
49
 
181
-
182
50
  def test_exclude
183
51
  @error_collector.ignore(["IOError"])
184
52
 
185
53
  @error_collector.notice_error(IOError.new("message"), :metric => 'path')
186
54
 
187
- errors = @error_collector.harvest!
55
+ errors = harvest_error_traces
188
56
 
189
57
  assert_equal 0, errors.length
190
58
  end
@@ -195,10 +63,9 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
195
63
  NewRelic::Agent.config.add_config_for_testing(:'error_collector.ignore_errors' => "IOError")
196
64
  @error_collector.notice_error(IOError.new("message"))
197
65
 
198
- errors = @error_collector.harvest!
66
+ errors = harvest_error_traces
199
67
 
200
68
  assert_equal 1, errors.length
201
-
202
69
  end
203
70
 
204
71
  def test_exclude_block
@@ -207,7 +74,7 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
207
74
  @error_collector.notice_error(IOError.new("message"), :metric => 'path')
208
75
  @error_collector.notice_error(StandardError.new("message"), :metric => 'path')
209
76
 
210
- errors = @error_collector.harvest!
77
+ errors = harvest_error_traces
211
78
 
212
79
  assert_equal 1, errors.length
213
80
  end
@@ -219,7 +86,7 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
219
86
 
220
87
  @error_collector.notice_error(StandardError.new("message"))
221
88
 
222
- errors = @error_collector.harvest!
89
+ errors = harvest_error_traces
223
90
 
224
91
  assert_equal 1, errors.length
225
92
  end
@@ -233,19 +100,7 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
233
100
  new_error_collector = NewRelic::Agent::ErrorCollector.new
234
101
  new_error_collector.notice_error(StandardError.new("message"))
235
102
 
236
- assert_empty new_error_collector.harvest!
237
- end
238
-
239
- def test_obfuscates_error_messages_when_high_security_is_set
240
- with_config(:high_security => true) do
241
- @error_collector.notice_error(StandardError.new("YO SQL BAD: serect * flom test where foo = 'bar'"))
242
- @error_collector.notice_error(StandardError.new("YO SQL BAD: serect * flom test where foo in (1,2,3,4,5)"))
243
-
244
- errors = @error_collector.harvest!
245
-
246
- assert_equal(NewRelic::NoticedError::STRIPPED_EXCEPTION_REPLACEMENT_MESSAGE, errors[0].message)
247
- assert_equal(NewRelic::NoticedError::STRIPPED_EXCEPTION_REPLACEMENT_MESSAGE, errors[1].message)
248
- end
103
+ assert_empty new_error_collector.error_trace_aggregator.harvest!
249
104
  end
250
105
 
251
106
  def test_increments_count_on_errors
@@ -274,7 +129,7 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
274
129
  'Errors/OtherTransaction/AnotherFramework/Job/perform'])
275
130
  end
276
131
 
277
- def test_icrement_error_count_summary_outside_transaction
132
+ def test_increment_error_count_summary_outside_transaction
278
133
  @error_collector.increment_error_count!(NewRelic::Agent::TransactionState.tl_get, StandardError.new('Boo'))
279
134
 
280
135
  assert_metrics_recorded(['Errors/all'])
@@ -289,95 +144,6 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
289
144
  assert_metrics_not_recorded(['Errors/(unknown)'])
290
145
  end
291
146
 
292
-
293
- class DifficultToDebugAgentError < NewRelic::Agent::InternalAgentError
294
- end
295
-
296
- class AnotherToughAgentError < NewRelic::Agent::InternalAgentError
297
- end
298
-
299
- def test_notices_agent_error
300
- @error_collector.notice_agent_error(DifficultToDebugAgentError.new)
301
- assert_equal 1, @error_collector.errors.size
302
- end
303
-
304
- def test_only_notices_agent_error_per_type
305
- @error_collector.notice_agent_error(DifficultToDebugAgentError.new)
306
- @error_collector.notice_agent_error(DifficultToDebugAgentError.new)
307
-
308
- assert_equal 1, @error_collector.errors.size
309
- end
310
-
311
- def test_only_notices_agent_error_per_type_allows_other_types
312
- @error_collector.notice_agent_error(DifficultToDebugAgentError.new)
313
- @error_collector.notice_agent_error(DifficultToDebugAgentError.new)
314
- @error_collector.notice_agent_error(AnotherToughAgentError.new)
315
-
316
- assert_equal 2, @error_collector.errors.size
317
- end
318
-
319
- def test_does_not_touch_error_metrics
320
- @error_collector.notice_agent_error(DifficultToDebugAgentError.new)
321
- @error_collector.notice_agent_error(DifficultToDebugAgentError.new)
322
- @error_collector.notice_agent_error(AnotherToughAgentError.new)
323
-
324
- assert_metrics_recorded_exclusive([])
325
- end
326
-
327
- def test_notice_agent_error_set_noticed_error_attributes
328
- @error_collector.notice_agent_error(DifficultToDebugAgentError.new)
329
-
330
- err = @error_collector.errors.first
331
- assert_equal "NewRelic/AgentError", err.path
332
- refute_nil err.stack_trace
333
- end
334
-
335
- def test_notice_agent_error_uses_exception_backtrace_if_present
336
- trace = ["boo", "yeah", "error"]
337
- exception = DifficultToDebugAgentError.new
338
- exception.set_backtrace(trace)
339
- @error_collector.notice_agent_error(exception)
340
-
341
- assert_equal trace, @error_collector.errors.first.stack_trace
342
- end
343
-
344
- def test_notice_agent_error_uses_caller_if_no_exception_backtrace
345
- exception = DifficultToDebugAgentError.new
346
- exception.set_backtrace(nil)
347
- @error_collector.notice_agent_error(exception)
348
-
349
- trace = @error_collector.errors.first.stack_trace
350
- assert trace.any? {|line| line.include?(__FILE__)}
351
- end
352
-
353
- def test_notice_agent_error_allows_an_error_past_queue_limit
354
- 100.times { @error_collector.notice_error(StandardError.new("Ouch")) }
355
-
356
- exception = DifficultToDebugAgentError.new
357
- @error_collector.notice_agent_error(exception)
358
-
359
- assert_equal 21, @error_collector.errors.size
360
- assert_equal DifficultToDebugAgentError.name, @error_collector.errors.last.exception_class_name
361
- end
362
-
363
- def test_notice_agent_error_doesnt_clog_up_the_queue_limit
364
- exception = DifficultToDebugAgentError.new
365
- @error_collector.notice_agent_error(exception)
366
-
367
- 100.times { @error_collector.notice_error(StandardError.new("Ouch")) }
368
-
369
- assert_equal 21, @error_collector.errors.size
370
- end
371
-
372
- def test_notice_agent_error_adds_support_message
373
- exception = DifficultToDebugAgentError.new("BOO")
374
- @error_collector.notice_agent_error(exception)
375
-
376
- err = @error_collector.errors.first
377
- assert err.message.include?(exception.message)
378
- assert err.message.include?("Ruby agent internal error")
379
- end
380
-
381
147
  def test_blamed_metric_from_options_outside_txn
382
148
  @error_collector.notice_error(StandardError.new('wut'), :metric => 'boo')
383
149
  assert_metrics_recorded(
@@ -420,8 +186,10 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
420
186
  @error_collector.notice_error(error)
421
187
  end
422
188
 
189
+ errors = @error_collector.error_trace_aggregator.harvest!
190
+
423
191
  assert_metrics_recorded('Errors/all' => { :call_count => 1 })
424
- assert_equal 1, @error_collector.errors.length
192
+ assert_equal 1, errors.length
425
193
  end
426
194
 
427
195
  def test_doesnt_count_seen_exceptions
@@ -431,8 +199,10 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
431
199
  @error_collector.notice_error(error)
432
200
  end
433
201
 
202
+ errors = @error_collector.error_trace_aggregator.harvest!
203
+
434
204
  assert_metrics_not_recorded(['Errors/all'])
435
- assert_empty @error_collector.errors
205
+ assert_empty errors
436
206
  end
437
207
 
438
208
  def test_captures_attributes_on_notice_error
@@ -440,7 +210,9 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
440
210
  attributes = Object.new
441
211
  @error_collector.notice_error(error, :attributes => attributes)
442
212
 
443
- noticed = @error_collector.errors.first
213
+ errors = @error_collector.error_trace_aggregator.harvest!
214
+ noticed = errors.first
215
+
444
216
  assert_equal attributes, noticed.attributes
445
217
  end
446
218
 
@@ -597,9 +369,7 @@ class NewRelic::Agent::ErrorCollectorTest < Minitest::Test
597
369
  end
598
370
  end
599
371
 
600
- def silence_stream(*args)
601
- super
602
- rescue NoMethodError
603
- yield
372
+ def harvest_error_traces
373
+ @error_collector.error_trace_aggregator.harvest!
604
374
  end
605
375
  end
@@ -0,0 +1,294 @@
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
+ require File.expand_path(File.join(File.dirname(__FILE__),'..','..','test_helper'))
6
+ require File.expand_path(File.join(File.dirname(__FILE__),'..','data_container_tests'))
7
+ require 'new_relic/agent/error_event_aggregator'
8
+
9
+ module NewRelic
10
+ module Agent
11
+ class ErrorEventAggregatorTest < Minitest::Test
12
+ def setup
13
+ freeze_time
14
+ @error_event_aggregator = ErrorEventAggregator.new
15
+ end
16
+
17
+ def teardown
18
+ error_event_aggregator.reset!
19
+ reset_error_event_buffer_state
20
+ end
21
+
22
+ def create_container
23
+ @error_event_aggregator
24
+ end
25
+
26
+ def populate_container(sampler, n)
27
+ n.times do
28
+ error = NewRelic::NoticedError.new "Controller/blogs/index", RuntimeError.new("Big Controller")
29
+ payload = in_transaction{}.payload
30
+ @error_event_aggregator.append_event error, payload
31
+ end
32
+ end
33
+
34
+ include NewRelic::DataContainerTests
35
+
36
+ def test_generates_event_from_error
37
+ generate_error
38
+
39
+ intrinsics, *_ = last_error_event
40
+
41
+ assert_equal "TransactionError", intrinsics[:type]
42
+ assert_equal Time.now.to_f, intrinsics[:timestamp]
43
+ assert_equal "RuntimeError", intrinsics[:'error.class']
44
+ assert_equal "Big Controller!", intrinsics[:'error.message']
45
+ assert_equal "Controller/blogs/index", intrinsics[:transactionName]
46
+ assert_equal 0.1, intrinsics[:duration]
47
+ assert_equal 80, intrinsics[:port]
48
+ end
49
+
50
+ def test_event_includes_synthetics
51
+ generate_error :payload_options => {
52
+ :synthetics_resource_id=>3,
53
+ :synthetics_job_id=>4,
54
+ :synthetics_monitor_id=>5
55
+ }
56
+
57
+ intrinsics, *_ = last_error_event
58
+
59
+ assert_equal 3, intrinsics[:'nr.syntheticsResourceId']
60
+ assert_equal 4, intrinsics[:'nr.syntheticsJobId']
61
+ assert_equal 5, intrinsics[:'nr.syntheticsMonitorId']
62
+ end
63
+
64
+ def test_includes_mapped_metrics
65
+ metrics = NewRelic::Agent::TransactionMetrics.new
66
+ metrics.record_unscoped 'Datastore/all', 10
67
+ metrics.record_unscoped 'GC/Transaction/all', 11
68
+ metrics.record_unscoped 'WebFrontend/QueueTime', 12
69
+ metrics.record_unscoped 'External/allWeb', 13
70
+
71
+ generate_error :payload_options => {:metrics => metrics}
72
+
73
+ intrinsics, *_ = last_error_event
74
+
75
+ assert_equal 10.0, intrinsics["databaseDuration"]
76
+ assert_equal 1, intrinsics["databaseCallCount"]
77
+ assert_equal 11.0, intrinsics["gcCumulative"]
78
+ assert_equal 12.0, intrinsics["queueDuration"]
79
+ assert_equal 13.0, intrinsics["externalDuration"]
80
+ assert_equal 1, intrinsics["externalCallCount"]
81
+ end
82
+
83
+ def test_includes_cat_attributes
84
+ generate_error :payload_options => {:guid => "GUID", :referring_transaction_guid=>"REFERRING_GUID"}
85
+
86
+ intrinsics, *_ = last_error_event
87
+
88
+ assert_equal "GUID", intrinsics[:"nr.transactionGuid"]
89
+ assert_equal "REFERRING_GUID", intrinsics[:"nr.referringTransactionGuid"]
90
+ end
91
+
92
+ def test_includes_custom_attributes
93
+ attrs = {"user" => "Wes Mantooth", "channel" => 9}
94
+
95
+ attributes = Transaction::Attributes.new(NewRelic::Agent.instance.attribute_filter)
96
+ attributes.merge_custom_attributes attrs
97
+
98
+ generate_error :error_options => {:attributes => attributes}
99
+
100
+ _, custom_attrs, _ = last_error_event
101
+
102
+ assert_equal attrs, custom_attrs
103
+ end
104
+
105
+ def test_includes_agent_attributes
106
+ attributes = Transaction::Attributes.new(NewRelic::Agent.instance.attribute_filter)
107
+ attributes.add_agent_attribute :'request.headers.referer', "http://blog.site/home", AttributeFilter::DST_ERROR_COLLECTOR
108
+ attributes.add_agent_attribute :httpResponseCode, "200", AttributeFilter::DST_ERROR_COLLECTOR
109
+
110
+ generate_error :error_options => {:attributes => attributes}
111
+
112
+ _, _, agent_attrs = last_error_event
113
+
114
+ expected = {:"request.headers.referer" => "http://blog.site/home", :httpResponseCode => "200"}
115
+ assert_equal expected, agent_attrs
116
+ end
117
+
118
+ def test_respects_max_samples_stored
119
+ with_config :'error_collector.max_event_samples_stored' => 5 do
120
+ 10.times { generate_error }
121
+ end
122
+
123
+ assert_equal 5, last_error_events.size
124
+ end
125
+
126
+ def test_reservoir_stats_reset_after_harvest
127
+ 5.times { generate_error }
128
+
129
+ reservoir_stats, samples = error_event_aggregator.harvest!
130
+ assert_equal 5, reservoir_stats[:events_seen]
131
+
132
+ reservoir_stats, samples = error_event_aggregator.harvest!
133
+ assert_equal 0, reservoir_stats[:events_seen]
134
+ end
135
+
136
+ def test_merge_merges_samples_back_into_buffer
137
+ 5.times { generate_error }
138
+
139
+ last_harvest = error_event_aggregator.harvest!
140
+
141
+ 5.times { generate_error }
142
+
143
+ error_event_aggregator.merge!(last_harvest)
144
+ events = last_error_events
145
+
146
+ assert_equal(10, events.size)
147
+ end
148
+
149
+ def test_merge_abides_by_max_samples_limit
150
+ with_config :'error_collector.max_event_samples_stored' => 5 do
151
+ 4.times { generate_error }
152
+ last_harvest = error_event_aggregator.harvest!
153
+
154
+ 4.times { generate_error }
155
+ error_event_aggregator.merge!(last_harvest)
156
+
157
+ assert_equal(5, last_error_events.size)
158
+ end
159
+ end
160
+
161
+ def test_sample_counts_are_correct_after_merge
162
+ with_config :'error_collector.max_event_samples_stored' => 5 do
163
+ buffer = error_event_aggregator.instance_variable_get :@error_event_buffer
164
+
165
+ 4.times { generate_error }
166
+ last_harvest = error_event_aggregator.harvest!
167
+
168
+ assert_equal 4, buffer.seen_lifetime
169
+ assert_equal 4, buffer.captured_lifetime
170
+ assert_equal 4, last_harvest[0][:events_seen]
171
+
172
+ 4.times { generate_error }
173
+ error_event_aggregator.merge! last_harvest
174
+
175
+ reservoir_stats, samples = error_event_aggregator.harvest!
176
+
177
+ assert_equal 5, samples.size
178
+ assert_equal 8, reservoir_stats[:events_seen]
179
+ assert_equal 8, buffer.seen_lifetime
180
+ assert_equal 5, buffer.captured_lifetime
181
+ end
182
+ end
183
+
184
+ def test_limits_total_number_of_samples_to_max_samples_stored
185
+ with_config :'error_collector.max_event_samples_stored' => 100 do
186
+ 150.times { generate_error }
187
+ assert_equal 100, last_error_events.size
188
+ end
189
+ end
190
+
191
+ def test_resets_limits_on_harvest
192
+ with_config :'error_collector.max_event_samples_stored' => 100 do
193
+ 50.times { generate_error }
194
+ events_before = last_error_events
195
+ assert_equal 50, events_before.size
196
+
197
+ 150.times { generate_error }
198
+ events_after = last_error_events
199
+ assert_equal 100, events_after.size
200
+ end
201
+ end
202
+
203
+ def test_does_not_drop_samples_when_used_from_multiple_threads
204
+ with_config :'error_collector.max_event_samples_stored' => 100 * 100 do
205
+ threads = []
206
+ 25.times do
207
+ threads << Thread.new do
208
+ 100.times{ generate_error }
209
+ end
210
+ end
211
+ threads.each { |t| t.join }
212
+
213
+ assert_equal(25 * 100, last_error_events.size)
214
+ end
215
+ end
216
+
217
+ def test_errors_not_noticed_when_disabled
218
+ with_config :'error_collector.capture_events' => false do
219
+ generate_error
220
+ errors = last_error_events
221
+ assert_empty errors
222
+ end
223
+ end
224
+
225
+ def test_errors_noticed_when_error_traces_disabled
226
+ config = {
227
+ :'error_collector.enabled' => false,
228
+ :'error_collector.capture_events' => true
229
+ }
230
+ with_config config do
231
+ generate_error
232
+ errors = last_error_events
233
+ assert_equal 1, errors.size
234
+ end
235
+ end
236
+
237
+ def error_event_aggregator
238
+ @error_event_aggregator
239
+ end
240
+
241
+ def last_error_events
242
+ error_event_aggregator.harvest![1]
243
+ end
244
+
245
+ def last_error_event
246
+ last_error_events.first
247
+ end
248
+
249
+ def reset_error_event_buffer_state
250
+ # this is not ideal, but we need to reset these counts to clear out state
251
+ # between tests
252
+ buffer = error_event_aggregator.instance_variable_get :@error_event_buffer
253
+ buffer.instance_variable_set :@seen_lifetime, 0
254
+ buffer.instance_variable_set :@captured_lifetime, 0
255
+ end
256
+
257
+ def create_noticed_error options = {}
258
+ exception = options.delete(:exception) || RuntimeError.new("Big Controller!")
259
+ txn_name = "Controller/blogs/index"
260
+ noticed_error = NewRelic::NoticedError.new(txn_name, exception)
261
+ noticed_error.request_uri = "http://site.com/blogs"
262
+ noticed_error.request_port = 80
263
+ noticed_error.attributes = options.delete(:attributes)
264
+ noticed_error.attributes_from_notice_error = options.delete(:custom_params) || {}
265
+ noticed_error.attributes_from_notice_error.merge!(options)
266
+
267
+ noticed_error
268
+ end
269
+
270
+ def create_transaction_payload options = {}
271
+ {
272
+ :name => "Controller/blogs/index",
273
+ :type => :controller,
274
+ :start_timestamp => Time.now.to_f,
275
+ :duration => 0.1
276
+ }.update(options)
277
+ end
278
+
279
+ def generate_error options = {}
280
+ error = create_noticed_error options[:error_options] || {}
281
+ payload = create_transaction_payload options[:payload_options] || {}
282
+ @error_event_aggregator.append_event error, payload
283
+ end
284
+
285
+ def generate_errors num_errors
286
+ txn_name = "Controller/blogs/index"
287
+
288
+ in_transaction :transaction_name => txn_name do |t|
289
+ num_errors.times {t.notice_error RuntimeError.new}
290
+ end
291
+ end
292
+ end
293
+ end
294
+ end