exception_hub 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|