exception_handling 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +79 -0
- data/LICENSE +22 -0
- data/README +1 -0
- data/Rakefile +9 -0
- data/config/exception_filters.yml +138 -0
- data/exception_handling.gemspec +24 -0
- data/lib/exception_handling/version.rb +3 -0
- data/lib/exception_handling.rb +630 -0
- data/lib/exception_handling_mailer.rb +77 -0
- data/test/exception_handling_test.rb +923 -0
- data/test/mocha_patch.rb +63 -0
- data/test/test_helper.rb +93 -0
- data/views/exception_handling/mailer/escalation_notification.html.erb +14 -0
- data/views/exception_handling/mailer/exception_notification.html.erb +81 -0
- data/views/exception_handling/mailer/log_parser_exception_notification.html.erb +82 -0
- metadata +177 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'action_mailer'
|
2
|
+
|
3
|
+
module ExceptionHandling
|
4
|
+
class Mailer < ActionMailer::Base
|
5
|
+
default :content_type => "text/html"
|
6
|
+
|
7
|
+
self.append_view_path "#{File.dirname(__FILE__)}/../views"
|
8
|
+
|
9
|
+
[:email_environment, :server_name, :sender_address, :exception_recipients, :escalation_recipients].each do |method|
|
10
|
+
define_method method do
|
11
|
+
ExceptionHandling.send(method) or raise "No #{method} set!"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def email_prefix
|
16
|
+
"#{email_environment} exception: "
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.reloadable?() false end
|
20
|
+
|
21
|
+
def exception_notification( cleaned_data, first_seen_at = nil, occurrences = 0 )
|
22
|
+
if cleaned_data.is_a?(Hash)
|
23
|
+
cleaned_data.merge!({:occurrences => occurrences, :first_seen_at => first_seen_at}) if first_seen_at
|
24
|
+
cleaned_data.merge!({:server => server_name })
|
25
|
+
end
|
26
|
+
|
27
|
+
subject = "#{email_prefix}#{"[#{occurrences} SUMMARIZED]" if first_seen_at}#{cleaned_data[:error]}"[0,300]
|
28
|
+
recipients = exception_recipients
|
29
|
+
from = sender_address
|
30
|
+
@cleaned_data = cleaned_data
|
31
|
+
|
32
|
+
mail(:from => from,
|
33
|
+
:to => recipients,
|
34
|
+
:subject => subject)
|
35
|
+
end
|
36
|
+
|
37
|
+
def escalation_notification( summary, data)
|
38
|
+
subject = "#{email_environment} Escalation: #{summary}"
|
39
|
+
from = sender_address.gsub('xception', 'scalation')
|
40
|
+
recipients = escalation_recipients rescue exception_recipients
|
41
|
+
|
42
|
+
@summary = summary
|
43
|
+
@server = ExceptionHandling.server_name
|
44
|
+
@cleaned_data = data
|
45
|
+
|
46
|
+
mail(:from => from,
|
47
|
+
:to => recipients,
|
48
|
+
:subject => subject)
|
49
|
+
end
|
50
|
+
|
51
|
+
def log_parser_exception_notification( cleaned_data, key )
|
52
|
+
if cleaned_data.is_a?(Hash)
|
53
|
+
cleaned_data = cleaned_data.symbolize_keys
|
54
|
+
local_subject = cleaned_data[:error]
|
55
|
+
else
|
56
|
+
local_subject = "#{key}: #{cleaned_data}"
|
57
|
+
cleaned_data = { :error => cleaned_data.to_s }
|
58
|
+
end
|
59
|
+
|
60
|
+
@subject = "#{email_prefix}#{local_subject}"[0,300]
|
61
|
+
@recipients = exception_recipients
|
62
|
+
from = sender_address
|
63
|
+
@cleaned_data = cleaned_data
|
64
|
+
|
65
|
+
mail(:from => from,
|
66
|
+
:to => @recipients,
|
67
|
+
:subject => @subject)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.mailer_method_category
|
71
|
+
{
|
72
|
+
:exception_notification => :NetworkOptout,
|
73
|
+
:log_parser_exception_notification => :NetworkOptout
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,923 @@
|
|
1
|
+
require './test/test_helper'
|
2
|
+
require 'exception_handling'
|
3
|
+
|
4
|
+
ExceptionHandling.email_environment = 'test'
|
5
|
+
ExceptionHandling.sender_address = 'server@example.com'
|
6
|
+
ExceptionHandling.exception_recipients = 'exceptions@example.com'
|
7
|
+
ExceptionHandling.server_name = 'server'
|
8
|
+
|
9
|
+
class ExceptionHandlingTest < ActiveSupport::TestCase
|
10
|
+
class LoggerStub
|
11
|
+
attr_accessor :logged
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
clear
|
15
|
+
end
|
16
|
+
|
17
|
+
def info(message)
|
18
|
+
logged << message
|
19
|
+
end
|
20
|
+
|
21
|
+
def warn(message)
|
22
|
+
logged << message
|
23
|
+
end
|
24
|
+
|
25
|
+
def fatal(message)
|
26
|
+
logged << message
|
27
|
+
end
|
28
|
+
|
29
|
+
def clear
|
30
|
+
@logged = []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
ExceptionHandling.logger = LoggerStub.new
|
36
|
+
|
37
|
+
def dont_stub_log_error
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
class TestController
|
42
|
+
class Request
|
43
|
+
attr_accessor :parameters, :protocol, :host, :request_uri, :env, :session_options
|
44
|
+
def initialize
|
45
|
+
@parameters = {:id => "1"}
|
46
|
+
@protocol = 'http'
|
47
|
+
@host = 'localhost'
|
48
|
+
@request_uri = "/fun/testing.html?foo=bar"
|
49
|
+
@env = {:HOST => "local"}
|
50
|
+
@session_options = { :id => '93951506217301' }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
attr_accessor :request, :session
|
55
|
+
class << self
|
56
|
+
attr_accessor :around_filter_method
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize
|
60
|
+
@request = Request.new
|
61
|
+
@session_id = "ZKL95"
|
62
|
+
@session =
|
63
|
+
if defined?(Username)
|
64
|
+
{
|
65
|
+
:login_count => 22,
|
66
|
+
:username_id => Username.first.id,
|
67
|
+
:user_id => User.first.id,
|
68
|
+
}
|
69
|
+
else
|
70
|
+
{ }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def simulate_around_filter( &block )
|
75
|
+
set_current_controller( &block )
|
76
|
+
end
|
77
|
+
|
78
|
+
def controller_name
|
79
|
+
"TestController"
|
80
|
+
end
|
81
|
+
|
82
|
+
def action_name
|
83
|
+
"test_action"
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.around_filter( method )
|
87
|
+
TestController.around_filter_method = method
|
88
|
+
end
|
89
|
+
|
90
|
+
def complete_request_uri
|
91
|
+
"#{@request.protocol}#{@request.host}#{@request.request_uri}"
|
92
|
+
end
|
93
|
+
|
94
|
+
include ExceptionHandling::Methods
|
95
|
+
end
|
96
|
+
|
97
|
+
if defined?(Rails)
|
98
|
+
class TestAdvertiser < Advertiser
|
99
|
+
def test_log_error( ex, message=nil )
|
100
|
+
log_error(ex, message)
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_ensure_escalation(summary)
|
104
|
+
ensure_escalation(summary) do
|
105
|
+
yield
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_log_warning( message )
|
110
|
+
log_warning(message)
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_ensure_safe(message="",&blk)
|
114
|
+
ensure_safe(message,&blk)
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.task_exception exception
|
118
|
+
@@task_exception = exception
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
module EventMachineStub
|
124
|
+
class << self
|
125
|
+
attr_accessor :block
|
126
|
+
|
127
|
+
def schedule(&block)
|
128
|
+
@block = block
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class SmtpClientStub
|
134
|
+
class << self
|
135
|
+
attr_reader :block
|
136
|
+
attr_reader :last_method
|
137
|
+
|
138
|
+
def errback(&block)
|
139
|
+
@block = block
|
140
|
+
end
|
141
|
+
|
142
|
+
def send_hash
|
143
|
+
@send_hash ||= {}
|
144
|
+
end
|
145
|
+
|
146
|
+
def send(hash)
|
147
|
+
@last_method = :send
|
148
|
+
send_hash.clear
|
149
|
+
send_hash.merge!(hash)
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
def asend(hash)
|
154
|
+
send(hash)
|
155
|
+
@last_method = :asend
|
156
|
+
self
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
class SmtpClientErrbackStub < SmtpClientStub
|
162
|
+
end
|
163
|
+
|
164
|
+
context "Exception Handling" do
|
165
|
+
setup do
|
166
|
+
ExceptionHandling.send(:clear_exception_summary)
|
167
|
+
end
|
168
|
+
|
169
|
+
context "exception filter parsing and loading" do
|
170
|
+
should "happen without an error" do
|
171
|
+
File.stubs(:mtime).returns( incrementing_mtime )
|
172
|
+
exception_filters = ExceptionHandling.send( :exception_filters )
|
173
|
+
assert( exception_filters.is_a?( ExceptionHandling::ExceptionFilters ) )
|
174
|
+
assert_nothing_raised "Loading the exception filter should not raise" do
|
175
|
+
exception_filters.send :load_file
|
176
|
+
end
|
177
|
+
assert !exception_filters.filtered?( "Scott says unlikely to ever match" )
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context "ExceptionHandling::ensure_safe" do
|
182
|
+
should "log an exception if an exception is raised." do
|
183
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
184
|
+
ExceptionHandling::ensure_safe { raise ArgumentError.new("blah") }
|
185
|
+
end
|
186
|
+
|
187
|
+
should "should not log an exception if an exception is not raised." do
|
188
|
+
ExceptionHandling.logger.expects(:fatal).never
|
189
|
+
ExceptionHandling::ensure_safe { ; }
|
190
|
+
end
|
191
|
+
|
192
|
+
should "return its value if used during an assignment" do
|
193
|
+
ExceptionHandling.logger.expects(:fatal).never
|
194
|
+
b = ExceptionHandling::ensure_safe { 5 }
|
195
|
+
assert_equal 5, b
|
196
|
+
end
|
197
|
+
|
198
|
+
should "return nil if an exception is raised during an assignment" do
|
199
|
+
ExceptionHandling.logger.expects(:fatal).returns(nil)
|
200
|
+
b = ExceptionHandling::ensure_safe { raise ArgumentError.new("blah") }
|
201
|
+
assert_equal nil, b
|
202
|
+
end
|
203
|
+
|
204
|
+
should "allow a message to be appended to the error when logged." do
|
205
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /mooo \(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
206
|
+
b = ExceptionHandling::ensure_safe("mooo") { raise ArgumentError.new("blah") }
|
207
|
+
assert_nil b
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context "ExceptionHandling::ensure_escalation" do
|
212
|
+
should "log the exception as usual and send the proper email" do
|
213
|
+
assert_equal 0, ActionMailer::Base.deliveries.count
|
214
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
215
|
+
ExceptionHandling::ensure_escalation( "Favorite Feature") { raise ArgumentError.new("blah") }
|
216
|
+
assert_equal 2, ActionMailer::Base.deliveries.count
|
217
|
+
email = ActionMailer::Base.deliveries.last
|
218
|
+
assert_equal 'test Escalation: Favorite Feature', email.subject
|
219
|
+
assert_match 'ArgumentError: blah', email.body.to_s
|
220
|
+
assert_match ExceptionHandling.last_exception_timestamp.to_s, email.body.to_s
|
221
|
+
end
|
222
|
+
|
223
|
+
should "should not escalate if an exception is not raised." do
|
224
|
+
assert_equal 0, ActionMailer::Base.deliveries.count
|
225
|
+
ExceptionHandling.logger.expects(:fatal).never
|
226
|
+
ExceptionHandling::ensure_escalation('Ignored') { ; }
|
227
|
+
assert_equal 0, ActionMailer::Base.deliveries.count
|
228
|
+
end
|
229
|
+
|
230
|
+
should "log if the escalation email can not be sent" do
|
231
|
+
Mail::Message.any_instance.expects(:deliver).times(2).returns(nil).then.raises(RuntimeError.new "Delivery Error")
|
232
|
+
exception_count = 0
|
233
|
+
exception_regexs = [/first_test_exception/, /safe_email_deliver .*Delivery Error/]
|
234
|
+
|
235
|
+
$stderr.stubs(:puts)
|
236
|
+
ExceptionHandling.logger.expects(:fatal).times(2).with { |ex| ex =~ exception_regexs[exception_count] or raise "Unexpected [#{exception_count}]: #{ex.inspect}"; exception_count += 1; true }
|
237
|
+
ExceptionHandling::ensure_escalation("Not Used") { raise ArgumentError.new("first_test_exception") }
|
238
|
+
#assert_equal 0, ActionMailer::Base.deliveries.count
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
context "exception timestamp" do
|
243
|
+
setup do
|
244
|
+
Time.now_override = Time.parse( '1986-5-21 4:17 am UTC' )
|
245
|
+
end
|
246
|
+
|
247
|
+
should "include the timestamp when the exception is logged" do
|
248
|
+
ExceptionHandling.logger.expects(:fatal).with { |ex| ex =~ /\(Error:517033020\) ArgumentError mooo \(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
249
|
+
b = ExceptionHandling::ensure_safe("mooo") { raise ArgumentError.new("blah") }
|
250
|
+
assert_nil b
|
251
|
+
|
252
|
+
assert_equal 517033020, ExceptionHandling.last_exception_timestamp
|
253
|
+
|
254
|
+
assert_emails 1
|
255
|
+
assert_match /517033020/, ActionMailer::Base.deliveries[-1].body.to_s
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
if defined?(LogErrorStub)
|
260
|
+
context "while running tests" do
|
261
|
+
setup do
|
262
|
+
stub_log_error
|
263
|
+
end
|
264
|
+
|
265
|
+
should "raise an error when log_error and log_warning are called" do
|
266
|
+
begin
|
267
|
+
ExceptionHandling.log_error("Something happened")
|
268
|
+
flunk
|
269
|
+
rescue LogErrorStub::UnexpectedExceptionLogged => ex
|
270
|
+
assert ex.to_s.starts_with?("StandardError: Something happened"), ex.to_s
|
271
|
+
end
|
272
|
+
|
273
|
+
begin
|
274
|
+
class ::RaisedError < StandardError; end
|
275
|
+
raise ::RaisedError, "This should raise"
|
276
|
+
rescue => ex
|
277
|
+
begin
|
278
|
+
ExceptionHandling.log_error(ex)
|
279
|
+
rescue LogErrorStub::UnexpectedExceptionLogged => ex_inner
|
280
|
+
assert ex_inner.to_s.starts_with?("RaisedError: This should raise"), ex_inner.to_s
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
should "allow for the regex specification of an expected exception to be ignored" do
|
286
|
+
exception_pattern = /StandardError: This is a test error/
|
287
|
+
assert_nil exception_whitelist # test that exception expectations are cleared
|
288
|
+
expects_exception(exception_pattern)
|
289
|
+
assert_equal exception_pattern, exception_whitelist[0][0]
|
290
|
+
begin
|
291
|
+
ExceptionHandling.log_error("This is a test error")
|
292
|
+
rescue => ex
|
293
|
+
flunk # Shouldn't raise an error in this case
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
should "allow for the string specification of an expected exception to be ignored" do
|
298
|
+
exception_pattern = "StandardError: This is a test error"
|
299
|
+
assert_nil exception_whitelist # test that exception expectations are cleared
|
300
|
+
expects_exception(exception_pattern)
|
301
|
+
assert_equal exception_pattern, exception_whitelist[0][0]
|
302
|
+
begin
|
303
|
+
ExceptionHandling.log_error("This is a test error")
|
304
|
+
rescue => ex
|
305
|
+
flunk # Shouldn't raise an error in this case
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
should "allow multiple errors to be ignored" do
|
310
|
+
class IgnoredError < StandardError; end
|
311
|
+
assert_nil exception_whitelist # test that exception expectations are cleared
|
312
|
+
expects_exception /StandardError: This is a test error/
|
313
|
+
expects_exception /IgnoredError: This should be ignored/
|
314
|
+
ExceptionHandling.log_error("This is a test error")
|
315
|
+
begin
|
316
|
+
raise IgnoredError, "This should be ignored"
|
317
|
+
rescue IgnoredError => ex
|
318
|
+
ExceptionHandling.log_error(ex)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
should "expect exception twice if declared twice" do
|
323
|
+
expects_exception /StandardError: ERROR: I love lamp/
|
324
|
+
expects_exception /StandardError: ERROR: I love lamp/
|
325
|
+
ExceptionHandling.log_error("ERROR: I love lamp")
|
326
|
+
ExceptionHandling.log_error("ERROR: I love lamp")
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
should "send just one copy of exceptions that don't repeat" do
|
332
|
+
ExceptionHandling.log_error(exception_1)
|
333
|
+
ExceptionHandling.log_error(exception_2)
|
334
|
+
assert_emails 2
|
335
|
+
assert_match /Exception 1/, ActionMailer::Base.deliveries[-2].subject
|
336
|
+
assert_match /Exception 2/, ActionMailer::Base.deliveries[-1].subject
|
337
|
+
end
|
338
|
+
|
339
|
+
should "only send 5 of a repeated error" do
|
340
|
+
assert_emails 5 do
|
341
|
+
10.times do
|
342
|
+
ExceptionHandling.log_error(exception_1)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
should "only send 5 of a repeated error but don't send summary if 6th is different" do
|
348
|
+
assert_emails 5 do
|
349
|
+
5.times do
|
350
|
+
ExceptionHandling.log_error(exception_1)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
assert_emails 1 do
|
354
|
+
ExceptionHandling.log_error(exception_2)
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
should "send the summary when the error is encountered an hour after the first occurrence" do
|
359
|
+
assert_emails 5 do # 5 exceptions, 4 summarized
|
360
|
+
9.times do |t|
|
361
|
+
ExceptionHandling.log_error(exception_1)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
Time.now_override = 2.hours.from_now
|
365
|
+
assert_emails 1 do # 1 summary (4 + 1 = 5) after 2 hours
|
366
|
+
ExceptionHandling.log_error(exception_1)
|
367
|
+
end
|
368
|
+
assert_match /\[5 SUMMARIZED\]/, ActionMailer::Base.deliveries.last.subject
|
369
|
+
assert_match /This exception occurred 5 times since/, ActionMailer::Base.deliveries.last.body.to_s
|
370
|
+
|
371
|
+
assert_emails 0 do # still summarizing...
|
372
|
+
7.times do
|
373
|
+
ExceptionHandling.log_error(exception_1)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
Time.now_override = 3.hours.from_now
|
378
|
+
|
379
|
+
assert_emails 1 + 2 do # 1 summary and 2 new
|
380
|
+
2.times do
|
381
|
+
ExceptionHandling.log_error(exception_2)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
assert_match /\[7 SUMMARIZED\]/, ActionMailer::Base.deliveries[-3].subject
|
385
|
+
assert_match /This exception occurred 7 times since/, ActionMailer::Base.deliveries[-3].body.to_s
|
386
|
+
end
|
387
|
+
|
388
|
+
should "send the summary if a summary is available, but not sent when another exception comes up" do
|
389
|
+
assert_emails 5 do # 5 to start summarizing
|
390
|
+
6.times do
|
391
|
+
ExceptionHandling.log_error(exception_1)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
assert_emails 1 + 1 do # 1 summary of previous, 1 from new exception
|
396
|
+
ExceptionHandling.log_error(exception_2)
|
397
|
+
end
|
398
|
+
|
399
|
+
assert_match /\[1 SUMMARIZED\]/, ActionMailer::Base.deliveries[-2].subject
|
400
|
+
assert_match /This exception occurred 1 times since/, ActionMailer::Base.deliveries[-2].body.to_s
|
401
|
+
|
402
|
+
assert_emails 5 do # 5 to start summarizing
|
403
|
+
10.times do
|
404
|
+
ExceptionHandling.log_error(exception_1)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
assert_emails 0 do # still summarizing
|
409
|
+
11.times do
|
410
|
+
ExceptionHandling.log_error(exception_1)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
class EventResponse
|
416
|
+
def to_s
|
417
|
+
"message from to_s!"
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
should "allow sections to have data with just a to_s method" do
|
422
|
+
ExceptionHandling.log_error("This is my RingSwitch example. Log, don't email!") do |data|
|
423
|
+
data.merge!(:event_response => EventResponse.new)
|
424
|
+
end
|
425
|
+
assert_emails 1
|
426
|
+
assert_match /message from to_s!/, ActionMailer::Base.deliveries.last.body
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
should "rescue exceptions that happen in log_error" do
|
431
|
+
ExceptionHandling.stubs(:make_exception).raises(ArgumentError.new("Bad argument"))
|
432
|
+
ExceptionHandling.expects(:write_exception_to_log).with do |ex, context, timestamp|
|
433
|
+
ex.to_s['Bad argument'] or raise "Unexpected ex #{ex.class} - #{ex}"
|
434
|
+
context['ExceptionHandling.log_error rescued exception while logging Runtime message'] or raise "Unexpected context #{context}"
|
435
|
+
true
|
436
|
+
end
|
437
|
+
$stderr.stubs(:puts)
|
438
|
+
ExceptionHandling.log_error(RuntimeError.new("A runtime error"), "Runtime message")
|
439
|
+
end
|
440
|
+
|
441
|
+
should "rescue exceptions that happen when log_error yields" do
|
442
|
+
ExceptionHandling.expects(:write_exception_to_log).with do |ex, context, timestamp|
|
443
|
+
ex.to_s['Bad argument'] or raise "=================================================\nUnexpected ex #{ex.class} - #{ex}\n#{ex.backtrace.join("\n")}\n=============================================\n"
|
444
|
+
context['Context message'] or raise "Unexpected context #{context}"
|
445
|
+
true
|
446
|
+
end
|
447
|
+
ExceptionHandling.log_error(ArgumentError.new("Bad argument"), "Context message") { |data| raise 'Error!!!' }
|
448
|
+
end
|
449
|
+
|
450
|
+
context "Exception Filtering" do
|
451
|
+
setup do
|
452
|
+
filter_list = { :exception1 => { :error => "my error message" },
|
453
|
+
:exception2 => { :error => "some other message", :session => "misc data" } }
|
454
|
+
YAML.stubs(:load_file).returns( ActiveSupport::HashWithIndifferentAccess.new( filter_list ) )
|
455
|
+
|
456
|
+
# bump modified time up to get the above filter loaded
|
457
|
+
File.stubs(:mtime).returns( incrementing_mtime )
|
458
|
+
end
|
459
|
+
|
460
|
+
should "handle case where filter list is not found" do
|
461
|
+
YAML.stubs(:load_file).raises( Errno::ENOENT.new( "File not found" ) )
|
462
|
+
|
463
|
+
ExceptionHandling.log_error( "My error message is in list" )
|
464
|
+
assert_emails 1
|
465
|
+
end
|
466
|
+
|
467
|
+
should "log exception and suppress email when exception is on filter list" do
|
468
|
+
ExceptionHandling.log_error( "Error message is not in list" )
|
469
|
+
assert_emails 1
|
470
|
+
|
471
|
+
ActionMailer::Base.deliveries.clear
|
472
|
+
ExceptionHandling.log_error( "My error message is in list" )
|
473
|
+
assert_emails 0
|
474
|
+
end
|
475
|
+
|
476
|
+
should "allow filtering exception on any text in exception data" do
|
477
|
+
filters = { :exception1 => { :session => "^data: my extra session data" } }
|
478
|
+
YAML.stubs(:load_file).returns( ActiveSupport::HashWithIndifferentAccess.new( filters ) )
|
479
|
+
|
480
|
+
ActionMailer::Base.deliveries.clear
|
481
|
+
ExceptionHandling.log_error( "No match here" ) do |data|
|
482
|
+
data[:session] = {
|
483
|
+
:key => "@session_id",
|
484
|
+
:data => "my extra session data"
|
485
|
+
}
|
486
|
+
end
|
487
|
+
assert_emails 0
|
488
|
+
|
489
|
+
ActionMailer::Base.deliveries.clear
|
490
|
+
ExceptionHandling.log_error( "No match here" ) do |data|
|
491
|
+
data[:session] = {
|
492
|
+
:key => "@session_id",
|
493
|
+
:data => "my extra session <no match!> data"
|
494
|
+
}
|
495
|
+
end
|
496
|
+
assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
|
497
|
+
end
|
498
|
+
|
499
|
+
should "reload filter list on the next exception if file was modified" do
|
500
|
+
ExceptionHandling.log_error( "Error message is not in list" )
|
501
|
+
assert_emails 1
|
502
|
+
|
503
|
+
filter_list = { :exception1 => { :error => "Error message is not in list" } }
|
504
|
+
YAML.stubs(:load_file).returns( ActiveSupport::HashWithIndifferentAccess.new( filter_list ) )
|
505
|
+
File.stubs(:mtime).returns( incrementing_mtime )
|
506
|
+
|
507
|
+
ActionMailer::Base.deliveries.clear
|
508
|
+
ExceptionHandling.log_error( "Error message is not in list" )
|
509
|
+
assert_emails 0, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
|
510
|
+
end
|
511
|
+
|
512
|
+
should "not consider filter if both error message and body do not match" do
|
513
|
+
# error message matches, but not full text
|
514
|
+
ExceptionHandling.log_error( "some other message" )
|
515
|
+
assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
|
516
|
+
|
517
|
+
# now both match
|
518
|
+
ActionMailer::Base.deliveries.clear
|
519
|
+
ExceptionHandling.log_error( "some other message" ) do |data|
|
520
|
+
data[:session] = {:some_random_key => "misc data"}
|
521
|
+
end
|
522
|
+
assert_emails 0, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
|
523
|
+
end
|
524
|
+
|
525
|
+
should "skip environment keys not on whitelist" do
|
526
|
+
ExceptionHandling.log_error( "some message" ) do |data|
|
527
|
+
data[:environment] = { :SERVER_PROTOCOL => "HTTP/1.0", :RAILS_SECRETS_YML_CONTENTS => 'password: VERY_SECRET_PASSWORD' }
|
528
|
+
end
|
529
|
+
assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
|
530
|
+
mail = ActionMailer::Base.deliveries.last
|
531
|
+
assert_nil mail.body.to_s["RAILS_SECRETS_YML_CONTENTS"], mail.body.to_s # this is not on whitelist
|
532
|
+
assert mail.body.to_s["SERVER_PROTOCOL: HTTP/1.0" ], mail.body.to_s # this is
|
533
|
+
end
|
534
|
+
|
535
|
+
should "omit environment defaults" do
|
536
|
+
ExceptionHandling.log_error( "some message" ) do |data|
|
537
|
+
data[:environment] = {:SERVER_PORT => '80', :SERVER_PROTOCOL => "HTTP/1.0"}
|
538
|
+
end
|
539
|
+
assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
|
540
|
+
mail = ActionMailer::Base.deliveries.last
|
541
|
+
assert_nil mail.body.to_s["SERVER_PORT" ], mail.body.to_s # this was default
|
542
|
+
assert mail.body.to_s["SERVER_PROTOCOL: HTTP/1.0"], mail.body.to_s # this was not
|
543
|
+
end
|
544
|
+
|
545
|
+
should "reject the filter file if any contain all empty regexes" do
|
546
|
+
filter_list = { :exception1 => { :error => "", :session => "" },
|
547
|
+
:exception2 => { :error => "is not in list", :session => "" } }
|
548
|
+
YAML.stubs(:load_file).returns( ActiveSupport::HashWithIndifferentAccess.new( filter_list ) )
|
549
|
+
File.stubs(:mtime).returns( incrementing_mtime )
|
550
|
+
|
551
|
+
ActionMailer::Base.deliveries.clear
|
552
|
+
ExceptionHandling.log_error( "Error message is not in list" )
|
553
|
+
assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
|
554
|
+
end
|
555
|
+
|
556
|
+
context "Exception Handling Mailer" do
|
557
|
+
should "create email" do
|
558
|
+
ExceptionHandling.log_error(exception_1) do |data|
|
559
|
+
data[:request] = { :params => {:id => 10993}, :url => "www.ringrevenue.com" }
|
560
|
+
data[:session] = { :key => "DECAFE" }
|
561
|
+
end
|
562
|
+
assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
|
563
|
+
assert mail = ActionMailer::Base.deliveries.last
|
564
|
+
assert_equal ['exceptions@example.com'], mail.to
|
565
|
+
assert_equal 'server@example.com', mail.from.to_s
|
566
|
+
assert_match /Exception 1/, mail.to_s
|
567
|
+
assert_match /key: DECAFE/, mail.to_s
|
568
|
+
assert_match /id: 10993/, mail.to_s
|
569
|
+
end
|
570
|
+
|
571
|
+
EXPECTED_SMTP_HASH =
|
572
|
+
{
|
573
|
+
:host => 'localhost',
|
574
|
+
:domain => 'localhost.localdomain',
|
575
|
+
:from => 'server@example.com',
|
576
|
+
:to => 'exceptions@example.com'
|
577
|
+
}
|
578
|
+
|
579
|
+
[true, :Synchrony].each do |synchrony_flag|
|
580
|
+
context "EVENTMACHINE_EXCEPTION_HANDLING = #{synchrony_flag}" do
|
581
|
+
setup do
|
582
|
+
set_test_const('EVENTMACHINE_EXCEPTION_HANDLING', synchrony_flag)
|
583
|
+
EventMachineStub.block = nil
|
584
|
+
set_test_const('EventMachine', EventMachineStub)
|
585
|
+
set_test_const('EventMachine::Protocols', Module.new)
|
586
|
+
end
|
587
|
+
|
588
|
+
should "schedule EventMachine STMP when EventMachine defined" do
|
589
|
+
set_test_const('EventMachine::Protocols::SmtpClient', SmtpClientStub)
|
590
|
+
|
591
|
+
ExceptionHandling.log_error(exception_1)
|
592
|
+
assert EventMachineStub.block
|
593
|
+
EventMachineStub.block.call
|
594
|
+
EXPECTED_SMTP_HASH.map { |key, value| assert_equal value, SmtpClientStub.send_hash[key].to_s, SmtpClientStub.send_hash.inspect }
|
595
|
+
assert_equal (synchrony_flag == :Synchrony ? :asend : :send), SmtpClientStub.last_method
|
596
|
+
assert_match /Exception 1/, SmtpClientStub.send_hash[:body]
|
597
|
+
assert_emails 0, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
|
598
|
+
end
|
599
|
+
|
600
|
+
should "log fatal on EventMachine STMP errback" do
|
601
|
+
set_test_const('EventMachine::Protocols::SmtpClient', SmtpClientErrbackStub)
|
602
|
+
ExceptionHandling.logger.expects(:fatal).twice.with do |message|
|
603
|
+
assert message =~ /Failed to email by SMTP: "credential mismatch"/ || message =~ /Exception 1/, message
|
604
|
+
true
|
605
|
+
end
|
606
|
+
ExceptionHandling.log_error(exception_1)
|
607
|
+
assert EventMachineStub.block
|
608
|
+
EventMachineStub.block.call
|
609
|
+
SmtpClientErrbackStub.block.call("credential mismatch")
|
610
|
+
EXPECTED_SMTP_HASH.map { |key, value| assert_equal value, SmtpClientErrbackStub.send_hash[key].to_s, SmtpClientErrbackStub.send_hash.inspect }
|
611
|
+
assert_emails 0, ActionMailer::Base.deliveries.map { |m| m.body.inspect }
|
612
|
+
end
|
613
|
+
end
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
should "truncate email subject" do
|
618
|
+
text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLM".split('').join("123456789")
|
619
|
+
begin
|
620
|
+
raise text
|
621
|
+
rescue => ex
|
622
|
+
ExceptionHandling.log_error( ex )
|
623
|
+
end
|
624
|
+
assert_emails 1, ActionMailer::Base.deliveries.map { |m| m.inspect }
|
625
|
+
mail = ActionMailer::Base.deliveries.last
|
626
|
+
subject = "test exception: RuntimeError: " + text
|
627
|
+
assert_equal subject[0,300], mail.subject
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
if defined?(Rails)
|
632
|
+
context "ExceptionHandling.Methods" do
|
633
|
+
setup do
|
634
|
+
@controller = TestController.new
|
635
|
+
end
|
636
|
+
|
637
|
+
teardown do
|
638
|
+
Time.now_override = nil
|
639
|
+
end
|
640
|
+
|
641
|
+
should "set the around filter" do
|
642
|
+
assert_equal :set_current_controller, TestController.around_filter_method
|
643
|
+
assert_nil ExceptionHandling.current_controller
|
644
|
+
@controller.simulate_around_filter( ) do
|
645
|
+
assert_equal @controller, ExceptionHandling.current_controller
|
646
|
+
end
|
647
|
+
assert_nil ExceptionHandling.current_controller
|
648
|
+
end
|
649
|
+
|
650
|
+
should "use the current controller when included in a Model" do
|
651
|
+
ActiveRecord::Base.logger.expects(:fatal).with { |ex| ex =~ /blah/ }
|
652
|
+
@controller.simulate_around_filter( ) do
|
653
|
+
a = TestAdvertiser.new :name => 'Joe Ads'
|
654
|
+
a.test_log_error( ArgumentError.new("blah") )
|
655
|
+
mail = ActionMailer::Base.deliveries.last
|
656
|
+
assert_equal EXCEPTION_HANDLING_MAILER_RECIPIENTS, mail.to
|
657
|
+
assert_match( @controller.request.request_uri, mail.body.to_s )
|
658
|
+
assert_match( Username.first.username.to_s, mail.body.to_s )
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
should "use the current_controller when available" do
|
663
|
+
ActiveRecord::Base.logger.expects(:fatal).with( ) { |ex| ex =~ /blah/ }
|
664
|
+
@controller.simulate_around_filter do
|
665
|
+
ExceptionHandling.log_error( ArgumentError.new("blah") )
|
666
|
+
mail = ActionMailer::Base.deliveries.last
|
667
|
+
assert_equal EXCEPTION_HANDLING_MAILER_RECIPIENTS, mail.to
|
668
|
+
assert_match( @controller.request.request_uri, mail.body )
|
669
|
+
assert_match( Username.first.username.to_s, mail.body )
|
670
|
+
mail = ActionMailer::Base.deliveries.last
|
671
|
+
assert_equal EXCEPTION_HANDLING_MAILER_RECIPIENTS, mail.to
|
672
|
+
assert_match( @controller.request.request_uri, mail.body.to_s )
|
673
|
+
assert_match( Username.first.username.to_s, mail.body.to_s )
|
674
|
+
assert mail = ActionMailer::Base.deliveries.last
|
675
|
+
assert_equal EXCEPTION_HANDLING_MAILER_RECIPIENTS, mail.to
|
676
|
+
assert_match( @controller.request.request_uri, mail.body.to_s )
|
677
|
+
assert_match( Username.first.username.to_s, mail.body.to_s )
|
678
|
+
end
|
679
|
+
end
|
680
|
+
|
681
|
+
should "report long running controller action" do
|
682
|
+
# If stubbing this causes problems, retreat.
|
683
|
+
Rails.expects(:env).returns('production')
|
684
|
+
ExceptionHandling.logger.expects(:fatal).with { |ex| ex =~ /Long controller action detected in TestController::test_action/ or raise "Unexpected: #{ex.inspect}"}
|
685
|
+
@controller.simulate_around_filter( ) do
|
686
|
+
Time.now_override = 1.hour.from_now
|
687
|
+
end
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
context "a model object" do
|
692
|
+
setup do
|
693
|
+
@a = TestAdvertiser.new :name => 'Joe Ads'
|
694
|
+
end
|
695
|
+
|
696
|
+
context "with an argument error" do
|
697
|
+
setup do
|
698
|
+
begin
|
699
|
+
raise ArgumentError.new("blah");
|
700
|
+
rescue => ex
|
701
|
+
@argument_error = ex
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
705
|
+
context "log_error on a model" do
|
706
|
+
should "log errors" do
|
707
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
708
|
+
@a.test_log_error( @argument_error )
|
709
|
+
end
|
710
|
+
|
711
|
+
should "log errors from strings" do
|
712
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
713
|
+
@a.test_log_error( "blah" )
|
714
|
+
end
|
715
|
+
|
716
|
+
should "log errors with strings" do
|
717
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /mooo.* \(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
718
|
+
@a.test_log_error( @argument_error, "mooo" )
|
719
|
+
end
|
720
|
+
end
|
721
|
+
|
722
|
+
context "ensure_escalation on a model" do
|
723
|
+
should "work" do
|
724
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
725
|
+
@a.test_ensure_escalation 'Favorite Feature' do
|
726
|
+
raise @argument_error
|
727
|
+
end
|
728
|
+
assert_equal 2, ActionMailer::Base.deliveries.count
|
729
|
+
email = ActionMailer::Base.deliveries.last
|
730
|
+
assert_equal 'development-local Escalation: Favorite Feature', email.subject
|
731
|
+
assert_match 'ArgumentError: blah', email.body.to_s
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
context "ExceptionHandling::log_error" do
|
736
|
+
should "log errors" do
|
737
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
738
|
+
ExceptionHandling::log_error( @argument_error )
|
739
|
+
end
|
740
|
+
|
741
|
+
should "log errors from strings" do
|
742
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
743
|
+
ExceptionHandling::log_error( "blah" )
|
744
|
+
end
|
745
|
+
|
746
|
+
should "log errors with strings" do
|
747
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /mooo.*\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
748
|
+
ExceptionHandling::log_error( @argument_error, "mooo" )
|
749
|
+
end
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
753
|
+
should "log warnings" do
|
754
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /blah/ }
|
755
|
+
@a.test_log_warning("blah")
|
756
|
+
end
|
757
|
+
|
758
|
+
context "ensure_safe on the model" do
|
759
|
+
should "log an exception if an exception is raised." do
|
760
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
761
|
+
@a.test_ensure_safe { raise ArgumentError.new("blah") }
|
762
|
+
end
|
763
|
+
|
764
|
+
should "should not log an exception if an exception is not raised." do
|
765
|
+
ExceptionHandling.logger.expects(:fatal).never
|
766
|
+
@a.test_ensure_safe { ; }
|
767
|
+
end
|
768
|
+
|
769
|
+
should "return its value if used during an assignment" do
|
770
|
+
ExceptionHandling.logger.expects(:fatal).never
|
771
|
+
b = @a.test_ensure_safe { 5 }
|
772
|
+
assert_equal 5, b
|
773
|
+
end
|
774
|
+
|
775
|
+
should "return nil if an exception is raised during an assignment" do
|
776
|
+
ExceptionHandling.logger.expects(:fatal).returns(nil)
|
777
|
+
b = @a.test_ensure_safe { raise ArgumentError.new("blah") }
|
778
|
+
assert_nil b
|
779
|
+
end
|
780
|
+
|
781
|
+
should "allow a message to be appended to the error when logged." do
|
782
|
+
ExceptionHandling.logger.expects(:fatal).with( ) { |ex| ex =~ /mooo.*\(blah\):\n.*exception_handling_test\.rb/ or raise "Unexpected: #{ex.inspect}" }
|
783
|
+
b = @a.test_ensure_safe("mooo") { raise ArgumentError.new("blah") }
|
784
|
+
assert_nil b
|
785
|
+
end
|
786
|
+
end
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
790
|
+
context "Exception mapping" do
|
791
|
+
setup do
|
792
|
+
@data = {
|
793
|
+
:environment=>{
|
794
|
+
'HTTP_HOST' => "localhost",
|
795
|
+
'HTTP_REFERER' => "http://localhost/action/controller/instance",
|
796
|
+
},
|
797
|
+
:session=>{
|
798
|
+
:data=>{
|
799
|
+
:affiliate_id=> defined?(Affiliate) ? Affiliate.first.id : '1',
|
800
|
+
:edit_mode=> true,
|
801
|
+
:advertiser_id=> defined?(Advertiser) ? Advertiser.first.id : '1',
|
802
|
+
:username_id=> defined?(Username) ? Username.first.id : '1',
|
803
|
+
:user_id=> defined?(User) ? User.first.id : '1',
|
804
|
+
:flash=>{},
|
805
|
+
:impersonated_organization_pk=> 'Advertiser_1'
|
806
|
+
}
|
807
|
+
},
|
808
|
+
:request=>{},
|
809
|
+
:backtrace=>["[GEM_ROOT]/gems/actionpack-2.1.0/lib/action_controller/filters.rb:580:in `call_filters'", "[GEM_ROOT]/gems/actionpack-2.1.0/lib/action_controller/filters.rb:601:in `run_before_filters'"],
|
810
|
+
:api_key=>"none",
|
811
|
+
:error_class=>"StandardError",
|
812
|
+
:error=>'Some error message'
|
813
|
+
}
|
814
|
+
end
|
815
|
+
|
816
|
+
should "clean backtraces" do
|
817
|
+
begin
|
818
|
+
raise "test exception"
|
819
|
+
rescue => ex
|
820
|
+
backtrace = ex.backtrace
|
821
|
+
end
|
822
|
+
result = ExceptionHandling.send(:clean_backtrace, ex).to_s
|
823
|
+
assert_not_equal result, backtrace
|
824
|
+
end
|
825
|
+
|
826
|
+
should "clean params" do
|
827
|
+
p = {'password' => 'apple', 'username' => 'sam' }
|
828
|
+
ExceptionHandling.send( :clean_params, p )
|
829
|
+
assert_equal "[FILTERED]", p['password']
|
830
|
+
assert_equal 'sam', p['username']
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
834
|
+
context "log_perodically" do
|
835
|
+
setup do
|
836
|
+
Time.now_override = Time.now # Freeze time
|
837
|
+
ExceptionHandling.logger.clear
|
838
|
+
end
|
839
|
+
|
840
|
+
teardown do
|
841
|
+
Time.now_override = nil
|
842
|
+
end
|
843
|
+
|
844
|
+
should "log immediately when we are expected to log" do
|
845
|
+
logger_stub = ExceptionHandling.logger
|
846
|
+
|
847
|
+
ExceptionHandling.log_periodically(:test_periodic_exception, 30.minutes, "this will be written")
|
848
|
+
assert_equal 1, logger_stub.logged.size
|
849
|
+
|
850
|
+
Time.now_override = Time.now + 5.minutes
|
851
|
+
ExceptionHandling.log_periodically(:test_periodic_exception, 30.minutes, "this will not be written")
|
852
|
+
assert_equal 1, logger_stub.logged.size
|
853
|
+
|
854
|
+
ExceptionHandling.log_periodically(:test_another_periodic_exception, 30.minutes, "this will be written")
|
855
|
+
assert_equal 2, logger_stub.logged.size
|
856
|
+
|
857
|
+
Time.now_override = Time.now + 26.minutes
|
858
|
+
|
859
|
+
ExceptionHandling.log_periodically(:test_periodic_exception, 30.minutes, "this will be written")
|
860
|
+
assert_equal 3, logger_stub.logged.size
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
864
|
+
context "Errplane" do
|
865
|
+
module ErrplaneStub
|
866
|
+
end
|
867
|
+
|
868
|
+
setup do
|
869
|
+
set_test_const('Errplane', ErrplaneStub)
|
870
|
+
end
|
871
|
+
|
872
|
+
should "forward exceptions" do
|
873
|
+
ex = data = nil
|
874
|
+
Errplane.expects(:transmit).with do |ex_, data_|
|
875
|
+
ex, data = ex_, data_
|
876
|
+
true
|
877
|
+
end
|
878
|
+
|
879
|
+
ExceptionHandling.log_error(exception_1, "context")
|
880
|
+
|
881
|
+
assert_equal exception_1, ex, ex.inspect
|
882
|
+
custom_data = data[:custom_data]
|
883
|
+
custom_data["error"].include?("context") or raise "Wrong custom_data #{custom_data["error"].inspect}"
|
884
|
+
end
|
885
|
+
|
886
|
+
should "not forward warnings" do
|
887
|
+
never = true
|
888
|
+
Errplane.stubs(:transmit).with do
|
889
|
+
never = false
|
890
|
+
true
|
891
|
+
end
|
892
|
+
|
893
|
+
ExceptionHandling.log_warning("warning message")
|
894
|
+
|
895
|
+
assert never, "transmit should not have been called"
|
896
|
+
end
|
897
|
+
end
|
898
|
+
|
899
|
+
private
|
900
|
+
|
901
|
+
def incrementing_mtime
|
902
|
+
@mtime ||= Time.now
|
903
|
+
@mtime += 1.day
|
904
|
+
end
|
905
|
+
|
906
|
+
def exception_1
|
907
|
+
@ex1 ||=
|
908
|
+
begin
|
909
|
+
raise StandardError, "Exception 1"
|
910
|
+
rescue => ex
|
911
|
+
ex
|
912
|
+
end
|
913
|
+
end
|
914
|
+
|
915
|
+
def exception_2
|
916
|
+
@ex2 ||=
|
917
|
+
begin
|
918
|
+
raise StandardError, "Exception 2"
|
919
|
+
rescue => ex
|
920
|
+
ex
|
921
|
+
end
|
922
|
+
end
|
923
|
+
end
|