crash_hook 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +41 -0
- data/.rspec +3 -0
- data/Gemfile +3 -0
- data/README.md +95 -0
- data/Rakefile +11 -0
- data/crash_hook.gemspec +26 -0
- data/lib/crash_hook.rb +28 -0
- data/lib/crash_hook/configuration.rb +61 -0
- data/lib/crash_hook/crash.rb +60 -0
- data/lib/crash_hook/errors.rb +5 -0
- data/lib/crash_hook/middleware.rb +19 -0
- data/lib/crash_hook/payload.rb +65 -0
- data/lib/crash_hook/request.rb +43 -0
- data/lib/crash_hook/serializer.rb +18 -0
- data/lib/crash_hook/version.rb +3 -0
- data/spec/configuration_spec.rb +30 -0
- data/spec/crash_spec.rb +20 -0
- data/spec/fixtures/stack.txt +1 -0
- data/spec/middleware_spec.rb +30 -0
- data/spec/request_spec.rb +29 -0
- data/spec/spec_helper.rb +40 -0
- metadata +157 -0
data/.gitignore
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
!.gitignore
|
2
|
+
*.gem
|
3
|
+
*.rbc
|
4
|
+
*.sw[a-p]
|
5
|
+
*.tmproj
|
6
|
+
*.tmproject
|
7
|
+
*.un~
|
8
|
+
*~
|
9
|
+
.DS_Store
|
10
|
+
.Spotlight-V100
|
11
|
+
.Trashes
|
12
|
+
._*
|
13
|
+
.bundle
|
14
|
+
.config
|
15
|
+
.directory
|
16
|
+
.elc
|
17
|
+
.redcar
|
18
|
+
.yardoc
|
19
|
+
/.emacs.desktop
|
20
|
+
/.emacs.desktop.lock
|
21
|
+
Desktop.ini
|
22
|
+
Gemfile.lock
|
23
|
+
Icon?
|
24
|
+
InstalledFiles
|
25
|
+
Session.vim
|
26
|
+
Thumbs.db
|
27
|
+
\#*\#
|
28
|
+
_yardoc
|
29
|
+
auto-save-list
|
30
|
+
coverage
|
31
|
+
doc/
|
32
|
+
lib/bundler/man
|
33
|
+
pkg
|
34
|
+
pkg/*
|
35
|
+
rdoc
|
36
|
+
spec/reports
|
37
|
+
test/tmp
|
38
|
+
test/version_tmp
|
39
|
+
tmp
|
40
|
+
tmtags
|
41
|
+
tramp
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# CrashHook
|
2
|
+
|
3
|
+
CrashHook is a rack middleware for handling exception in your rack-based application.
|
4
|
+
|
5
|
+
It sends a http request with exception information, backtrace and environment details to your url via specified method.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
gem install crash_hook
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
### Rails 3
|
14
|
+
|
15
|
+
``` ruby
|
16
|
+
YOUR_RAILS_APP::Application.config.middleware.use CrashHook::Middleware, {
|
17
|
+
:url => 'YOUR_URL'
|
18
|
+
}
|
19
|
+
```
|
20
|
+
|
21
|
+
You can add any exceptions into ignore list:
|
22
|
+
|
23
|
+
``` ruby
|
24
|
+
YOUR_RAILS_APP::Application.config.middleware.use CrashHook::Middleware, {
|
25
|
+
:url => 'YOUR_URL',
|
26
|
+
:ignore => [
|
27
|
+
'NoMethodError'
|
28
|
+
]
|
29
|
+
}
|
30
|
+
```
|
31
|
+
|
32
|
+
### Sinatra
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
errors do
|
36
|
+
CrashHook.notify(request.env['sinatra.error'], request.env)
|
37
|
+
# your stuff, render 500, etc
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
Please note that this will work only in production mode,
|
42
|
+
since in development mode Sinatra is overriding this method with a nice looking exception backtrace page.
|
43
|
+
|
44
|
+
### General
|
45
|
+
|
46
|
+
In other rack apps (config.ru):
|
47
|
+
|
48
|
+
``` ruby
|
49
|
+
require 'crash_hook'
|
50
|
+
|
51
|
+
use CrashHook::Middleware {
|
52
|
+
:url => 'YOUR_URL',
|
53
|
+
:method => :post
|
54
|
+
}
|
55
|
+
```
|
56
|
+
|
57
|
+
## Options
|
58
|
+
|
59
|
+
You can use these options during configuration:
|
60
|
+
|
61
|
+
- :url — Target URL (**required**)
|
62
|
+
- :method — Request method *(default: :post)*
|
63
|
+
- :params — Extra parameters for the request *(ex.: api key, app ID, etc.)*
|
64
|
+
- :format — Report format (:form, :json, :yaml), *(default: json)*
|
65
|
+
- :ignore — Array of exception class names you want to ignore, *(default: [])*
|
66
|
+
- :logger — Set a logger for delivery errors, *(default: nil)*
|
67
|
+
|
68
|
+
## Payload
|
69
|
+
|
70
|
+
Each request has a following structure:
|
71
|
+
|
72
|
+
crash:
|
73
|
+
exception:
|
74
|
+
class_name: "Exception class name"
|
75
|
+
message: "Exception message"
|
76
|
+
backtrace: "Exception backtrace"
|
77
|
+
environment: "rack request environment"
|
78
|
+
framework: "rails"
|
79
|
+
version: "crash_hook version"
|
80
|
+
|
81
|
+
Framework is being auto-detected and could one of the following:
|
82
|
+
|
83
|
+
- rack (default)
|
84
|
+
- rails
|
85
|
+
- sinatra
|
86
|
+
|
87
|
+
## License
|
88
|
+
|
89
|
+
Copyright © 2011 Dan Sosedoff.
|
90
|
+
|
91
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
92
|
+
|
93
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
94
|
+
|
95
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/crash_hook.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/crash_hook/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'crash_hook'
|
6
|
+
gem.version = CrashHook::VERSION.dup
|
7
|
+
gem.author = 'Dan Sosedoff'
|
8
|
+
gem.email = 'dan.sosedoff@gmail.com'
|
9
|
+
gem.homepage = 'https://github.com/sosedoff/crash_hook'
|
10
|
+
gem.summary = %q{Exception notifications via HTTP}
|
11
|
+
gem.description = %q{Rack middleware to notify HTTP endpoint with application notifications}
|
12
|
+
|
13
|
+
gem.files = `git ls-files`.split("\n")
|
14
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
|
16
|
+
gem.require_paths = ['lib']
|
17
|
+
|
18
|
+
gem.add_development_dependency 'rack', '~> 1.2'
|
19
|
+
gem.add_development_dependency 'rake', '~> 0.9'
|
20
|
+
gem.add_development_dependency 'rspec', '~> 2.6'
|
21
|
+
gem.add_development_dependency 'simplecov', '~> 0.4'
|
22
|
+
gem.add_development_dependency 'fakeweb', '~> 1.3'
|
23
|
+
|
24
|
+
gem.add_runtime_dependency 'rest-client', '~> 1.6'
|
25
|
+
gem.add_runtime_dependency 'multi_json', '~> 1.0'
|
26
|
+
end
|
data/lib/crash_hook.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'crash_hook/version'
|
2
|
+
require 'crash_hook/errors'
|
3
|
+
require 'crash_hook/configuration'
|
4
|
+
require 'crash_hook/middleware'
|
5
|
+
require 'crash_hook/request'
|
6
|
+
require 'crash_hook/serializer'
|
7
|
+
require 'crash_hook/payload'
|
8
|
+
require 'crash_hook/crash'
|
9
|
+
|
10
|
+
module CrashHook
|
11
|
+
# Set global configuration
|
12
|
+
#
|
13
|
+
def self.configure(options)
|
14
|
+
@@config = CrashHook::Configuration.new(options)
|
15
|
+
@@config
|
16
|
+
end
|
17
|
+
|
18
|
+
# Manually sent notification
|
19
|
+
# exception => Exception object
|
20
|
+
# env => Environment hash
|
21
|
+
#
|
22
|
+
def self.notify(exception, env)
|
23
|
+
if @@config.nil?
|
24
|
+
raise CrashHook::ConfigurationError, "No configuration were provided."
|
25
|
+
end
|
26
|
+
CrashHook::Crash.new(@@config, exception, env).notify
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module CrashHook
|
4
|
+
@@config = nil
|
5
|
+
|
6
|
+
class Configuration
|
7
|
+
ALLOWED_METHODS = [:get, :post, :put, :delete].freeze
|
8
|
+
ALLOWED_FORMATS = [:form, :json, :yaml]
|
9
|
+
|
10
|
+
attr_reader :url
|
11
|
+
attr_reader :method
|
12
|
+
attr_reader :ignore
|
13
|
+
attr_reader :extra_params
|
14
|
+
attr_reader :format
|
15
|
+
attr_reader :logger
|
16
|
+
|
17
|
+
# Initialize configuration. Options:
|
18
|
+
# :url => Target url (required)
|
19
|
+
# :method => Request method (default: post)
|
20
|
+
# :format => Request format (default: json)
|
21
|
+
# :params => Additional parameters for the request
|
22
|
+
# :ignore => Set of exception classes to ignore
|
23
|
+
# :logger => Set logger class (default: none)
|
24
|
+
#
|
25
|
+
def initialize(options={})
|
26
|
+
if options[:url].to_s.strip.empty?
|
27
|
+
raise CrashHook::ConfigurationError, ":url option required!"
|
28
|
+
end
|
29
|
+
|
30
|
+
@url = options[:url].to_s
|
31
|
+
@method = options.key?(:method) ? options[:method].to_sym : :post
|
32
|
+
@format = options.key?(:format) ? options[:format].to_sym : :json
|
33
|
+
@extra_params = options[:params].kind_of?(Hash) ? options[:params] : {}
|
34
|
+
@logger = options[:logger]
|
35
|
+
|
36
|
+
unless ALLOWED_METHODS.include?(@method)
|
37
|
+
raise CrashHook::ConfigurationError, "#{@method} is not a valid :method option."
|
38
|
+
end
|
39
|
+
|
40
|
+
unless ALLOWED_FORMATS.include?(@format)
|
41
|
+
raise CrashHook::ConfigurationError, "#{@format} is not a valid :format option."
|
42
|
+
end
|
43
|
+
|
44
|
+
@ignore = []
|
45
|
+
@ignore += options[:ignore] if options[:ignore].kind_of?(Array)
|
46
|
+
@ignore.map! { |c| c.to_s }
|
47
|
+
@ignore.uniq!
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns true if specified exception class is ignored
|
51
|
+
#
|
52
|
+
def ignore_exception?(ex)
|
53
|
+
@ignore.include?(ex.to_s)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns true if configuration has a logger
|
57
|
+
def has_logger?
|
58
|
+
!@logger.nil?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
|
3
|
+
module CrashHook
|
4
|
+
class Crash
|
5
|
+
include CrashHook::Request
|
6
|
+
|
7
|
+
# Initialize a new Crash object
|
8
|
+
# config => CrashHook::Configuration object
|
9
|
+
# exception => Exception raised from middleware
|
10
|
+
# env => Environment variable
|
11
|
+
#
|
12
|
+
def initialize(config, exception=nil, env={})
|
13
|
+
unless config.kind_of?(CrashHook::Configuration)
|
14
|
+
raise ArgumentError, "CrashHook::Configuration required!"
|
15
|
+
end
|
16
|
+
|
17
|
+
raise ArgumentError, "Exception required!" if exception.nil?
|
18
|
+
raise ArgumentError, "Environment required!" if env.nil?
|
19
|
+
|
20
|
+
@config = config
|
21
|
+
@payload = CrashHook::Payload.new(exception, env, @config.extra_params)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Send notification to the endpoint
|
25
|
+
def notify
|
26
|
+
begin
|
27
|
+
request(@config.method, @config.url, payload_data, @config.format)
|
28
|
+
true
|
29
|
+
rescue Exception => ex
|
30
|
+
log_error(ex) if @config.has_logger?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Log CrashHook delivery error
|
38
|
+
#
|
39
|
+
def log_error(ex)
|
40
|
+
if @config.logger.respond_to?(:error)
|
41
|
+
@config.logger.error("CrashHook Error: #{ex.inspect}")
|
42
|
+
elsif @config.logger.kind_of?(IO)
|
43
|
+
@config.logger.puts("CrashHook Error: #{ex.inspect}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns data formatted into config's format
|
48
|
+
#
|
49
|
+
def payload_data
|
50
|
+
case @config.format
|
51
|
+
when :json
|
52
|
+
@payload.to_json
|
53
|
+
when :yaml
|
54
|
+
@payload.to_yaml
|
55
|
+
else
|
56
|
+
@payload.to_hash
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CrashHook
|
2
|
+
class Middleware
|
3
|
+
attr_reader :config
|
4
|
+
|
5
|
+
def initialize(app, options={})
|
6
|
+
@app = app
|
7
|
+
@config = CrashHook.configure(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
@app.call(env)
|
12
|
+
rescue Exception => exception
|
13
|
+
unless @config.ignore_exception?(exception.class.to_s)
|
14
|
+
CrashHook::Crash.new(@config, exception, env).notify
|
15
|
+
end
|
16
|
+
raise exception
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module CrashHook
|
5
|
+
class Payload
|
6
|
+
include CrashHook::Serializer
|
7
|
+
|
8
|
+
attr_reader :exception
|
9
|
+
attr_reader :environment
|
10
|
+
attr_reader :framework
|
11
|
+
attr_reader :version
|
12
|
+
attr_reader :extra_params
|
13
|
+
|
14
|
+
# Initialize a new CrashHook::Payload object
|
15
|
+
# exception => Exception object instance
|
16
|
+
# env => Environment hash
|
17
|
+
# extra_params => Additional parameters
|
18
|
+
#
|
19
|
+
def initialize(exception, env, extra_params={})
|
20
|
+
@exception = {
|
21
|
+
:class_name => exception.class.to_s,
|
22
|
+
:message => exception.message,
|
23
|
+
:backtrace => exception.backtrace,
|
24
|
+
:timestamp => Time.now
|
25
|
+
}
|
26
|
+
|
27
|
+
@environment = clean_non_serializable_data(env)
|
28
|
+
@version = CrashHook::VERSION
|
29
|
+
@framework = 'rack'
|
30
|
+
@framework = 'rails' if defined?(Rails)
|
31
|
+
@framework = 'sinatra' if defined?(Sinatra)
|
32
|
+
@extra_params = extra_params.kind_of?(Hash) ? extra_params : {}
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns HASH representation of payload
|
36
|
+
def to_hash
|
37
|
+
{
|
38
|
+
:exception => @exception,
|
39
|
+
:environment => @environment,
|
40
|
+
:version => @version,
|
41
|
+
:framework => @framework
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns JSON representation of payload
|
46
|
+
#
|
47
|
+
def to_json
|
48
|
+
@extra_params.delete(:crash)
|
49
|
+
@extra_params.delete('crash')
|
50
|
+
MultiJson.encode({:crash => self.to_hash}.merge(@extra_params))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns YAML representation of payload
|
54
|
+
#
|
55
|
+
def to_yaml
|
56
|
+
YAML.dump({:crash => self.to_hash})
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns XML representation of payload
|
60
|
+
#
|
61
|
+
def to_xml
|
62
|
+
# Not Implemented Yet
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module CrashHook
|
2
|
+
module Request
|
3
|
+
TIMEOUT = 4
|
4
|
+
OPEN_TIMEOUT = 4
|
5
|
+
|
6
|
+
CONTENT_TYPES = {
|
7
|
+
:form => 'application/x-www-form-urlencoded',
|
8
|
+
:json => 'application/json',
|
9
|
+
:yaml => 'application/x-yaml'
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def get(url, payload={}, format=:json)
|
13
|
+
request(:get, url, payload, format)
|
14
|
+
end
|
15
|
+
|
16
|
+
def post(url, payload={}, format=:json)
|
17
|
+
request(:post, url, payload, format)
|
18
|
+
end
|
19
|
+
|
20
|
+
def put(url, payload={}, format=:json)
|
21
|
+
request(:put, url, payload, format)
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(url, payload={}, format=:json)
|
25
|
+
request(:delete, url, payload, format)
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def request(method, url, payload, format)
|
31
|
+
opts = {
|
32
|
+
:method => method,
|
33
|
+
:url => url,
|
34
|
+
:payload => payload,
|
35
|
+
:headers => {:content_type => CONTENT_TYPES[format]},
|
36
|
+
:timeout => TIMEOUT,
|
37
|
+
:open_timeout => OPEN_TIMEOUT
|
38
|
+
}
|
39
|
+
|
40
|
+
RestClient::Request.execute(opts)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module CrashHook
|
2
|
+
module Serializer
|
3
|
+
def serializable?(value)
|
4
|
+
value.is_a?(Fixnum) ||
|
5
|
+
value.is_a?(Array) ||
|
6
|
+
value.is_a?(String) ||
|
7
|
+
value.is_a?(Hash) ||
|
8
|
+
value.is_a?(Bignum)
|
9
|
+
end
|
10
|
+
|
11
|
+
def clean_non_serializable_data(data)
|
12
|
+
data.select{|k,v| serializable?(v) }.inject({}) do |h, pair|
|
13
|
+
h[pair.first] = pair.last.is_a?(Hash) ? clean_non_serializable_data(pair.last) : pair.last
|
14
|
+
h
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'CrashHook::Configuration' do
|
4
|
+
it 'raises an exception if no url were provided' do
|
5
|
+
proc { CrashHook::Configuration.new }.
|
6
|
+
should raise_error CrashHook::ConfigurationError, ":url option required!"
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'raises an exception on invalid url method' do
|
10
|
+
proc {
|
11
|
+
CrashHook::Configuration.new(
|
12
|
+
:url => 'http://foo.com',
|
13
|
+
:method => :foo
|
14
|
+
)
|
15
|
+
}.should raise_error CrashHook::ConfigurationError, "foo is not a valid :method option."
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'has an unique list of ignored exceptions' do
|
19
|
+
c = CrashHook::Configuration.new(
|
20
|
+
:url => 'http://foo.com',
|
21
|
+
:ignore => [
|
22
|
+
'RuntimeError',
|
23
|
+
RuntimeError
|
24
|
+
]
|
25
|
+
)
|
26
|
+
|
27
|
+
c.ignore.size.should == 1
|
28
|
+
c.ignore_exception?(RuntimeError).should == true
|
29
|
+
end
|
30
|
+
end
|
data/spec/crash_spec.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'CrashHook::Crash' do
|
4
|
+
it 'raises error if no configuration were provided' do
|
5
|
+
proc { CrashHook::Crash.new(nil) }.
|
6
|
+
should raise_error ArgumentError, "CrashHook::Configuration required!"
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'raises error if no exception were provided' do
|
10
|
+
proc { CrashHook::Crash.new(sample_configuration) }.
|
11
|
+
should raise_error ArgumentError, "Exception required!"
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'raises error if no environment configuration were provided' do
|
15
|
+
e = ArgumentError.new("Sample message")
|
16
|
+
|
17
|
+
proc { CrashHook::Crash.new(sample_configuration, e, nil) }.
|
18
|
+
should raise_error ArgumentError, "Environment required!"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
crash[exception]=missing%20something&crash[backtrace][]=%2FUsers%2Fsosedoff%2FProjects%2Fcrash_hook%2Fspec%2Fspec_helper.rb%3A23%3Ain%20%60block%20in%20raise_endpoint'&crash[backtrace][]=%2FUsers%2Fsosedoff%2FProjects%2Fcrash_hook%2Flib%2Fcrash_hook%2Fmiddleware.rb%3A11%3Ain%20%60call'&crash[backtrace][]=%2FUsers%2Fsosedoff%2FProjects%2Fcrash_hook%2Flib%2Fcrash_hook%2Fmiddleware.rb%3A11%3Ain%20%60call'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frack-1.2.3%2Flib%2Frack%2Fbuilder.rb%3A77%3Ain%20%60call'&crash[backtrace][]=%2FUsers%2Fsosedoff%2FProjects%2Fcrash_hook%2Fspec%2Fmiddleware_spec.rb%3A26%3Ain%20%60block%20(2%20levels)%20in%20%3Ctop%20(required)%3E'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fexample.rb%3A48%3Ain%20%60instance_eval'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fexample.rb%3A48%3Ain%20%60block%20in%20run'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fexample.rb%3A107%3Ain%20%60with_around_hooks'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fexample.rb%3A45%3Ain%20%60run'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fexample_group.rb%3A294%3Ain%20%60block%20in%20run_examples'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fexample_group.rb%3A290%3Ain%20%60map'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fexample_group.rb%3A290%3Ain%20%60run_examples'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fexample_group.rb%3A262%3Ain%20%60run'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fcommand_line.rb%3A24%3Ain%20%60block%20(2%20levels)%20in%20run'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fcommand_line.rb%3A24%3Ain%20%60map'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fcommand_line.rb%3A24%3Ain%20%60block%20in%20run'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Freporter.rb%3A12%3Ain%20%60report'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Fcommand_line.rb%3A21%3Ain%20%60run'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Frunner.rb%3A80%3Ain%20%60run_in_process'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Frunner.rb%3A69%3Ain%20%60run'&crash[backtrace][]=%2FUsers%2Fsosedoff%2F.rvm%2Fgems%2Fruby-1.9.2-p180%2Fgems%2Frspec-core-2.6.4%2Flib%2Frspec%2Fcore%2Frunner.rb%3A11%3Ain%20%60block%20in%20autorun'&crash[environment][rack.version][]=1&crash[environment][rack.version][]=1&crash[environment][rack.input]=%23%3CStringIO%3A0x00000100a83fd0%3E&crash[environment][rack.errors]=%23%3CStringIO%3A0x00000100a840e8%3E&crash[environment][rack.multithread]=true&crash[environment][rack.multiprocess]=true&crash[environment][rack.run_once]=false&crash[environment][REQUEST_METHOD]=GET&crash[environment][SERVER_NAME]=example.org&crash[environment][SERVER_PORT]=80&crash[environment][QUERY_STRING]=&crash[environment][PATH_INFO]=%2F&crash[environment][rack.url_scheme]=http&crash[environment][HTTPS]=off&crash[environment][SCRIPT_NAME]=&crash[environment][CONTENT_LENGTH]=0
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'CrashHook::Middleware' do
|
4
|
+
before do
|
5
|
+
@env = Rack::MockRequest.env_for("/")
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'does nothing if no errors occurred' do
|
9
|
+
result = build_stack.call(@env)
|
10
|
+
result.status.should == 200
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'sends notification to the url on exception' do
|
14
|
+
FakeWeb.register_uri(:post, "http://localhost:4567/exception", :body => fixture('stack.txt'))
|
15
|
+
|
16
|
+
proc { build_stack(raise_endpoint(ArgumentError, "missing something")).call(@env) }.
|
17
|
+
should raise_error ArgumentError, "missing something"
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'sends notification in json format' do
|
21
|
+
proc { build_stack(raise_endpoint(ArgumentError, "missing something")).call(@env) }.
|
22
|
+
should raise_error ArgumentError, "missing something"
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'does not produce any errors on invalid http endpoint' do
|
26
|
+
FakeWeb.register_uri(:post, "http://localhost:4567/exception", :body => fixture('stack.txt'), :status => ["404", "Not Found"])
|
27
|
+
proc { build_stack(raise_endpoint(ArgumentError, "missing something")).call(@env) }.
|
28
|
+
should raise_error ArgumentError, "missing something"
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'CrashHook::Request' do
|
4
|
+
class RequestTester
|
5
|
+
extend CrashHook::Request
|
6
|
+
end
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
CrashHook::Configuration::ALLOWED_METHODS.each do |m|
|
10
|
+
FakeWeb.register_uri(m, "http://localhost:4567/test", :body => "")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'sends a GET request' do
|
15
|
+
RequestTester.get('http://localhost:4567/test', {:foo => 'bar'})
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'sends a POST request' do
|
19
|
+
RequestTester.post('http://localhost:4567/test', {:foo => 'bar'})
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'sends a PUT request' do
|
23
|
+
RequestTester.put('http://localhost:4567/test', {:foo => 'bar'})
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'sends a DELETE request' do
|
27
|
+
RequestTester.delete('http://localhost:4567/test', {:foo => 'bar'})
|
28
|
+
end
|
29
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
$:.unshift File.expand_path("../..", __FILE__)
|
2
|
+
|
3
|
+
require 'rack/mock'
|
4
|
+
require 'fakeweb'
|
5
|
+
require 'crash_hook'
|
6
|
+
|
7
|
+
FakeWeb.allow_net_connect = false
|
8
|
+
|
9
|
+
def fixture_path(file=nil)
|
10
|
+
path = File.expand_path("../fixtures", __FILE__)
|
11
|
+
path = File.join(path, file) unless file.nil?
|
12
|
+
path
|
13
|
+
end
|
14
|
+
|
15
|
+
def fixture(file)
|
16
|
+
File.read(File.join(fixture_path, file))
|
17
|
+
end
|
18
|
+
|
19
|
+
def safe_endpoint(msg = "OK")
|
20
|
+
proc { |e| Rack::Response.new(msg) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def raise_endpoint(exception, msg)
|
24
|
+
proc { |e| raise exception, msg }
|
25
|
+
end
|
26
|
+
|
27
|
+
def sample_configuration
|
28
|
+
CrashHook::Configuration.new(
|
29
|
+
:url => 'http://localhost:4567/sample'
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_stack(endpoint = safe_endpoint, options={})
|
34
|
+
options[:url] ||= 'http://localhost:4567/exception'
|
35
|
+
|
36
|
+
Rack::Builder.new do
|
37
|
+
use CrashHook::Middleware, options
|
38
|
+
run endpoint
|
39
|
+
end
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: crash_hook
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.2.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dan Sosedoff
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-07-18 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rack
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "1.2"
|
25
|
+
type: :development
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ~>
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "0.9"
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rspec
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "2.6"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: simplecov
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ~>
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0.4"
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: fakeweb
|
62
|
+
prerelease: false
|
63
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "1.3"
|
69
|
+
type: :development
|
70
|
+
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: rest-client
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ~>
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: "1.6"
|
80
|
+
type: :runtime
|
81
|
+
version_requirements: *id006
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: multi_json
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "1.0"
|
91
|
+
type: :runtime
|
92
|
+
version_requirements: *id007
|
93
|
+
description: Rack middleware to notify HTTP endpoint with application notifications
|
94
|
+
email: dan.sosedoff@gmail.com
|
95
|
+
executables: []
|
96
|
+
|
97
|
+
extensions: []
|
98
|
+
|
99
|
+
extra_rdoc_files: []
|
100
|
+
|
101
|
+
files:
|
102
|
+
- .gitignore
|
103
|
+
- .rspec
|
104
|
+
- Gemfile
|
105
|
+
- README.md
|
106
|
+
- Rakefile
|
107
|
+
- crash_hook.gemspec
|
108
|
+
- lib/crash_hook.rb
|
109
|
+
- lib/crash_hook/configuration.rb
|
110
|
+
- lib/crash_hook/crash.rb
|
111
|
+
- lib/crash_hook/errors.rb
|
112
|
+
- lib/crash_hook/middleware.rb
|
113
|
+
- lib/crash_hook/payload.rb
|
114
|
+
- lib/crash_hook/request.rb
|
115
|
+
- lib/crash_hook/serializer.rb
|
116
|
+
- lib/crash_hook/version.rb
|
117
|
+
- spec/configuration_spec.rb
|
118
|
+
- spec/crash_spec.rb
|
119
|
+
- spec/fixtures/stack.txt
|
120
|
+
- spec/middleware_spec.rb
|
121
|
+
- spec/request_spec.rb
|
122
|
+
- spec/spec_helper.rb
|
123
|
+
has_rdoc: true
|
124
|
+
homepage: https://github.com/sosedoff/crash_hook
|
125
|
+
licenses: []
|
126
|
+
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: "0"
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: "0"
|
144
|
+
requirements: []
|
145
|
+
|
146
|
+
rubyforge_project:
|
147
|
+
rubygems_version: 1.6.2
|
148
|
+
signing_key:
|
149
|
+
specification_version: 3
|
150
|
+
summary: Exception notifications via HTTP
|
151
|
+
test_files:
|
152
|
+
- spec/configuration_spec.rb
|
153
|
+
- spec/crash_spec.rb
|
154
|
+
- spec/fixtures/stack.txt
|
155
|
+
- spec/middleware_spec.rb
|
156
|
+
- spec/request_spec.rb
|
157
|
+
- spec/spec_helper.rb
|