failsafe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/.rvmrc +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +9 -0
- data/MIT-LICENSE +21 -0
- data/README.md +64 -0
- data/Rakefile +5 -0
- data/failsafe.gemspec +19 -0
- data/lib/failsafe.rb +47 -0
- data/lib/failsafe/backends/airbrake.rb +11 -0
- data/lib/failsafe/backends/base.rb +15 -0
- data/lib/failsafe/backends/file.rb +18 -0
- data/lib/failsafe/backends/stderr.rb +19 -0
- data/lib/failsafe/failure.rb +8 -0
- data/lib/failsafe/version.rb +4 -0
- data/spec/failsafe/backends/base_spec.rb +7 -0
- data/spec/failsafe/backends/stderr_spec.rb +27 -0
- data/spec/failsafe_spec.rb +60 -0
- data/spec/spec_helper.rb +12 -0
- metadata +69 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3
|
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2012 Alex Sharp
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
[](http://travis-ci.org/zaarly/failsafe)
|
2
|
+
|
3
|
+
## What
|
4
|
+
|
5
|
+
Failsafe is a tiny library that makes it easy to make sure non-critical exceptions
|
6
|
+
don't interrupt program flow. It uses a failure handling system heavily inspired
|
7
|
+
by that of [Resque](http://github.com/defunkt/resque).
|
8
|
+
|
9
|
+
You can use failsafe to suppress exceptions from your users, but continue to deliver
|
10
|
+
them to your errors backends (log files, airbrake, etc).
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
Failsafe.error_backends << Failsafe::Backends::Stderr
|
16
|
+
|
17
|
+
class MyApp < Sinatra::Base
|
18
|
+
get '/' do
|
19
|
+
Failsafe.failsafe { track_some_metrics; raise("boom!") }
|
20
|
+
status(200)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
```
|
24
|
+
|
25
|
+
## Disabling Failsafe
|
26
|
+
|
27
|
+
In production, you want your app to keep ticking along when certain exceptions
|
28
|
+
occur without missing a beat. However, in development and test environments,
|
29
|
+
you probably want to know that exceptions are occurring.
|
30
|
+
Use the `disabled` property for that:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
if Rails.env.test?
|
34
|
+
Failsafe.disabled = true
|
35
|
+
else
|
36
|
+
# not necessary, this is the default setting
|
37
|
+
Failsafe.disabled = false
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
## Configuration
|
42
|
+
|
43
|
+
Failsafe comes with 3 error backends by default: Airbrake, File, and Stderr.
|
44
|
+
You must add them to the failsafe configuration in order to log exceptions
|
45
|
+
to them:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
Failsafe.error_backends << Failsafe::Backends::Airbrake
|
49
|
+
Failsafe.error_backends << Failsafe::Backends::Stderr
|
50
|
+
```
|
51
|
+
|
52
|
+
## Backends
|
53
|
+
|
54
|
+
"Backends" are what your exceptions get handled by instead of letting them
|
55
|
+
bubble up to the user. When an exception occurs within a piece of code wrapped
|
56
|
+
by failsafe, the exception object is handed to each error backend.
|
57
|
+
|
58
|
+
Failsafe ships with three error backends by default:
|
59
|
+
|
60
|
+
* Airbrake - Send errors to airbrake
|
61
|
+
* Stderr - Send errors to stderr
|
62
|
+
* File - Send errors to a log file
|
63
|
+
|
64
|
+
Note: The File backend logs to a log file in the log directory called failsafe_errors.log.
|
data/Rakefile
ADDED
data/failsafe.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "failsafe/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "failsafe"
|
7
|
+
s.version = Failsafe::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Alex Sharp"]
|
10
|
+
s.email = ["ajsharp@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/zaarly/failsafe"
|
12
|
+
s.summary = %q{Tiny little library for silently handling errors so they don't interrupt program flow.}
|
13
|
+
s.description = %q{}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
end
|
data/lib/failsafe.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
require 'failsafe/failure'
|
4
|
+
|
5
|
+
module Failsafe
|
6
|
+
|
7
|
+
# Attach error backends to be fire when errors occur.
|
8
|
+
#
|
9
|
+
# @example Adding multiple backends
|
10
|
+
# Failsafe::Config.error_backends << Failsafe::Backends::Airbrake
|
11
|
+
# Failsafe::Config.error_backends << Failsafe::Backends::Stderr
|
12
|
+
def self.error_backends
|
13
|
+
@backends ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
@@disabled = false
|
17
|
+
|
18
|
+
# Determines if failsafe should rescue errors or let them bubble up.
|
19
|
+
#
|
20
|
+
# @default false
|
21
|
+
# @return [Boolean]
|
22
|
+
def self.disabled; @@disabled; end
|
23
|
+
def self.disabled?; disabled; end
|
24
|
+
|
25
|
+
def self.disabled=(val)
|
26
|
+
@@disabled = val
|
27
|
+
end
|
28
|
+
|
29
|
+
# Wraps code in a begin..rescue and delivers exceptions
|
30
|
+
# to the configured error backends.
|
31
|
+
#
|
32
|
+
# @todo make this threadsafe
|
33
|
+
def self.failsafe
|
34
|
+
# Let errors bubble up if failsafe has been disabled
|
35
|
+
if disabled?
|
36
|
+
return yield
|
37
|
+
end
|
38
|
+
|
39
|
+
begin
|
40
|
+
yield
|
41
|
+
rescue => e
|
42
|
+
error_backends.each do |backend|
|
43
|
+
backend.new(e).save
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Failsafe
|
2
|
+
module Backends
|
3
|
+
|
4
|
+
# File failure backend. Writes exception backtraces to a logfile.
|
5
|
+
class File < Base
|
6
|
+
def self.logger
|
7
|
+
@logger ||= ::Logger.new(::File.join(Rails.root, 'log', 'failsafe_errors.log')).tap { |l| l.formatter = Logger::Formatter.new }
|
8
|
+
end
|
9
|
+
|
10
|
+
def save
|
11
|
+
msg = []
|
12
|
+
msg << exception.message
|
13
|
+
msg << exception.backtrace.join("\n")
|
14
|
+
self.class.logger.error(msg.join)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
module Failsafe
|
3
|
+
module Backends
|
4
|
+
|
5
|
+
# Failure backend to log errors to stderr
|
6
|
+
class Stderr < Base
|
7
|
+
def self.logger
|
8
|
+
@logger ||= ::Logger.new($stderr).tap { |l| l.formatter = Logger::Formatter.new }
|
9
|
+
end
|
10
|
+
|
11
|
+
def save
|
12
|
+
msg = []
|
13
|
+
msg << exception.message
|
14
|
+
msg << exception.backtrace.join("\n")
|
15
|
+
self.class.logger.error(msg.join)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Failsafe::Backends::Stderr do
|
4
|
+
let!(:stderr_bucket) { $stderr }
|
5
|
+
let(:exception) { RuntimeError.new("Oh noez!") }
|
6
|
+
|
7
|
+
subject { Failsafe::Backends::Stderr.new(exception) }
|
8
|
+
|
9
|
+
before do
|
10
|
+
$stderr = StringIO.new
|
11
|
+
Failsafe::Backends::Stderr.stubs(:logger => ::Logger.new($stderr))
|
12
|
+
exception.set_backtrace(["#{__FILE__}:6"])
|
13
|
+
subject.save
|
14
|
+
$stderr.rewind
|
15
|
+
end
|
16
|
+
|
17
|
+
after { $stderr = stderr_bucket }
|
18
|
+
|
19
|
+
it "logs something to stderr when a exception is raised" do
|
20
|
+
$stderr.read.should =~ /Oh noez!/
|
21
|
+
end
|
22
|
+
|
23
|
+
it "logs the backtrace" do
|
24
|
+
$stderr.read.should include "stderr_spec.rb:6"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "configuring error backends" do
|
4
|
+
it "adds the error backend class to the config" do
|
5
|
+
some_backend_class = Struct.new(:save)
|
6
|
+
Failsafe.error_backends << some_backend_class
|
7
|
+
Failsafe.error_backends.should include some_backend_class
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Failsafe, ".failsafe" do
|
12
|
+
class DangerousClass
|
13
|
+
def boom_boom
|
14
|
+
raise RuntimeError.new "room!"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
before { Failsafe.error_backends << MockFailureBackend }
|
19
|
+
let(:danger) { DangerousClass.new }
|
20
|
+
|
21
|
+
subject { Failsafe.failsafe { danger.boom_boom } }
|
22
|
+
|
23
|
+
it "does not raise an error when wrapped with failsafe" do
|
24
|
+
expect {
|
25
|
+
subject
|
26
|
+
}.not_to raise_error
|
27
|
+
end
|
28
|
+
|
29
|
+
it "notifies backends of the exception" do
|
30
|
+
MockFailureBackend.any_instance.expects(:save)
|
31
|
+
subject
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "disabling failsafe" do
|
36
|
+
it "is not disabled by default" do
|
37
|
+
Failsafe.should_not be_disabled
|
38
|
+
end
|
39
|
+
|
40
|
+
context "when false" do
|
41
|
+
before { Failsafe.disabled = false }
|
42
|
+
|
43
|
+
it "allows exception to bubble up" do
|
44
|
+
expect {
|
45
|
+
Failsafe.failsafe { raise 'boom' }
|
46
|
+
}.not_to raise_error
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when true" do
|
51
|
+
before { Failsafe.disabled = true }
|
52
|
+
|
53
|
+
it "allows errors to bubble up" do
|
54
|
+
expect {
|
55
|
+
Failsafe.failsafe { raise 'boom' }
|
56
|
+
}.to raise_error
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$:.push(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
$:.push(File.expand_path(File.dirname(__FILE__)) + '/../lib')
|
3
|
+
|
4
|
+
require 'failsafe'
|
5
|
+
|
6
|
+
class MockFailureBackend < Failsafe::Backends::Base
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.mock_with :mocha
|
11
|
+
config.after { Failsafe.error_backends.clear }
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: failsafe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alex Sharp
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-16 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ''
|
15
|
+
email:
|
16
|
+
- ajsharp@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- .rvmrc
|
23
|
+
- .travis.yml
|
24
|
+
- Gemfile
|
25
|
+
- MIT-LICENSE
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- failsafe.gemspec
|
29
|
+
- lib/failsafe.rb
|
30
|
+
- lib/failsafe/backends/airbrake.rb
|
31
|
+
- lib/failsafe/backends/base.rb
|
32
|
+
- lib/failsafe/backends/file.rb
|
33
|
+
- lib/failsafe/backends/stderr.rb
|
34
|
+
- lib/failsafe/failure.rb
|
35
|
+
- lib/failsafe/version.rb
|
36
|
+
- spec/failsafe/backends/base_spec.rb
|
37
|
+
- spec/failsafe/backends/stderr_spec.rb
|
38
|
+
- spec/failsafe_spec.rb
|
39
|
+
- spec/spec_helper.rb
|
40
|
+
homepage: https://github.com/zaarly/failsafe
|
41
|
+
licenses: []
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.8.15
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Tiny little library for silently handling errors so they don't interrupt
|
64
|
+
program flow.
|
65
|
+
test_files:
|
66
|
+
- spec/failsafe/backends/base_spec.rb
|
67
|
+
- spec/failsafe/backends/stderr_spec.rb
|
68
|
+
- spec/failsafe_spec.rb
|
69
|
+
- spec/spec_helper.rb
|