eh 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .idea
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in eh.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ernst van Graan, Wynand van Dyk, Hetzner (Pty) Ltd
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # Eh
2
+
3
+ The Error handler gem provids the following major functional support for error handling:
4
+
5
+ o Wrapping of code blocks in exception handling logic, supporting retry, logging and exception consumption
6
+ o Provision of fault tolerant logging that logs to configured logger(s), or to stderr should loggers be unavailable
7
+ o Capability to handle unhandled exceptions, log them, email them and either raise them again or convert them to system exit codes (listing of standard linux exit codes provided as constants)
8
+
9
+ The Error Handler gem allows the wrapping of arbitrary blocks of code in exception handling logic. Specifically, once an exception occurs:
10
+
11
+ o The block can be retried 'threshold' times, with a delay of 'delay' seconds in-between retries (EH::retry and EH::retry!)
12
+ o The exception can be logged to a single logger, or an array of loggers
13
+ o The excepion can be re-raised (suppressed during retry, but logged, raised again after retry failure)
14
+ o The same functionality is available, with retry disabled (EH::run and EH::run!)
15
+ o The list of Linux system exit codes is also provided as EH::EX_<exit>
16
+ o Unhandled exceptions can be logged and emailed in main() using EH::report_unhandled()
17
+
18
+ This gem is sponsored by Hetzner (Pty) Ltd - http://hetzner.co.za
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'eh'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install eh
33
+
34
+ ## Usage
35
+
36
+ -- Run code block with retry, logging and exception re-raise, specifying logger, number of
37
+ times to retry, retry delay, and block arguments
38
+ def load_configuration(configuration_path, logger = nil)
39
+ EH::retry!(:logger => logger,
40
+ :message => "Could not parse YAML configuration",
41
+ :args => [configuration_path],
42
+ :threshold => 5,
43
+ :delay => 0.5) do
44
+ YAML.load(File.open(configuration_path))
45
+ end
46
+ end
47
+
48
+ -- Run code block with retry only on the exceptions listed, with the default threshold and delay
49
+
50
+ def load_configuration(configuration_path, logger = nil)
51
+ EH::retry!(:args => [configuration_path], :exceptions => [IOError, RuntimeError]) do
52
+ YAML.load(File.open(configuration_path))
53
+ end
54
+ end
55
+
56
+ -- Run code block without retry, and swallowing all exceptions
57
+
58
+ def load_configuration(configuration_path, logger = nil)
59
+ EH::run(:args => [configuration_path]) do
60
+ YAML.load(File.open(configuration_path))
61
+ end
62
+ end
63
+
64
+ Any combination of the following options are allowed, both with both flavours of run() and retry().
65
+
66
+ :logger - log using the logger's error() method
67
+ :message - log this message, with the exception appended
68
+ :args - code block arguments
69
+ :threshold - the number of execution attempts
70
+ :delay - the number of seconds to delay between retries
71
+
72
+
73
+ Function descriptions:
74
+
75
+ run - Execute block, swallow all exceptions, no retry
76
+ run! - Execute block, re-raise exceptions, no retry
77
+ retry - Execute block, swallow all exceptions, retry
78
+ retry! - Execute block, re-raise exceptions, retry
79
+
80
+ Exception filters:
81
+
82
+ When :exception_filter is provided to retry!(), only the exceptions in the filter are retried. If retry fails, all exceptions are re-raised.
83
+ When :exception_filter is provided to retry(), only the exceptions in the filter are retried. If retry fails. Exceptions are not re-raised.
84
+ When :exception_filter is provided to run!(), only the exceptions in the filter are logged, if a logger is provided. All exceptions are re-raised.
85
+ When :exception_filter is provided to run(), only the exceptions in the filter are logged, if a logger is provided. Exceptions are not re-raised.
86
+
87
+ Unhandled exceptions can be logged and emailed as follows:
88
+
89
+ at_exit do
90
+ EH::report_unhandled(logfile = "/var/log/myapp/crash.log", email = "ernst.van.graan@hetzner.co.za")
91
+ exit EH::EX_CONFIG
92
+ end
93
+
94
+ Please send feedback and comments to the authors at:
95
+ Ernst van Graan <ernst.van.graan@hetzner.co.za>
96
+ Wynand van Dyk <wynand.van.dyk@hetzner.co.za>
97
+
98
+ ## Contributing
99
+
100
+ 1. Fork it
101
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
102
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
103
+ 4. Push to the branch (`git push origin my-new-feature`)
104
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/eh.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'eh/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "eh"
8
+ spec.version = ErrorHandler::VERSION
9
+ spec.authors = ["Ernst ban Graan"]
10
+ spec.email = ["ernst.van.graan@hetzner.co.za"]
11
+ spec.description = %q{Error handler gem that allows wrapping of code blocks with support for block retry, logging, exception filtering and re-raise}
12
+ spec.summary = %q{Error handler gem that allows wrapping of code blocks with support for block retry, logging, exception filtering and re-raise}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "simplecov"
25
+ end
data/lib/eh/eh.rb ADDED
@@ -0,0 +1,171 @@
1
+ module ErrorHandler
2
+ class EH
3
+ EX_OK = 0 ; EX_OK.freeze
4
+ EX_BASE = 64; EX_BASE.freeze # base value for error messages
5
+ EX_USAGE = 64; EX_USAGE.freeze # command line usage error
6
+ EX_DATAERR = 65; EX_DATAERR.freeze # data format error
7
+ EX_NOINPUT = 66; EX_NOINPUT.freeze # cannot open input
8
+ EX_NOUSER = 67; EX_NOUSER.freeze # addressee unknown
9
+ EX_NOHOST = 68; EX_NOHOST.freeze # host name unknown
10
+ EX_UNAVAILABLE = 69; EX_UNAVAILABLE.freeze # service unavailable
11
+ EX_SOFTWARE = 70; EX_SOFTWARE.freeze # internal software error
12
+ EX_OSERR = 71; EX_OSERR.freeze # system error (e.g., can't fork)
13
+ EX_OSFILE = 72; EX_OSFILE.freeze # critical OS file missing
14
+ EX_CANTCREAT = 73; EX_CANTCREAT.freeze # /* can't create (user) output file
15
+ EX_IOERR = 74; EX_IOERR.freeze # input/output error
16
+ EX_TEMPFAIL = 75; EX_TEMPFAIL.freeze # temp failure; user is invited to retry
17
+ EX_PROTOCOL = 76; EX_PROTOCOL.freeze # remote error in protocol
18
+ EX_NOPERM = 77; EX_NOPERM.freeze # permission denied
19
+
20
+ ERROR = 'error'
21
+ DEBUG = 'debug'
22
+ INFO = 'info'
23
+ WARN = 'warn'
24
+ FATAL = 'fatal'
25
+
26
+ # Coming soon...
27
+ #def self.report_unhandled(logfile = nil, email = nil)
28
+ # if $!
29
+ # message = "Unhandled exception: #{$!}"
30
+ # warn message
31
+ # if not logfile.nil?
32
+ # open(logfile, 'a') { |f|
33
+ # f.puts message
34
+ # }
35
+ # end
36
+ #
37
+ # if not email.nil?
38
+ # send_email(email, message)
39
+ # end
40
+ # end
41
+ #end
42
+
43
+ def self.retry!(options, &block)
44
+ opts = options || {}
45
+ EH::retry_with_raise(opts, block)
46
+
47
+ rescue => e
48
+ raise e if opts.nil? == false and opts[:exception_filter] and not opts[:exception_filter].include? e.class
49
+
50
+ EH::log(opts[:logger], "#{opts[:message]}: #{e.message}", EH::log_level(opts)) if opts.nil? == false and not opts[:logger].nil? and not opts[:message].nil?
51
+ raise e
52
+ end
53
+
54
+ def self.retry(options, &block)
55
+ opts = options || {}
56
+ begin
57
+ EH::retry_with_raise(opts, block)
58
+ return true
59
+ rescue => e
60
+ if not opts[:logger].nil?
61
+ EH::log(opts[:logger], "#{opts[:message]}: #{e.message}", EH::log_level(opts)) if opts[:exception_filter].nil? or opts[:exception_filter].include? e.class
62
+ end
63
+ return false
64
+ end
65
+ end
66
+
67
+ def self.run!(options, &block)
68
+ opts = options || {}
69
+ block.call(EH::construct_args(opts))
70
+
71
+ rescue => e
72
+ if not opts[:logger].nil?
73
+ EH::log(opts[:logger], "#{opts[:message]}: #{e.message}", EH::log_level(opts)) if opts[:exception_filter].nil? or opts[:exception_filter].include? e.class
74
+ end
75
+
76
+ raise e if opts.nil? == false and opts[:exception_filter] and not opts[:exception_filter].include? e.class
77
+ raise e
78
+ end
79
+
80
+ def self.run(options, &block)
81
+ opts = options || {}
82
+ block.call(EH::construct_args(opts))
83
+
84
+ rescue => e
85
+ if not opts[:logger].nil?
86
+ EH::log(opts[:logger], "#{opts[:message]}: #{e.message}", EH::log_level(opts)) if opts[:exception_filter].nil? or opts[:exception_filter].include? e.class
87
+ end
88
+ end
89
+
90
+ def self.log(facilities, msg, msg_type)
91
+ if facilities.is_a? Array
92
+ EH::log_multiple_loggers(facilities, msg, msg_type)
93
+ else
94
+ EH::log_single_logger(facilities, msg, msg_type)
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ def self.construct_args(opts)
101
+ return nil if opts.nil?
102
+ return nil if opts[:args].nil?
103
+ opts[:args].join(' ')
104
+ end
105
+
106
+ def self.retry_with_raise(opts, block)
107
+ retry_threshold = opts[:threshold] || 3
108
+ delay = opts[:delay] || 0.2
109
+ attempts = 0
110
+ begin
111
+ block.call(EH::construct_args(opts))
112
+ rescue => e
113
+ raise e if opts[:exception_filter] and not opts[:exception_filter].include? e.class
114
+
115
+ attempts += 1
116
+ sleep delay
117
+ retry if attempts < retry_threshold
118
+ raise e
119
+ end
120
+ end
121
+
122
+ # Coming soon...
123
+ # def send_email(to, message, opts={})
124
+ # require 'net/smtp'
125
+ #
126
+ # opts[:server] ||= Socket.gethostbyname(Socket.gethostname).first
127
+ # me = "database-agent@#{opts[:server]}"
128
+ # opts[:from] ||= me
129
+ # opts[:from_alias] ||= me
130
+ # opts[:subject] ||= "Uncaught exception"
131
+ # opts[:body] ||= message
132
+ #
133
+ # msg = <<END_OF_MESSAGE
134
+ #From: #{opts[:from_alias]} <#{opts[:from]}>
135
+ #To: <#{to}>
136
+ #Subject: #{opts[:subject]}
137
+ #
138
+ # #{opts[:body]}
139
+ #END_OF_MESSAGE
140
+ #
141
+ # Net::SMTP.start(opts[:server]) do |smtp|
142
+ # smtp.send_message msg, opts[:from], to
143
+ # end
144
+ # end
145
+
146
+ def self.log_single_logger(logger, msg, msg_type)
147
+ if logger.nil?
148
+ warn msg_type + ': ' + msg
149
+ else
150
+ self.log([logger], msg, msg_type)
151
+ end
152
+ end
153
+
154
+ def self.log_multiple_loggers(loggers, msg, msg_type)
155
+ loggers.each do |logger|
156
+ next if logger.nil?
157
+ logger.warn msg if msg_type == WARN
158
+ logger.debug msg if msg_type == DEBUG
159
+ logger.error msg if msg_type == ERROR
160
+ logger.info msg if msg_type == INFO
161
+ logger.fatal msg if msg_type == FATAL
162
+ end
163
+ end
164
+
165
+ def self.log_level(opts)
166
+ opts[:level] || EH::ERROR
167
+ end
168
+ end
169
+ end
170
+
171
+ EH = ErrorHandler::EH
data/lib/eh/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module ErrorHandler
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "eh/version"
2
+
3
+ module ErrorHandler
4
+ end
data/spec/eh_spec.rb ADDED
@@ -0,0 +1,380 @@
1
+ require "spec_helper"
2
+ require "eh/eh"
3
+ require "mock_logger"
4
+
5
+ describe ErrorHandler do
6
+ before :each do
7
+ @logger = MockLogger.new
8
+ end
9
+
10
+ context "When executing a block of code without retry" do
11
+ describe "run" do
12
+ it "should swallow all exceptions if asked to" do
13
+ begin
14
+ exception = nil
15
+ EH::run(nil) do
16
+ raise RuntimeError
17
+ end
18
+ rescue => e
19
+ exception = e
20
+ end
21
+ exception.nil?.should == true
22
+ end
23
+
24
+ it "should not retry" do
25
+ begin
26
+ count = 0
27
+ EH::run(:exception_filter => [RuntimeError], :args => [count]) do
28
+ count += 1
29
+ raise RuntimeError
30
+ end
31
+ rescue => e
32
+ end
33
+ count.should == 1
34
+ end
35
+
36
+ it "should log the message specified with the exception appended, using the logger specified" do
37
+ @logger.should_receive(:error).with("the message: RuntimeError")
38
+ begin
39
+ EH::run(:logger => @logger, :message => "the message") do
40
+ raise RuntimeError
41
+ end
42
+ end
43
+ end
44
+
45
+ it "should log the message specified with the exception appended, if the exception is in :exception_filter, using the logger specified" do
46
+ @logger.should_receive(:error).with("the message: RuntimeError")
47
+ begin
48
+ EH::run(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
49
+ raise RuntimeError
50
+ end
51
+ end
52
+ end
53
+
54
+ it "should not log, if :exception_filter is specified and the exception is not in :exception_filter" do
55
+ @logger.should_not_receive(:error)
56
+ begin
57
+ EH::run(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
58
+ raise IOError
59
+ end
60
+ end
61
+ end
62
+
63
+ it "should log using the level specified" do
64
+ @logger.should_receive(:warn).with("the message: RuntimeError")
65
+ begin
66
+ EH::run(:logger => @logger, :message => "the message", :level => EH::WARN) do
67
+ raise RuntimeError
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "run!" do
74
+ it "should re-raise all exceptions if asked to" do
75
+ begin
76
+ exception = nil
77
+ EH::run!(nil) do
78
+ raise RuntimeError
79
+ end
80
+ rescue => e
81
+ exception = e
82
+ end
83
+ exception.class.should == RuntimeError
84
+ end
85
+
86
+ it "should not retry" do
87
+ begin
88
+ count = 0
89
+ EH::run!(:exception_filter => [RuntimeError], :args => [count]) do
90
+ count += 1
91
+ raise RuntimeError
92
+ end
93
+ rescue => e
94
+ end
95
+ count.should == 1
96
+ end
97
+
98
+ it "should log the message specified with the exception appended, using the logger specified" do
99
+ @logger.should_receive(:error).with("the message: RuntimeError")
100
+ begin
101
+ EH::run!(:logger => @logger, :message => "the message") do
102
+ raise RuntimeError
103
+ end
104
+ rescue => e
105
+ end
106
+ end
107
+
108
+ it "should log the message specified with the exception appended, if the exception is in :exception_filter, using the logger specified" do
109
+ @logger.should_receive(:error).with("the message: RuntimeError")
110
+ begin
111
+ EH::run!(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
112
+ raise RuntimeError
113
+ end
114
+ rescue => e
115
+ end
116
+ end
117
+
118
+ it "should not log, if :exception_filter is specified and the exception is not in :exception_filter" do
119
+ @logger.should_not_receive(:error)
120
+ begin
121
+ EH::run!(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
122
+ raise IOError
123
+ end
124
+ rescue => e
125
+ end
126
+ end
127
+
128
+ it "should log using the level specified" do
129
+ @logger.should_receive(:warn).with("the message: RuntimeError")
130
+ begin
131
+ EH::run!(:logger => @logger, :message => "the message", :level => EH::WARN) do
132
+ raise RuntimeError
133
+ end
134
+ rescue => e
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ context "When executing a block of code with retry" do
141
+ describe "retry" do
142
+ it "should swallow all exceptions if asked to" do
143
+ begin
144
+ exception = nil
145
+ EH::retry(nil) do
146
+ raise RuntimeError
147
+ end
148
+ rescue => e
149
+ exception = e
150
+ end
151
+ exception.nil?.should == true
152
+ end
153
+
154
+ it "should retry" do
155
+ begin
156
+ count = 0
157
+ EH::retry(:exception_filter => [RuntimeError], :args => [count]) do
158
+ count += 1
159
+ raise RuntimeError
160
+ end
161
+ end
162
+ count.should == 3
163
+ end
164
+
165
+ it "should return true if the code succeeds after retry" do
166
+ begin
167
+ count = 0
168
+ result = EH::retry(:exception_filter => [RuntimeError], :args => [count]) do
169
+ count += 1
170
+ raise RuntimeError if count < 2
171
+ end
172
+ end
173
+ result.should == true
174
+ end
175
+
176
+ it "should return false if the code does not succeed after retry" do
177
+ begin
178
+ result = EH::retry(:exception_filter => [RuntimeError]) do
179
+ raise RuntimeError
180
+ end
181
+ end
182
+ result.should == false
183
+ end
184
+
185
+ it "should attempt the number of retries specified in :threshold" do
186
+ begin
187
+ count = 0
188
+ EH::retry(:exception_filter => [RuntimeError], :args => [count], :threshold => 5) do
189
+ count += 1
190
+ raise RuntimeError
191
+ end
192
+ end
193
+ count.should == 5
194
+ end
195
+
196
+ it "should delay between intervals as specified in :delay" do
197
+ begin
198
+ pre = Time.now
199
+ EH::retry(:exception_filter => [RuntimeError], :delay => 0.1, :threshold => 6) do
200
+ raise RuntimeError
201
+ end
202
+ end
203
+ post = Time.now
204
+ (post - pre > 0.6).should == true
205
+ end
206
+
207
+ it "should log the message specified with the exception appended, using the logger specified" do
208
+ @logger.should_receive(:error).with("the message: RuntimeError")
209
+ begin
210
+ EH::retry(:logger => @logger, :message => "the message") do
211
+ raise RuntimeError
212
+ end
213
+ end
214
+ end
215
+
216
+ it "should log the message specified with the exception appended, if the exception is in :exception_filter, using the logger specified" do
217
+ @logger.should_receive(:error).with("the message: RuntimeError")
218
+ begin
219
+ EH::retry(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
220
+ raise RuntimeError
221
+ end
222
+ end
223
+ end
224
+
225
+ it "should not log, if :exception_filter is specified and the exception is not in :exception_filter" do
226
+ @logger.should_not_receive(:error)
227
+ begin
228
+ EH::retry(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
229
+ raise IOError
230
+ end
231
+ end
232
+ end
233
+
234
+ it "should log using the level specified" do
235
+ @logger.should_receive(:warn).with("the message: RuntimeError")
236
+ begin
237
+ EH::retry(:logger => @logger, :message => "the message", :level => EH::WARN) do
238
+ raise RuntimeError
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ describe "retry!" do
245
+ it "should re-raise all exceptions if asked to" do
246
+ begin
247
+ exception = nil
248
+ EH::retry!(nil) do
249
+ raise RuntimeError
250
+ end
251
+ rescue => e
252
+ exception = e
253
+ end
254
+ exception.class.should == RuntimeError
255
+ end
256
+
257
+ it "should retry" do
258
+ begin
259
+ count = 0
260
+ EH::retry!(:exception_filter => [RuntimeError], :args => [count]) do
261
+ count += 1
262
+ raise RuntimeError
263
+ end
264
+ rescue => e
265
+ end
266
+ count.should == 3
267
+ end
268
+
269
+ it "should attempt the number of retries specified in :threshold" do
270
+ begin
271
+ count = 0
272
+ EH::retry(:exception_filter => [RuntimeError], :args => [count], :threshold => 5) do
273
+ count += 1
274
+ raise RuntimeError
275
+ end
276
+ rescue => e
277
+ end
278
+ count.should == 5
279
+ end
280
+
281
+ it "should delay between intervals as specified in :delay" do
282
+ begin
283
+ pre = Time.now
284
+ EH::retry(:exception_filter => [RuntimeError], :delay => 0.1, :threshold => 6) do
285
+ raise RuntimeError
286
+ end
287
+ rescue => e
288
+ end
289
+ post = Time.now
290
+ (post - pre > 0.6).should == true
291
+ end
292
+
293
+ it "should log the message specified with the exception appended, using the logger specified" do
294
+ @logger.should_receive(:error).with("the message: RuntimeError")
295
+ begin
296
+ EH::retry!(:logger => @logger, :message => "the message") do
297
+ raise RuntimeError
298
+ end
299
+ rescue => e
300
+ end
301
+ end
302
+
303
+ it "should log the message specified with the exception appended, if the exception is in :exception_filter, using the logger specified" do
304
+ @logger.should_receive(:error).with("the message: RuntimeError")
305
+ begin
306
+ EH::retry!(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
307
+ raise RuntimeError
308
+ end
309
+ rescue => e
310
+ end
311
+ end
312
+
313
+ it "should not log, if :exception_filter is specified and the exception is not in :exception_filter" do
314
+ @logger.should_not_receive(:error)
315
+ begin
316
+ EH::retry!(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
317
+ raise IOError
318
+ end
319
+ rescue => e
320
+ end
321
+ end
322
+
323
+ it "should log using the level specified" do
324
+ @logger.should_receive(:warn).with("the message: RuntimeError")
325
+ begin
326
+ EH::retry!(:logger => @logger, :message => "the message", :level => EH::WARN) do
327
+ raise RuntimeError
328
+ end
329
+ rescue => e
330
+ end
331
+ end
332
+ end
333
+ end
334
+
335
+ context "when asked to log" do
336
+ it "should log to a single logger provided, with the message specified, at the level specified" do
337
+ @logger.should_receive(:info).with("testing single logging")
338
+ EH::log(@logger, "testing single logging", EH::INFO)
339
+ end
340
+
341
+ it "should log to a single logger provided, with the message specified, at the level specified" do
342
+ @logger2 = MockLogger.new
343
+
344
+ @logger.should_receive(:debug).with("testing single logging")
345
+ @logger2.should_receive(:debug).with("testing single logging")
346
+ EH::log([@logger, @logger2], "testing single logging", EH::DEBUG)
347
+ end
348
+
349
+ it "should log at all log levels" do
350
+ @logger.should_receive(:info)
351
+ EH::log(@logger, "info", EH::INFO)
352
+
353
+ @logger.should_receive(:debug)
354
+ EH::log(@logger, "debug", EH::DEBUG)
355
+
356
+ @logger.should_receive(:error)
357
+ EH::log(@logger, "error", EH::ERROR)
358
+
359
+ @logger.should_receive(:warn)
360
+ EH::log(@logger, "warn", EH::WARN)
361
+
362
+ @logger.should_receive(:fatal)
363
+ EH::log(@logger, "fatal", EH::FATAL)
364
+ end
365
+
366
+ it "should log using 'warn' if a nil single logger is provided" do
367
+ EH::should_receive(:warn).with "fatal: fatal"
368
+
369
+ EH::log(nil, "fatal", EH::FATAL)
370
+ end
371
+
372
+ it "should not log if nil is provided in a list of loggers, and 'warn' should not be called" do
373
+ @logger::should_receive(:fatal).once().with "fatal"
374
+ EH::should_not_receive(:warn)
375
+
376
+ EH::log([nil, @logger], "fatal", EH::FATAL)
377
+ end
378
+ end
379
+
380
+ end
@@ -0,0 +1,16 @@
1
+ class MockLogger
2
+ def error(message)
3
+ end
4
+
5
+ def info(message)
6
+ end
7
+
8
+ def debug(message)
9
+ end
10
+
11
+ def warn(message)
12
+ end
13
+
14
+ def fatal(message)
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eh
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ernst ban Graan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: simplecov
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Error handler gem that allows wrapping of code blocks with support for
79
+ block retry, logging, exception filtering and re-raise
80
+ email:
81
+ - ernst.van.graan@hetzner.co.za
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - eh.gemspec
92
+ - lib/eh/eh.rb
93
+ - lib/eh/version.rb
94
+ - lib/error_handler.rb
95
+ - spec/eh_spec.rb
96
+ - spec/mock_logger.rb
97
+ homepage: ''
98
+ licenses:
99
+ - MIT
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ segments:
111
+ - 0
112
+ hash: 798203344795618863
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ segments:
120
+ - 0
121
+ hash: 798203344795618863
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 1.8.24
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: Error handler gem that allows wrapping of code blocks with support for block
128
+ retry, logging, exception filtering and re-raise
129
+ test_files:
130
+ - spec/eh_spec.rb
131
+ - spec/mock_logger.rb