exception_hub 0.0.1
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 +19 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +16 -0
- data/Guardfile +24 -0
- data/LICENSE +22 -0
- data/README.md +43 -0
- data/Rakefile +10 -0
- data/exception_hub.gemspec +23 -0
- data/lib/exception_hub/client/authorization.rb +26 -0
- data/lib/exception_hub/client.rb +26 -0
- data/lib/exception_hub/configuration.rb +81 -0
- data/lib/exception_hub/interceptor.rb +34 -0
- data/lib/exception_hub/issue.rb +25 -0
- data/lib/exception_hub/notifier.rb +64 -0
- data/lib/exception_hub/rack.rb +27 -0
- data/lib/exception_hub/rails/controller_interceptor.rb +21 -0
- data/lib/exception_hub/rails/middleware/exceptions_interceptor.rb +22 -0
- data/lib/exception_hub/railtie.rb +24 -0
- data/lib/exception_hub/rake_task.rb +27 -0
- data/lib/exception_hub/version.rb +3 -0
- data/lib/exception_hub.rb +20 -0
- data/lib/tasks/exception_hub.rake +3 -0
- data/spec/integration/exception_hub_integration/.gitignore +17 -0
- data/spec/integration/exception_hub_integration/Gemfile +40 -0
- data/spec/integration/exception_hub_integration/README.rdoc +261 -0
- data/spec/integration/exception_hub_integration/Rakefile +7 -0
- data/spec/integration/exception_hub_integration/app/assets/images/rails.png +0 -0
- data/spec/integration/exception_hub_integration/app/assets/javascripts/application.js +15 -0
- data/spec/integration/exception_hub_integration/app/assets/stylesheets/application.css +13 -0
- data/spec/integration/exception_hub_integration/app/controllers/application_controller.rb +3 -0
- data/spec/integration/exception_hub_integration/app/controllers/errors_controller.rb +5 -0
- data/spec/integration/exception_hub_integration/app/helpers/application_helper.rb +2 -0
- data/spec/integration/exception_hub_integration/app/mailers/.gitkeep +0 -0
- data/spec/integration/exception_hub_integration/app/models/.gitkeep +0 -0
- data/spec/integration/exception_hub_integration/app/views/layouts/application.html.erb +14 -0
- data/spec/integration/exception_hub_integration/config/application.rb +62 -0
- data/spec/integration/exception_hub_integration/config/boot.rb +6 -0
- data/spec/integration/exception_hub_integration/config/database.yml +25 -0
- data/spec/integration/exception_hub_integration/config/environment.rb +5 -0
- data/spec/integration/exception_hub_integration/config/environments/development.rb +37 -0
- data/spec/integration/exception_hub_integration/config/environments/production.rb +67 -0
- data/spec/integration/exception_hub_integration/config/environments/test.rb +37 -0
- data/spec/integration/exception_hub_integration/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/integration/exception_hub_integration/config/initializers/inflections.rb +15 -0
- data/spec/integration/exception_hub_integration/config/initializers/mime_types.rb +5 -0
- data/spec/integration/exception_hub_integration/config/initializers/secret_token.rb +7 -0
- data/spec/integration/exception_hub_integration/config/initializers/session_store.rb +8 -0
- data/spec/integration/exception_hub_integration/config/initializers/wrap_parameters.rb +14 -0
- data/spec/integration/exception_hub_integration/config/locales/en.yml +5 -0
- data/spec/integration/exception_hub_integration/config/routes.rb +59 -0
- data/spec/integration/exception_hub_integration/config.ru +4 -0
- data/spec/integration/exception_hub_integration/db/seeds.rb +7 -0
- data/spec/integration/exception_hub_integration/lib/assets/.gitkeep +0 -0
- data/spec/integration/exception_hub_integration/lib/tasks/.gitkeep +0 -0
- data/spec/integration/exception_hub_integration/log/.gitkeep +0 -0
- data/spec/integration/exception_hub_integration/public/404.html +26 -0
- data/spec/integration/exception_hub_integration/public/422.html +26 -0
- data/spec/integration/exception_hub_integration/public/500.html +25 -0
- data/spec/integration/exception_hub_integration/public/favicon.ico +0 -0
- data/spec/integration/exception_hub_integration/public/index.html +241 -0
- data/spec/integration/exception_hub_integration/public/robots.txt +5 -0
- data/spec/integration/exception_hub_integration/script/rails +6 -0
- data/spec/integration/exception_hub_integration/test/fixtures/.gitkeep +0 -0
- data/spec/integration/exception_hub_integration/test/functional/.gitkeep +0 -0
- data/spec/integration/exception_hub_integration/test/integration/.gitkeep +0 -0
- data/spec/integration/exception_hub_integration/test/performance/browsing_test.rb +12 -0
- data/spec/integration/exception_hub_integration/test/test_helper.rb +13 -0
- data/spec/integration/exception_hub_integration/test/unit/.gitkeep +0 -0
- data/spec/integration/exception_hub_integration/vendor/assets/javascripts/.gitkeep +0 -0
- data/spec/integration/exception_hub_integration/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/integration/exception_hub_integration/vendor/plugins/.gitkeep +0 -0
- data/spec/integration/rake_task_spec.rb +9 -0
- data/spec/lib/exception_hub/client/authorization_spec.rb +22 -0
- data/spec/lib/exception_hub/client_spec.rb +28 -0
- data/spec/lib/exception_hub/configuration_spec.rb +35 -0
- data/spec/lib/interceptor_spec.rb +112 -0
- data/spec/lib/issue_spec.rb +26 -0
- data/spec/lib/notifier_spec.rb +31 -0
- data/spec/lib/rack_spec.rb +22 -0
- data/spec/spec_helper.rb +16 -0
- metadata +249 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rvm use @exception_hub --create
|
data/Gemfile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
source 'https://rubygems.org'
|
|
2
|
+
|
|
3
|
+
# Specify your gem's dependencies in exception_hub.gemspec
|
|
4
|
+
gemspec
|
|
5
|
+
|
|
6
|
+
gem 'guard'
|
|
7
|
+
gem 'guard-rspec'
|
|
8
|
+
group :osx do
|
|
9
|
+
gem 'growl'
|
|
10
|
+
end
|
|
11
|
+
gem 'yard'
|
|
12
|
+
gem 'octokit'
|
|
13
|
+
gem 'rails', '~> 3.2.6'
|
|
14
|
+
group :test do
|
|
15
|
+
gem 'simplecov', :require => false
|
|
16
|
+
end
|
data/Guardfile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# A sample Guardfile
|
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
|
3
|
+
|
|
4
|
+
guard 'rspec', :version => 2 do
|
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
|
8
|
+
|
|
9
|
+
# Rails example
|
|
10
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
|
11
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
|
12
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
|
13
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
|
14
|
+
watch('config/routes.rb') { "spec/routing" }
|
|
15
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
|
16
|
+
|
|
17
|
+
# Capybara request specs
|
|
18
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
|
|
19
|
+
|
|
20
|
+
# Turnip features and steps
|
|
21
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
|
22
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
|
23
|
+
end
|
|
24
|
+
|
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2012 Jesse Dearing
|
|
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,43 @@
|
|
|
1
|
+
# ExceptionHub
|
|
2
|
+
|
|
3
|
+
ExceptionHub will take your applications exceptions as they are thrown
|
|
4
|
+
and will log them in Github issues.
|
|
5
|
+
|
|
6
|
+
Currently this gem is in alpha and not ready for production use.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Add this line to your application's Gemfile:
|
|
11
|
+
|
|
12
|
+
gem 'exception_hub'
|
|
13
|
+
|
|
14
|
+
And then execute:
|
|
15
|
+
|
|
16
|
+
$ bundle
|
|
17
|
+
|
|
18
|
+
Or install it yourself as:
|
|
19
|
+
|
|
20
|
+
$ gem install exception_hub
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Rails
|
|
25
|
+
|
|
26
|
+
In Rails 3, you can run the rake task to enter in the information needed
|
|
27
|
+
for the initializer and it will fetch the Github token for you.
|
|
28
|
+
|
|
29
|
+
`rake exception_hub:generate_initializer`
|
|
30
|
+
|
|
31
|
+
The initializer needs the following fields:
|
|
32
|
+
* `github_api_token`
|
|
33
|
+
* `github_user_name`
|
|
34
|
+
* `repo_name`
|
|
35
|
+
* `repo_owner`
|
|
36
|
+
|
|
37
|
+
## Contributing
|
|
38
|
+
|
|
39
|
+
1. Fork it
|
|
40
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
41
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
|
42
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
43
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require File.expand_path('../lib/exception_hub/version', __FILE__)
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |gem|
|
|
5
|
+
gem.authors = ["Jesse Dearing"]
|
|
6
|
+
gem.email = ["jesse.dearing@gmail.com"]
|
|
7
|
+
gem.description = %q{exception_hub logs exceptions in your Rails application to Github issues}
|
|
8
|
+
gem.summary = %q{Logs exceptions in your application to Github issues}
|
|
9
|
+
gem.homepage = "https://github.com/jessedearing/exception_hub"
|
|
10
|
+
|
|
11
|
+
gem.files = `git ls-files`.split($\)
|
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
14
|
+
gem.name = "exception_hub"
|
|
15
|
+
gem.require_paths = ["lib"]
|
|
16
|
+
gem.version = ExceptionHub::VERSION
|
|
17
|
+
|
|
18
|
+
gem.add_dependency('octokit', '~> 1.8.1')
|
|
19
|
+
gem.add_dependency('faraday')
|
|
20
|
+
gem.add_dependency('faraday_middleware')
|
|
21
|
+
|
|
22
|
+
gem.add_development_dependency('rspec', '~> 2.10.0')
|
|
23
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'erb'
|
|
2
|
+
|
|
3
|
+
module ExceptionHub
|
|
4
|
+
module Client
|
|
5
|
+
module Authorization
|
|
6
|
+
INITIALIZER_TEMPLATE = File.read(__FILE__).split("__END__\n").last
|
|
7
|
+
|
|
8
|
+
def get_api_token(user, password, note = 'ExceptionHub', url = 'http://github.com/jessedearing/exception_hub')
|
|
9
|
+
o = Octokit::Client.new(:login => user, :password => password)
|
|
10
|
+
o.create_authorization(:scopes => [:repo], :note => note, :note_url => url)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def generate_initializer(user, api_token, auth_id)
|
|
14
|
+
t = ERB.new(INITIALIZER_TEMPLATE)
|
|
15
|
+
t.result binding
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
__END__
|
|
22
|
+
ExceptionHub.configure do |config|
|
|
23
|
+
# Github Authorization #<%= auth_id %>
|
|
24
|
+
config.github_user_name = '<%= user %>'
|
|
25
|
+
config.github_api_token = '<%= api_token %>'
|
|
26
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'exception_hub/client/authorization'
|
|
2
|
+
|
|
3
|
+
module ExceptionHub
|
|
4
|
+
module Client
|
|
5
|
+
include Authorization
|
|
6
|
+
|
|
7
|
+
# @returns [Octokit::Client] Returns an Octokit client singleton
|
|
8
|
+
def current_octokit
|
|
9
|
+
@current_octokit ||= get_octokit_client
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @returns [Octokit::Client] Creates new instance of the Octokit client singleton
|
|
13
|
+
def reload_octokit!
|
|
14
|
+
@current_octokit = get_octokit_client
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
protected
|
|
18
|
+
def get_octokit_client
|
|
19
|
+
if ExceptionHub.github_user_name && ExceptionHub.github_api_token
|
|
20
|
+
Octokit::Client.new(:login => ExceptionHub.github_user_name, :oauth_token => ExceptionHub.github_api_token)
|
|
21
|
+
else
|
|
22
|
+
Octokit::Client.new
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module ExceptionHub
|
|
2
|
+
module Configuration
|
|
3
|
+
IGNORED_EXCEPTIONS_DEFAULT = ['ActiveRecord::RecordNotFound',
|
|
4
|
+
'ActionController::RoutingError',
|
|
5
|
+
'ActionController::InvalidAuthenticityToken',
|
|
6
|
+
'CGI::Session::CookieStore::TamperedWithCookie',
|
|
7
|
+
'ActionController::UnknownAction',
|
|
8
|
+
'AbstractController::ActionNotFound',
|
|
9
|
+
'Mongoid::Errors::DocumentNotFound']
|
|
10
|
+
# @!attribute [rw]
|
|
11
|
+
# @return [String] Name of the Github repository
|
|
12
|
+
attr_accessor :repo_name
|
|
13
|
+
|
|
14
|
+
# @!attribute [rw]
|
|
15
|
+
# @return [String] Github username for the user account that will make the API calls
|
|
16
|
+
attr_accessor :github_user_name
|
|
17
|
+
|
|
18
|
+
# @!attribute [rw]
|
|
19
|
+
# @return [String] Name of the user or organization that hosts the repository
|
|
20
|
+
attr_accessor :repo_owner
|
|
21
|
+
|
|
22
|
+
# @!attribute [rw]
|
|
23
|
+
# @return [Boolean] If true, sends all exceptions to Github including duplicates.
|
|
24
|
+
attr_accessor :send_all_exceptions
|
|
25
|
+
|
|
26
|
+
# @!attribute [rw]
|
|
27
|
+
# @return [String] Github token to make API calls with
|
|
28
|
+
attr_accessor :github_api_token
|
|
29
|
+
|
|
30
|
+
# @!attribute [r]
|
|
31
|
+
# @return [Array<Proc>] Callbacks after the exception is created
|
|
32
|
+
attr_reader :after_create_exception_callbacks
|
|
33
|
+
|
|
34
|
+
# @!attribute [r]
|
|
35
|
+
# @return [Array<Proc>] Callbacks before the exception is created
|
|
36
|
+
attr_reader :before_create_exception_callbacks
|
|
37
|
+
|
|
38
|
+
# @!attribute [rw]
|
|
39
|
+
# @return [Array<String>] Exception types as strings of the message to ignore
|
|
40
|
+
attr_accessor :ignored_exceptions
|
|
41
|
+
|
|
42
|
+
# @!attribute [rw]
|
|
43
|
+
# @return [Array<Symbol>] Environments to send exceptions from
|
|
44
|
+
attr_accessor :reporting_environments
|
|
45
|
+
|
|
46
|
+
# @!attribute [rw]
|
|
47
|
+
# @return [Logger] Logger to send output messages to
|
|
48
|
+
attr_accessor :logger
|
|
49
|
+
|
|
50
|
+
# Provides configuration block for ExceptionHub
|
|
51
|
+
#
|
|
52
|
+
# @example
|
|
53
|
+
# ExceptionHub.configure do |config|
|
|
54
|
+
# config.repo_name = 'exception_hub'
|
|
55
|
+
# # ...
|
|
56
|
+
# end
|
|
57
|
+
# @yield [config] Current instance of ExceptionHub::Configuration
|
|
58
|
+
def configure
|
|
59
|
+
define_defaults
|
|
60
|
+
yield(self) if block_given?
|
|
61
|
+
true
|
|
62
|
+
end
|
|
63
|
+
alias_method :config, :configure
|
|
64
|
+
|
|
65
|
+
def define_defaults
|
|
66
|
+
@after_create_exception_callbacks ||= []
|
|
67
|
+
@before_create_exception_callbacks ||= []
|
|
68
|
+
@ignored_exceptions ||= IGNORED_EXCEPTIONS_DEFAULT.dup
|
|
69
|
+
@reporting_environments ||= [:production]
|
|
70
|
+
@logger ||= defined?(::Rails) && ::Rails.logger || Logger.new(STDOUT)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def after_create_exception(&block)
|
|
74
|
+
@after_create_exception_callbacks << block if block
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def before_create_exception(&block)
|
|
78
|
+
@before_create_exception_callbacks << block if block
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module ExceptionHub
|
|
2
|
+
class Interceptor
|
|
3
|
+
def initialize(exception, rack_env)
|
|
4
|
+
@exception = exception
|
|
5
|
+
@env = rack_env
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def intercept!
|
|
9
|
+
if should_create_issue?
|
|
10
|
+
n = ExceptionHub::Notifier.new(@exception, @env)
|
|
11
|
+
ExceptionHub.before_create_exception_callbacks.each do |callback|
|
|
12
|
+
callback.call(n, @env)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
n.notify!
|
|
16
|
+
|
|
17
|
+
ExceptionHub.after_create_exception_callbacks.each do |callback|
|
|
18
|
+
callback.call(n, @env)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def should_create_issue?
|
|
25
|
+
return false if ExceptionHub.ignored_exceptions.include? @exception.class.name
|
|
26
|
+
|
|
27
|
+
return true if defined?(::Rails) && ExceptionHub.reporting_environments.include?(::Rails.env.to_sym)
|
|
28
|
+
return true if defined?(Sinatra::Base) && ExceptionHub.reporting_environments.include?(Sinatra::Base.environment)
|
|
29
|
+
return true if ENV['RACK_ENV'] && ExceptionHub.reporting_environments.include?(ENV['RACK_ENV'].to_sym)
|
|
30
|
+
#TODO Compare exception to YAML cache here
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module ExceptionHub
|
|
2
|
+
# Issue marshals exception information
|
|
3
|
+
class Issue
|
|
4
|
+
# !@attribute [rw]
|
|
5
|
+
# @return [Integer] The issue ID in Github
|
|
6
|
+
attr_accessor :github_issue_id
|
|
7
|
+
|
|
8
|
+
# !@attribute [rw]
|
|
9
|
+
# @return [String] Title of the Github issue
|
|
10
|
+
attr_accessor :title
|
|
11
|
+
|
|
12
|
+
# !@attribute [rw]
|
|
13
|
+
# @return [String] Description to be displayed in the body of the Github issue
|
|
14
|
+
attr_accessor :description
|
|
15
|
+
|
|
16
|
+
# Creates the current Issue in Github
|
|
17
|
+
def send_to_github
|
|
18
|
+
ExceptionHub.current_octokit.create_issue("#{ExceptionHub.repo_owner}/#{ExceptionHub.repo_name}", self.title, self.description, :open_timeout => 5)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_yaml_properties
|
|
22
|
+
['@github_issue_id', '@title']
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module ExceptionHub
|
|
4
|
+
class Notifier
|
|
5
|
+
def initialize(exception, env)
|
|
6
|
+
@exception = exception
|
|
7
|
+
@env = env
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def notify!
|
|
11
|
+
begin
|
|
12
|
+
issue = Issue.new
|
|
13
|
+
issue.description = build_description
|
|
14
|
+
issue.title = "#{@exception.class.name}: #{@exception.message}"
|
|
15
|
+
|
|
16
|
+
issue.send_to_github
|
|
17
|
+
rescue Exception => ex
|
|
18
|
+
ExceptionHub.logger.error("ExceptionHub: #{ex.class.name}: #{ex.message}")
|
|
19
|
+
ExceptionHub.logger.error(ex.backtrace.reduce("") {|memo, line| memo << line << "\n"})
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
def build_description
|
|
25
|
+
backtrace = if @exception.backtrace
|
|
26
|
+
@exception.backtrace.reduce("") {|memo, line| memo << line << "\n"}
|
|
27
|
+
else
|
|
28
|
+
""
|
|
29
|
+
end
|
|
30
|
+
description = <<-DESC
|
|
31
|
+
## Exception Message
|
|
32
|
+
#{@exception.message}
|
|
33
|
+
|
|
34
|
+
## Data
|
|
35
|
+
### Backtrace
|
|
36
|
+
```
|
|
37
|
+
#{backtrace}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Rack Env
|
|
41
|
+
```
|
|
42
|
+
#{pretty_jsonify(@env)}
|
|
43
|
+
```
|
|
44
|
+
DESC
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def pretty_jsonify(env)
|
|
48
|
+
json = env_to_hash(env)
|
|
49
|
+
JSON.pretty_generate(JSON.parse(json.to_json))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def env_to_hash(env)
|
|
53
|
+
hash = {}
|
|
54
|
+
env.keys do |key|
|
|
55
|
+
if env[key].is_a?(Hash)
|
|
56
|
+
hash[key] = env_to_hash(env[key])
|
|
57
|
+
else
|
|
58
|
+
hash[key] = env[key].to_s
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
hash
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module ExceptionHub
|
|
2
|
+
# Rack middleware to intercept exceptions
|
|
3
|
+
class Rack
|
|
4
|
+
def initialize(app)
|
|
5
|
+
@app = app
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def handle_exception(exception, env)
|
|
9
|
+
ExceptionHub.handle_exception(exception, env)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call(env)
|
|
13
|
+
begin
|
|
14
|
+
response = @app.call(env)
|
|
15
|
+
rescue Exception => ex
|
|
16
|
+
env['exception_hub.issue_id'] = handle_exception(ex, env)
|
|
17
|
+
raise
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if env['rack.exception']
|
|
21
|
+
env['exception_hub.issue_id'] = handle_exception(ex, env)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
response
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module ExceptionHub
|
|
2
|
+
module Rails
|
|
3
|
+
module ContollerInterceptor
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.send(:alias_method, :rescue_action_in_public_without_exception_hub, :rescue_action_in_public)
|
|
6
|
+
base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_exception_hub)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def rescue_action_in_public_with_exception_hub(exception)
|
|
12
|
+
begin
|
|
13
|
+
ExceptionHub.handle_exception(exception, {})
|
|
14
|
+
rescue Exception => ex
|
|
15
|
+
ExceptionHub.logger.error("ExceptionHub: #{ex.class.name}: #{ex.message}")
|
|
16
|
+
ExceptionHub.logger.error(ex.backtrace.reduce("") {|memo, line| memo << line << "\n"})
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module ExceptionHub
|
|
2
|
+
module Rails
|
|
3
|
+
module Middleware
|
|
4
|
+
module ExceptionsInterceptor
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.send(:alias_method_chain, :render_exception, :exception_hub_intercept)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
def render_exception_with_exception_hub_intercept(env, exception)
|
|
11
|
+
begin
|
|
12
|
+
ExceptionHub.handle_exception(exception, env)
|
|
13
|
+
render_exception_without_exception_hub_intercept(env, exception)
|
|
14
|
+
rescue Exception => ex
|
|
15
|
+
ExceptionHub.logger.error("ExceptionHub: #{ex.class.name}: #{ex.message}")
|
|
16
|
+
ExceptionHub.logger.error(ex.backtrace.reduce("") {|memo, line| memo << line << "\n"})
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'exception_hub'
|
|
2
|
+
require 'rails'
|
|
3
|
+
|
|
4
|
+
module ExceptionHub
|
|
5
|
+
class Railtie < Rails::Railtie
|
|
6
|
+
railtie_name :exception_hub
|
|
7
|
+
|
|
8
|
+
initializer "exception_hub rack middleware" do |app|
|
|
9
|
+
app.config.middleware.insert 0, "ExceptionHub::Rack"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
if defined?(::ActionDispatch::DebugExceptions)
|
|
13
|
+
require 'exception_hub/rails/middleware/exceptions_interceptor'
|
|
14
|
+
::ActionDispatch::DebugExceptions.send(:include, ExceptionHub::Rails::Middleware::ExceptionsInterceptor)
|
|
15
|
+
elsif defined(::ActionDispatch::ShowExceptions)
|
|
16
|
+
require 'exception_hub/rails/controller_interceptor'
|
|
17
|
+
::ActionDispatch::ShowExceptions.send(:include, ExceptionHub::Rails::ControllerInterceptor)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
rake_tasks do
|
|
21
|
+
load 'tasks/exception_hub.rake'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'exception_hub'
|
|
2
|
+
|
|
3
|
+
module ExceptionHub
|
|
4
|
+
class RakeTask < ::Rake::TaskLib
|
|
5
|
+
include ::Rake::DSL if defined?(::Rake::DSL)
|
|
6
|
+
|
|
7
|
+
def initialize(*args)
|
|
8
|
+
namespace :exception_hub do
|
|
9
|
+
desc "Generate ExceptionHub initializer"
|
|
10
|
+
task :generate_initializer do
|
|
11
|
+
puts "Github username:"
|
|
12
|
+
username = STDIN.gets.chomp
|
|
13
|
+
puts "Github password:"
|
|
14
|
+
system('stty -echo')
|
|
15
|
+
password = STDIN.gets.chomp
|
|
16
|
+
system('stty echo')
|
|
17
|
+
|
|
18
|
+
puts "Getting authorization from Github"
|
|
19
|
+
auth = ExceptionHub.get_api_token(username, password)
|
|
20
|
+
|
|
21
|
+
puts "Generating initializer"
|
|
22
|
+
File.write('./config/initializers/exception_hub.rb', ExceptionHub.generate_initializer(username, auth.token, auth.id))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'exception_hub/version'
|
|
2
|
+
require 'exception_hub/configuration'
|
|
3
|
+
require 'exception_hub/issue'
|
|
4
|
+
require 'exception_hub/client'
|
|
5
|
+
require 'exception_hub/rack'
|
|
6
|
+
require 'exception_hub/notifier'
|
|
7
|
+
require 'exception_hub/interceptor'
|
|
8
|
+
require 'octokit'
|
|
9
|
+
|
|
10
|
+
require 'exception_hub/railtie' if defined?(Rails)
|
|
11
|
+
|
|
12
|
+
# @author Jesse Dearing <jesse.dearing@gmail.com>
|
|
13
|
+
module ExceptionHub
|
|
14
|
+
extend Configuration
|
|
15
|
+
extend Client
|
|
16
|
+
|
|
17
|
+
def self.handle_exception(exception, env)
|
|
18
|
+
Interceptor.new(exception, env).intercept!
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
|
2
|
+
#
|
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
|
5
|
+
# git config --global core.excludesfile ~/.gitignore_global
|
|
6
|
+
|
|
7
|
+
# Ignore bundler config
|
|
8
|
+
/.bundle
|
|
9
|
+
|
|
10
|
+
# Ignore the default SQLite database.
|
|
11
|
+
/db/*.sqlite3
|
|
12
|
+
|
|
13
|
+
# Ignore all logfiles and tempfiles.
|
|
14
|
+
/log/*.log
|
|
15
|
+
/tmp
|
|
16
|
+
|
|
17
|
+
config/initializers/exception_hub.rb
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
source 'https://rubygems.org'
|
|
2
|
+
|
|
3
|
+
gem 'rails', '3.2.6'
|
|
4
|
+
|
|
5
|
+
# Bundle edge Rails instead:
|
|
6
|
+
# gem 'rails', :git => 'git://github.com/rails/rails.git'
|
|
7
|
+
|
|
8
|
+
gem 'sqlite3'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Gems used only for assets and not required
|
|
12
|
+
# in production environments by default.
|
|
13
|
+
group :assets do
|
|
14
|
+
gem 'sass-rails', '~> 3.2.3'
|
|
15
|
+
gem 'coffee-rails', '~> 3.2.1'
|
|
16
|
+
|
|
17
|
+
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
|
18
|
+
# gem 'therubyracer', :platforms => :ruby
|
|
19
|
+
|
|
20
|
+
gem 'uglifier', '>= 1.0.3'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
gem 'jquery-rails'
|
|
24
|
+
|
|
25
|
+
gem 'exception_hub', :path => File.expand_path('../../../', File.dirname(__FILE__))
|
|
26
|
+
|
|
27
|
+
# To use ActiveModel has_secure_password
|
|
28
|
+
# gem 'bcrypt-ruby', '~> 3.0.0'
|
|
29
|
+
|
|
30
|
+
# To use Jbuilder templates for JSON
|
|
31
|
+
# gem 'jbuilder'
|
|
32
|
+
|
|
33
|
+
# Use unicorn as the app server
|
|
34
|
+
# gem 'unicorn'
|
|
35
|
+
|
|
36
|
+
# Deploy with Capistrano
|
|
37
|
+
# gem 'capistrano'
|
|
38
|
+
|
|
39
|
+
# To use debugger
|
|
40
|
+
# gem 'debugger'
|