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.
- checksums.yaml +4 -4
- data/CHANGELOG +20 -0
- data/README.md +1 -13
- data/lib/new_relic/agent/agent.rb +40 -15
- data/lib/new_relic/agent/agent_logger.rb +1 -1
- data/lib/new_relic/agent/configuration/default_source.rb +16 -6
- data/lib/new_relic/agent/configuration/server_source.rb +2 -1
- data/lib/new_relic/agent/error_collector.rb +13 -21
- data/lib/new_relic/agent/error_event_aggregator.rb +124 -0
- data/lib/new_relic/agent/error_trace_aggregator.rb +15 -5
- data/lib/new_relic/agent/instrumentation/middleware_tracing.rb +16 -2
- data/lib/new_relic/agent/new_relic_service.rb +11 -10
- data/lib/new_relic/agent/new_relic_service/json_marshaller.rb +1 -1
- data/lib/new_relic/agent/payload_metric_mapping.rb +58 -0
- data/lib/new_relic/agent/sampled_buffer.rb +15 -1
- data/lib/new_relic/agent/supported_versions.rb +4 -4
- data/lib/new_relic/agent/transaction.rb +52 -44
- data/lib/new_relic/agent/transaction/request_attributes.rb +110 -0
- data/lib/new_relic/agent/transaction_event_aggregator.rb +4 -41
- data/lib/new_relic/noticed_error.rb +25 -9
- data/lib/new_relic/rack/agent_middleware.rb +5 -0
- data/lib/new_relic/version.rb +2 -2
- data/newrelic_rpm.gemspec +6 -0
- data/test/agent_helper.rb +16 -7
- data/test/environments/norails/Gemfile +1 -0
- data/test/multiverse/lib/multiverse/runner.rb +1 -1
- data/test/multiverse/lib/multiverse/suite.rb +6 -3
- data/test/multiverse/suites/agent_only/agent_attributes_test.rb +57 -0
- data/test/multiverse/suites/agent_only/audit_log_test.rb +1 -2
- data/test/multiverse/suites/agent_only/error_events_test.rb +82 -0
- data/test/multiverse/suites/agent_only/harvest_timestamps_test.rb +31 -6
- data/test/multiverse/suites/agent_only/logging_test.rb +2 -2
- data/test/multiverse/suites/agent_only/marshaling_test.rb +6 -0
- data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +2 -2
- data/test/multiverse/suites/agent_only/thread_profiling_test.rb +0 -20
- data/test/multiverse/suites/grape/grape_test.rb +16 -1
- data/test/multiverse/suites/no_json/Envfile +12 -0
- data/test/multiverse/suites/no_json/config/newrelic.yml +27 -0
- data/test/multiverse/suites/no_json/marshal_config_test.rb +22 -0
- data/test/multiverse/suites/rack/example_app.rb +19 -0
- data/test/multiverse/suites/rack/response_content_type_test.rb +50 -0
- data/test/multiverse/suites/rails/error_tracing_test.rb +12 -7
- data/test/multiverse/suites/rails/parameter_capture_test.rb +21 -1
- data/test/multiverse/suites/sinatra/sinatra_error_tracing_test.rb +5 -2
- data/test/multiverse/suites/sinatra/sinatra_parameter_capture_test.rb +16 -1
- data/test/multiverse/suites/sinatra/sinatra_test_cases.rb +4 -2
- data/test/new_relic/agent/agent_logger_test.rb +9 -0
- data/test/new_relic/agent/agent_test.rb +6 -4
- data/test/new_relic/agent/audit_logger_test.rb +0 -7
- data/test/new_relic/agent/error_collector_test.rb +20 -250
- data/test/new_relic/agent/error_event_aggregator_test.rb +294 -0
- data/test/new_relic/agent/error_trace_aggregator_test.rb +273 -5
- data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -2
- data/test/new_relic/agent/new_relic_service_test.rb +8 -32
- data/test/new_relic/agent/payload_metric_mapping_test.rb +74 -0
- data/test/new_relic/agent/pipe_channel_manager_test.rb +6 -5
- data/test/new_relic/agent/sampled_buffer_test.rb +36 -0
- data/test/new_relic/agent/sql_sampler_test.rb +4 -14
- data/test/new_relic/agent/stats_engine/stats_hash_test.rb +5 -4
- data/test/new_relic/agent/threading/thread_profile_test.rb +1 -2
- data/test/new_relic/agent/transaction/request_attributes_test.rb +76 -0
- data/test/new_relic/agent/transaction_event_aggregator_test.rb +12 -1
- data/test/new_relic/agent/transaction_test.rb +60 -11
- data/test/new_relic/data_container_tests.rb +17 -6
- data/test/new_relic/fake_collector.rb +16 -21
- data/test/new_relic/marshalling_test_cases.rb +1 -0
- data/test/new_relic/noticed_error_test.rb +59 -0
- data/test/new_relic/rack/agent_hooks_test.rb +1 -1
- data/test/new_relic/rack/error_collector_test.rb +7 -5
- data/test/performance/suites/error_collector.rb +28 -0
- data/test/performance/suites/marshalling.rb +0 -8
- metadata +14 -3
- 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 =
|
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.
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
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,
|
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
|
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
|
-
|
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
|
601
|
-
|
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
|