rails_exception_handler 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +11 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +91 -0
- data/LICENCE +20 -0
- data/README.markdown +192 -0
- data/Rakefile +31 -0
- data/VERSION +1 -0
- data/app/controllers/error_response_controller.rb +6 -0
- data/app/models/error_message.rb +3 -0
- data/lib/patch/show_exceptions.rb +14 -0
- data/lib/rails_exception_handler/configuration.rb +13 -0
- data/lib/rails_exception_handler/handler.rb +69 -0
- data/lib/rails_exception_handler/parser.rb +73 -0
- data/lib/rails_exception_handler.rb +37 -0
- data/rails_exception_handler.gemspec +87 -0
- data/spec/integration/configuration_spec.rb +161 -0
- data/spec/integration/rails_exception_handler_spec.rb +67 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/test_macros.rb +61 -0
- data/spec/testapp_30/.gitignore +4 -0
- data/spec/testapp_30/Gemfile +31 -0
- data/spec/testapp_30/Gemfile.lock +75 -0
- data/spec/testapp_30/Rakefile +7 -0
- data/spec/testapp_30/app/controllers/application_controller.rb +12 -0
- data/spec/testapp_30/app/controllers/home_controller.rb +22 -0
- data/spec/testapp_30/app/helpers/application_helper.rb +2 -0
- data/spec/testapp_30/app/models/stored_exception.rb +9 -0
- data/spec/testapp_30/app/views/home/view_error.html.erb +2 -0
- data/spec/testapp_30/app/views/layouts/fallback.html.erb +15 -0
- data/spec/testapp_30/app/views/layouts/home.html.erb +15 -0
- data/spec/testapp_30/config/application.rb +44 -0
- data/spec/testapp_30/config/boot.rb +6 -0
- data/spec/testapp_30/config/database.yml +12 -0
- data/spec/testapp_30/config/environment.rb +5 -0
- data/spec/testapp_30/config/environments/development.rb +26 -0
- data/spec/testapp_30/config/environments/production.rb +49 -0
- data/spec/testapp_30/config/environments/test.rb +35 -0
- data/spec/testapp_30/config/examples/database.yml +13 -0
- data/spec/testapp_30/config/locales/en.yml +5 -0
- data/spec/testapp_30/config/routes.rb +6 -0
- data/spec/testapp_30/config.ru +4 -0
- data/spec/testapp_30/db/migrate/20110630174538_create_error_messages.rb +22 -0
- data/spec/testapp_30/db/migrate/20110702131654_add_sessions_table.rb +16 -0
- data/spec/testapp_30/db/schema.rb +39 -0
- data/spec/testapp_30/db/seeds.rb +7 -0
- data/spec/testapp_30/lib/tasks/.gitkeep +0 -0
- data/spec/testapp_30/script/rails +6 -0
- data/spec/testapp_30/script/setup +19 -0
- data/spec/testapp_30/vendor/plugins/.gitkeep +0 -0
- data/spec/unit/configuration_spec.rb +37 -0
- data/spec/unit/handler_spec.rb +136 -0
- data/spec/unit/parser_spec.rb +112 -0
- metadata +107 -0
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
abstract (1.0.0)
|
5
|
+
actionmailer (3.0.9)
|
6
|
+
actionpack (= 3.0.9)
|
7
|
+
mail (~> 2.2.19)
|
8
|
+
actionpack (3.0.9)
|
9
|
+
activemodel (= 3.0.9)
|
10
|
+
activesupport (= 3.0.9)
|
11
|
+
builder (~> 2.1.2)
|
12
|
+
erubis (~> 2.6.6)
|
13
|
+
i18n (~> 0.5.0)
|
14
|
+
rack (~> 1.2.1)
|
15
|
+
rack-mount (~> 0.6.14)
|
16
|
+
rack-test (~> 0.5.7)
|
17
|
+
tzinfo (~> 0.3.23)
|
18
|
+
activemodel (3.0.9)
|
19
|
+
activesupport (= 3.0.9)
|
20
|
+
builder (~> 2.1.2)
|
21
|
+
i18n (~> 0.5.0)
|
22
|
+
activerecord (3.0.9)
|
23
|
+
activemodel (= 3.0.9)
|
24
|
+
activesupport (= 3.0.9)
|
25
|
+
arel (~> 2.0.10)
|
26
|
+
tzinfo (~> 0.3.23)
|
27
|
+
activeresource (3.0.9)
|
28
|
+
activemodel (= 3.0.9)
|
29
|
+
activesupport (= 3.0.9)
|
30
|
+
activesupport (3.0.9)
|
31
|
+
arel (2.0.10)
|
32
|
+
builder (2.1.2)
|
33
|
+
diff-lcs (1.1.2)
|
34
|
+
erubis (2.6.6)
|
35
|
+
abstract (>= 1.0.0)
|
36
|
+
i18n (0.5.0)
|
37
|
+
mail (2.2.19)
|
38
|
+
activesupport (>= 2.3.6)
|
39
|
+
i18n (>= 0.4.0)
|
40
|
+
mime-types (~> 1.16)
|
41
|
+
treetop (~> 1.4.8)
|
42
|
+
mime-types (1.16)
|
43
|
+
mysql2 (0.2.6)
|
44
|
+
polyglot (0.3.1)
|
45
|
+
rack (1.2.3)
|
46
|
+
rack-mount (0.6.14)
|
47
|
+
rack (>= 1.0.0)
|
48
|
+
rack-test (0.5.7)
|
49
|
+
rack (>= 1.0)
|
50
|
+
rails (3.0.9)
|
51
|
+
actionmailer (= 3.0.9)
|
52
|
+
actionpack (= 3.0.9)
|
53
|
+
activerecord (= 3.0.9)
|
54
|
+
activeresource (= 3.0.9)
|
55
|
+
activesupport (= 3.0.9)
|
56
|
+
bundler (~> 1.0)
|
57
|
+
railties (= 3.0.9)
|
58
|
+
railties (3.0.9)
|
59
|
+
actionpack (= 3.0.9)
|
60
|
+
activesupport (= 3.0.9)
|
61
|
+
rake (>= 0.8.7)
|
62
|
+
rdoc (~> 3.4)
|
63
|
+
thor (~> 0.14.4)
|
64
|
+
rake (0.9.2)
|
65
|
+
rdoc (3.8)
|
66
|
+
rspec (2.6.0)
|
67
|
+
rspec-core (~> 2.6.0)
|
68
|
+
rspec-expectations (~> 2.6.0)
|
69
|
+
rspec-mocks (~> 2.6.0)
|
70
|
+
rspec-core (2.6.4)
|
71
|
+
rspec-expectations (2.6.0)
|
72
|
+
diff-lcs (~> 1.1.2)
|
73
|
+
rspec-mocks (2.6.0)
|
74
|
+
rspec-rails (2.6.1)
|
75
|
+
actionpack (~> 3.0)
|
76
|
+
activesupport (~> 3.0)
|
77
|
+
railties (~> 3.0)
|
78
|
+
rspec (~> 2.6.0)
|
79
|
+
thor (0.14.6)
|
80
|
+
treetop (1.4.9)
|
81
|
+
polyglot (>= 0.3.1)
|
82
|
+
tzinfo (0.3.29)
|
83
|
+
|
84
|
+
PLATFORMS
|
85
|
+
ruby
|
86
|
+
|
87
|
+
DEPENDENCIES
|
88
|
+
mysql2 (= 0.2.6)
|
89
|
+
rack-test (= 0.5.7)
|
90
|
+
rails (= 3.0.9)
|
91
|
+
rspec-rails
|
data/LICENCE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Bjørn Trondsen
|
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.
|
data/README.markdown
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
# Rails Exception Handler [![Build Status](http://travis-ci.org/Sharagoz/rails_execption_handler.png)](http://travis-ci.org/#!/Sharagoz/rails_exception_handler)
|
2
|
+
|
3
|
+
This is an exception handler for Rails 3 built as Rack middleware. It enables you to save the key information from the error message in a database somewhere and display a customized error message to the user within the applications layout file. You can hook this gem into all your rails apps and gather the exception reports in one place. If you make yourself a simple web front on top of that you have a user friendly way of keeping track of exceptions raised by your rails apps.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
Add this line to your Gemfile:
|
7
|
+
|
8
|
+
```
|
9
|
+
config.gem 'rails_exception_handler', '~> 1.0'
|
10
|
+
```
|
11
|
+
|
12
|
+
Create an initializer in **config/initializers** called **rails_exception_handler.rb** and uncomment the options where you want something other than the default:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
RailsExceptionHandler.configure do |config|
|
16
|
+
# config.environments = [:development, :test, :production] # Defaults to [:production]
|
17
|
+
# config.storage_strategies = [:active_record, :rails_log, :remote_url => {:target => 'http://example.com'}] # Defaults to []
|
18
|
+
# config.fallback_layout = 'home' # Defaults to 'application'
|
19
|
+
# config.store_user_info = {:method => :current_user, :field => :login} # Defaults to false
|
20
|
+
# config.responses['404'] = "<h1>404</h1><p>Page not found</p>"
|
21
|
+
# config.responses['500'] = "<h1>500</h1><p>Internal server error</p>"
|
22
|
+
# config.filters = [ # No filters are enabled by default
|
23
|
+
# :all_404s,
|
24
|
+
# :no_referer_404s,
|
25
|
+
# {:user_agent_regxp => /\b(ApptusBot|TurnitinBot|DotBot|SiteBot)\b/i},
|
26
|
+
# {:target_url_regxp => /\b(myphpadmin)\b/i}
|
27
|
+
#]
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
## Configuration options explained
|
32
|
+
|
33
|
+
### environments
|
34
|
+
An array of symbols that says which Rails environments you want the exception handler to run in.
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
config.environments = [:production, :test, :development]
|
38
|
+
```
|
39
|
+
|
40
|
+
Default value: [:production]
|
41
|
+
|
42
|
+
### storage_strategies
|
43
|
+
An array of zero or more symbols that says which storage strategies you want to use. Each are explained in detail in separate sections at the end of this document.
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
config.storage_strategies = [:active_record, :rails_log, :remote_url => {:target => 'http://example.com'}]
|
47
|
+
```
|
48
|
+
|
49
|
+
Default value: []
|
50
|
+
|
51
|
+
|
52
|
+
### fallback_layout
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
config.fallback_layout = 'home'
|
56
|
+
```
|
57
|
+
|
58
|
+
Default value: 'application'
|
59
|
+
|
60
|
+
The exception handler will always use the layout file of the controller action that was accessed when the error occured. However, when routing errors occures there are no controller action to get this layout from, so it falls back to the default 'application' layout that most apps have. If your application does not have a layout file called 'application', then you need to override this, otherwise a "missing layout" exception will occur.
|
61
|
+
|
62
|
+
### store_user_info
|
63
|
+
|
64
|
+
Having some way of identifying the user can be very useful at times, so I always store information on who generated the exception in applications that have a log in feature.
|
65
|
+
To enable this you need to specify the name of the controller method that provides the user object and the name of the field which contains the info you want to save.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
config.store_user_info = {:method => :current_user, :field => :login}
|
69
|
+
```
|
70
|
+
|
71
|
+
Default value: false (no info will be stored)
|
72
|
+
If you turn this on and the error is generated by a client that is not logged in, then "Anonymous" will be used.
|
73
|
+
|
74
|
+
### filters
|
75
|
+
|
76
|
+
All filters are disabled by default. I recommend you deploy your application this way, and then add filters as they become necessary.
|
77
|
+
The only reason I've ever wanted filtering have been due to what seem like poorly programmed web crawlers and black bots probing for security holes.
|
78
|
+
Every once in a while I'll get dozens of errors within a few minutes caused by a bot looking for things like Joomla/Wordpress libraries with known security holes, or a web crawler that follows the target of forms.
|
79
|
+
|
80
|
+
**:all_404s**
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
config.filters = [:all_404s]
|
84
|
+
```
|
85
|
+
|
86
|
+
When turned on the following exceptions will no longer be stored: ActionController::RoutingError, AbstractController::ActionNotFound, ActiveRecord::RecordNotFound
|
87
|
+
Consider this a last resort. You will miss all "real" 404s when this is turned on, like broken redirections.
|
88
|
+
|
89
|
+
**:no_referer_404s**
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
config.filters = [:no_referer_404s]
|
93
|
+
```
|
94
|
+
|
95
|
+
ActionController::RoutingError, AbstractController::ActionNotFound, ActiveRecord::RecordNotFound will be ignored if it was caused by a request without a referer.
|
96
|
+
This is very effective against bots. 99.9% of the time a routing error with no referer will be caused by a bot, and then once in a while it will be caused by a real user that happened to generate an error on the first page he opened. You will get a lot less false positives with this filter than :all_404s.
|
97
|
+
|
98
|
+
**:user_agent_regxp**
|
99
|
+
|
100
|
+
The legit bots usually adds something to the user agent string that lets you identify them. You can use this to filter out the errors they genereate, and be pretty sure you are not going to get any false positives.
|
101
|
+
The regular expression I use looks something like this:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
config.filters = [:user_agent_regxp => /\b(ZyBorg|Yandex|Jyxobot|Huaweisymantecspider|ApptusBot|TurnitinBot|DotBot)\b/i]
|
105
|
+
```
|
106
|
+
|
107
|
+
If you (like me) dont know regular expressions by heart, then http://www.rubular.com/ is great tool to use when creating a regxp.
|
108
|
+
|
109
|
+
**:target_url_regxp**
|
110
|
+
|
111
|
+
Sometimes the bad bots add a common user agent string and a referer to their requests, which makes it hard to filter them without filtering all routing errors. I guess they do this to make it look less suspicious.
|
112
|
+
What you can often do is to filter on what they target, which is usually security holes in some well known library like phpMyAdmin.
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
config.filters = [:target_url_regxp => /\b(phpMyAdmin|joomla|wordpress)\b/i]
|
116
|
+
```
|
117
|
+
|
118
|
+
## Storage strategy - active record
|
119
|
+
```ruby
|
120
|
+
config.storage_strategies = [:active_record]
|
121
|
+
```
|
122
|
+
This means that the error reports will be stored through active record directly to a database, which is pretty much the whole reason why I created this library in the first place. A new entry called **exception_database** is needed in **database.yml**:
|
123
|
+
|
124
|
+
```
|
125
|
+
# for mysql the entry would look something like this:
|
126
|
+
exception_database:
|
127
|
+
adapter: mysql2
|
128
|
+
encoding: utf8
|
129
|
+
reconnect: false
|
130
|
+
database: your_database
|
131
|
+
pool: 5
|
132
|
+
username: user
|
133
|
+
password: secret
|
134
|
+
host: 127.0.0.1
|
135
|
+
```
|
136
|
+
|
137
|
+
You could of course store the error messages in the same database as the application uses, but one of the main purposes of this library is to enable you to easily store error reports from many applications in the same database, so I recommend you set up a separate dedicated database for this.
|
138
|
+
|
139
|
+
The exception database needs a table called **error_messages**. Here's a migration script that you can use to create the table with the necessary fields:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
class CreateErrorMessages < ActiveRecord::Migration
|
143
|
+
def self.up
|
144
|
+
create_table :error_messages do |t|
|
145
|
+
t.text :class_name
|
146
|
+
t.text :message
|
147
|
+
t.text :trace
|
148
|
+
t.text :params
|
149
|
+
t.text :target_url
|
150
|
+
t.text :referer_url
|
151
|
+
t.text :user_agent
|
152
|
+
t.string :user_info
|
153
|
+
t.string :app_name
|
154
|
+
|
155
|
+
t.timestamps
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.down
|
160
|
+
drop_table :error_messages
|
161
|
+
end
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
## Storage strategy - rails log
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
config.storage_strategies = [:rails_log]
|
169
|
+
```
|
170
|
+
|
171
|
+
An error will be logged in the standard rails log. The log i located in the RAILS_ROOT/log directory and is named after the Rails environment.
|
172
|
+
Example of what a report might look like:
|
173
|
+
|
174
|
+
```
|
175
|
+
TARGET: http://example.com/users
|
176
|
+
REFERER: http://example.com/
|
177
|
+
PARAMS: {"controller"=>"users", "action"=>"index"}
|
178
|
+
USER_AGENT: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30
|
179
|
+
USER_INFO: matz
|
180
|
+
ActionView::Template::Error (ActionView::Template::Error):
|
181
|
+
activesupport (3.0.7) lib/active_support/whiny_nil.rb:48:in `method_missing'
|
182
|
+
actionpack (3.0.7) lib/action_view/template.rb:135:in `block in render'
|
183
|
+
activesupport (3.0.7) lib/active_support/notifications.rb:54:in `instrument'
|
184
|
+
(the rest of the stack trace has been omitted from the example)
|
185
|
+
```
|
186
|
+
|
187
|
+
## Storage strategy - remote url
|
188
|
+
```ruby
|
189
|
+
config.storage_strategies = [:remote_url => {:target => 'http://example.com/error_messages'}]
|
190
|
+
```
|
191
|
+
This option is meant for those who want to store the exception in a database table, but does not have direct access to the database itself, making active_record store unsuitable. You need a web app on a server that has access to the database. An HTTP POST request will be sent to the specified URL with the error message as data.
|
192
|
+
If you use a Rails app at the other end you should simply be able to do _ErrorMessage.create(params[:error_message])_ to save the report.
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
desc 'Run tests'
|
8
|
+
task :test do
|
9
|
+
files = ["spec/unit/handler_spec.rb",
|
10
|
+
"spec/unit/parser_spec.rb",
|
11
|
+
"spec/unit/configuration_spec.rb",
|
12
|
+
"spec/integration/rails_exception_handler_spec.rb",
|
13
|
+
"spec/integration/configuration_spec.rb"
|
14
|
+
]
|
15
|
+
system "bundle exec rspec #{files.join(' ')}"
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'jeweler'
|
19
|
+
Jeweler::Tasks.new do |gem|
|
20
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
21
|
+
gem.name = "rails_exception_handler"
|
22
|
+
gem.homepage = "http://github.com/Sharagoz/rails_exception_handler"
|
23
|
+
gem.license = "MIT"
|
24
|
+
gem.summary = %Q{Exception Handling for Rails 3}
|
25
|
+
gem.description = %Q{}
|
26
|
+
gem.email = "contact@sharagoz.com"
|
27
|
+
gem.authors = ["Sharagoz"]
|
28
|
+
gem.extra_rdoc_files = ['README.markdown']
|
29
|
+
# dependencies defined in Gemfile
|
30
|
+
end
|
31
|
+
Jeweler::RubygemsDotOrgTasks.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This patch enables the exception handler to catch routing errors
|
2
|
+
module ActionDispatch
|
3
|
+
class ShowExceptions
|
4
|
+
private
|
5
|
+
def render_exception_with_template(env, exception)
|
6
|
+
if(RailsExceptionHandler.configuration.environments.include?(Rails.env.to_sym))
|
7
|
+
RailsExceptionHandler::Handler.new(env, exception).handle_exception
|
8
|
+
else
|
9
|
+
raise "RailsExceptionHandler: This patch should not have been loaded"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
alias_method_chain :render_exception, :template
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class RailsExceptionHandler::Configuration
|
2
|
+
attr_accessor :storage_strategies, :environments, :filters, :responses, :fallback_layout, :store_user_info
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@environments = [:production]
|
6
|
+
@storage_strategies = []
|
7
|
+
@filters = []
|
8
|
+
@store_user_info = false
|
9
|
+
@fallback_layout = 'application'
|
10
|
+
@responses = { '404' => '<h1>Page not found</h1><p>The page you were looking for could not be found.</p>',
|
11
|
+
'500' => '<h1>Internal server error</h1><p>The application has encountered an unexpected issue.</p>' }
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class RailsExceptionHandler::Handler
|
2
|
+
def initialize(env, exception)
|
3
|
+
@exception = exception
|
4
|
+
@env = env
|
5
|
+
@request = ActionDispatch::Request.new(@env)
|
6
|
+
@parsed_error = nil
|
7
|
+
if(@env['action_controller.instance'])
|
8
|
+
@controller = @env['action_controller.instance']
|
9
|
+
else
|
10
|
+
# During routing errors a controller object must be created manually and the before filters run,
|
11
|
+
# in case they are used to set up the authentication mechanism that provides the current_user object
|
12
|
+
@controller = ApplicationController.new
|
13
|
+
@controller.request = @request
|
14
|
+
filters = @controller._process_action_callbacks.collect {|cb| cb.filter if(cb.kind == :before)}
|
15
|
+
filters.each { |filter| @controller.send(filter) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def handle_exception
|
20
|
+
@parsed_error = RailsExceptionHandler::Parser.new(@exception, @request, @controller)
|
21
|
+
store_error unless(@parsed_error.ignore?)
|
22
|
+
return response
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def store_error
|
28
|
+
RailsExceptionHandler.configuration.storage_strategies.each do |strategy|
|
29
|
+
if(strategy.class == Symbol)
|
30
|
+
send("store_in_#{strategy}")
|
31
|
+
elsif(strategy.class == Hash && strategy[:remote_url])
|
32
|
+
store_in_remote_url(strategy[:remote_url])
|
33
|
+
else
|
34
|
+
raise "RailsExceptionHandler: Unknown storage strategy #{strategy.inspect}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def store_in_active_record
|
40
|
+
ErrorMessage.create(@parsed_error.relevant_info)
|
41
|
+
end
|
42
|
+
|
43
|
+
def store_in_rails_log
|
44
|
+
info = @parsed_error.relevant_info
|
45
|
+
message = "TARGET: #{info[:target_url]}\n"
|
46
|
+
message += "REFERER: #{info[:referer_url]}\n"
|
47
|
+
message += "PARAMS: #{info[:params]}\n"
|
48
|
+
message += "USER_AGENT: #{info[:user_agent]}\n"
|
49
|
+
message += "USER_INFO: #{info[:user_info]}\n"
|
50
|
+
message += "#{info[:class_name]} (#{info[:message]}):\n"
|
51
|
+
message += Rails.backtrace_cleaner.clean(info[:trace].split("\n"), :noise).join("\n")
|
52
|
+
Rails.logger.fatal(message)
|
53
|
+
end
|
54
|
+
|
55
|
+
def store_in_remote_url(args)
|
56
|
+
uri = URI.parse(args[:target])
|
57
|
+
params = {}
|
58
|
+
@parsed_error.relevant_info.each do |key,value|
|
59
|
+
params["error_message[#{key}]"] = value
|
60
|
+
end
|
61
|
+
Net::HTTP::post_form(uri, params)
|
62
|
+
end
|
63
|
+
|
64
|
+
def response
|
65
|
+
@env['exception_handler.layout'] = @controller.send(:_default_layout) || RailsExceptionHandler.configuration.fallback_layout
|
66
|
+
@env['exception_handler.response_code'] = @parsed_error.routing_error? ? '404' : '500'
|
67
|
+
ErrorResponseController.action(:index).call(@env)
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
class RailsExceptionHandler::Parser
|
2
|
+
def initialize(exception, request, controller)
|
3
|
+
@exception = exception
|
4
|
+
@request = request
|
5
|
+
@controller = controller
|
6
|
+
end
|
7
|
+
|
8
|
+
def relevant_info
|
9
|
+
info = {}
|
10
|
+
info[:app_name] = Rails.application.class.parent_name
|
11
|
+
info[:class_name] = @exception.class.to_s
|
12
|
+
info[:message] = @exception.to_s
|
13
|
+
info[:trace] = @exception.backtrace.join("\n")
|
14
|
+
info[:target_url] = @request.url
|
15
|
+
info[:referer_url] = @request.referer
|
16
|
+
info[:params] = @request.params.inspect
|
17
|
+
info[:user_agent] = @request.user_agent
|
18
|
+
info[:user_info] = user_info
|
19
|
+
info[:created_at] = Time.now
|
20
|
+
return info
|
21
|
+
end
|
22
|
+
|
23
|
+
def ignore?
|
24
|
+
filters = RailsExceptionHandler.configuration.filters
|
25
|
+
filters.each do |filter|
|
26
|
+
if(filter.class == Symbol)
|
27
|
+
result = send("filter_#{filter}")
|
28
|
+
elsif(filter.class == Hash)
|
29
|
+
result = send("filter_#{filter.flatten[0]}", filter.flatten[1])
|
30
|
+
else
|
31
|
+
raise "RailsExceptionHandler: Unknown filter #{filter.inspect}"
|
32
|
+
end
|
33
|
+
return true if(result)
|
34
|
+
end
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
def routing_error?
|
39
|
+
routing_errors = [ActionController::RoutingError, AbstractController::ActionNotFound, ActiveRecord::RecordNotFound]
|
40
|
+
routing_errors.include?(@exception.class)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def blank_referer?
|
46
|
+
relevant_info[:referer_url] == "/" || relevant_info[:referer_url].blank?
|
47
|
+
end
|
48
|
+
|
49
|
+
def user_info
|
50
|
+
config = RailsExceptionHandler.configuration.store_user_info
|
51
|
+
return nil unless(config)
|
52
|
+
user_object = @controller.send(config[:method])
|
53
|
+
user_object ? user_object.send(config[:field]) : 'Anonymous'
|
54
|
+
end
|
55
|
+
|
56
|
+
def filter_all_404s
|
57
|
+
routing_error?
|
58
|
+
end
|
59
|
+
|
60
|
+
def filter_no_referer_404s
|
61
|
+
routing_error? && blank_referer?
|
62
|
+
end
|
63
|
+
|
64
|
+
def filter_user_agent_regxp(regxp)
|
65
|
+
result = relevant_info[:user_agent].match(regxp)
|
66
|
+
result != nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def filter_target_url_regxp(regxp)
|
70
|
+
result = relevant_info[:target_url].match(regxp)
|
71
|
+
result != nil
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class RailsExceptionHandler
|
2
|
+
def initialize(app)
|
3
|
+
@app = app
|
4
|
+
end
|
5
|
+
|
6
|
+
def call(env)
|
7
|
+
@app.call(env)
|
8
|
+
rescue Exception => e
|
9
|
+
Handler.new(env, e).handle_exception
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.configuration
|
13
|
+
@configuration ||= Configuration.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configure
|
17
|
+
yield configuration
|
18
|
+
return unless configuration.environments.include?(Rails.env.to_sym)
|
19
|
+
|
20
|
+
Rails.configuration.middleware.use(RailsExceptionHandler)
|
21
|
+
|
22
|
+
Rails.configuration.action_dispatch.show_exceptions = true
|
23
|
+
require File.expand_path(File.dirname(__FILE__)) + '/patch/show_exceptions.rb'
|
24
|
+
|
25
|
+
%w{ models controllers }.each do |dir|
|
26
|
+
path = File.join(File.dirname(__FILE__), '../app', dir)
|
27
|
+
$LOAD_PATH << path
|
28
|
+
ActiveSupport::Dependencies.autoload_paths << path
|
29
|
+
ActiveSupport::Dependencies.autoload_once_paths.delete(path)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'rails_exception_handler/configuration.rb'
|
35
|
+
require 'rails_exception_handler/handler.rb'
|
36
|
+
require 'rails_exception_handler/parser.rb'
|
37
|
+
require 'net/http'
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{rails_exception_handler}
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Sharagoz"]
|
12
|
+
s.date = %q{2011-07-17}
|
13
|
+
s.description = %q{}
|
14
|
+
s.email = %q{contact@sharagoz.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.markdown"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".travis.yml",
|
20
|
+
"Gemfile",
|
21
|
+
"Gemfile.lock",
|
22
|
+
"LICENCE",
|
23
|
+
"README.markdown",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"app/controllers/error_response_controller.rb",
|
27
|
+
"app/models/error_message.rb",
|
28
|
+
"lib/patch/show_exceptions.rb",
|
29
|
+
"lib/rails_exception_handler.rb",
|
30
|
+
"lib/rails_exception_handler/configuration.rb",
|
31
|
+
"lib/rails_exception_handler/handler.rb",
|
32
|
+
"lib/rails_exception_handler/parser.rb",
|
33
|
+
"rails_exception_handler.gemspec",
|
34
|
+
"spec/integration/configuration_spec.rb",
|
35
|
+
"spec/integration/rails_exception_handler_spec.rb",
|
36
|
+
"spec/spec_helper.rb",
|
37
|
+
"spec/test_macros.rb",
|
38
|
+
"spec/testapp_30/.gitignore",
|
39
|
+
"spec/testapp_30/Gemfile",
|
40
|
+
"spec/testapp_30/Gemfile.lock",
|
41
|
+
"spec/testapp_30/Rakefile",
|
42
|
+
"spec/testapp_30/app/controllers/application_controller.rb",
|
43
|
+
"spec/testapp_30/app/controllers/home_controller.rb",
|
44
|
+
"spec/testapp_30/app/helpers/application_helper.rb",
|
45
|
+
"spec/testapp_30/app/models/stored_exception.rb",
|
46
|
+
"spec/testapp_30/app/views/home/view_error.html.erb",
|
47
|
+
"spec/testapp_30/app/views/layouts/fallback.html.erb",
|
48
|
+
"spec/testapp_30/app/views/layouts/home.html.erb",
|
49
|
+
"spec/testapp_30/config.ru",
|
50
|
+
"spec/testapp_30/config/application.rb",
|
51
|
+
"spec/testapp_30/config/boot.rb",
|
52
|
+
"spec/testapp_30/config/database.yml",
|
53
|
+
"spec/testapp_30/config/environment.rb",
|
54
|
+
"spec/testapp_30/config/environments/development.rb",
|
55
|
+
"spec/testapp_30/config/environments/production.rb",
|
56
|
+
"spec/testapp_30/config/environments/test.rb",
|
57
|
+
"spec/testapp_30/config/examples/database.yml",
|
58
|
+
"spec/testapp_30/config/locales/en.yml",
|
59
|
+
"spec/testapp_30/config/routes.rb",
|
60
|
+
"spec/testapp_30/db/migrate/20110630174538_create_error_messages.rb",
|
61
|
+
"spec/testapp_30/db/migrate/20110702131654_add_sessions_table.rb",
|
62
|
+
"spec/testapp_30/db/schema.rb",
|
63
|
+
"spec/testapp_30/db/seeds.rb",
|
64
|
+
"spec/testapp_30/lib/tasks/.gitkeep",
|
65
|
+
"spec/testapp_30/script/rails",
|
66
|
+
"spec/testapp_30/script/setup",
|
67
|
+
"spec/testapp_30/vendor/plugins/.gitkeep",
|
68
|
+
"spec/unit/configuration_spec.rb",
|
69
|
+
"spec/unit/handler_spec.rb",
|
70
|
+
"spec/unit/parser_spec.rb"
|
71
|
+
]
|
72
|
+
s.homepage = %q{http://github.com/Sharagoz/rails_exception_handler}
|
73
|
+
s.licenses = ["MIT"]
|
74
|
+
s.require_paths = ["lib"]
|
75
|
+
s.rubygems_version = %q{1.6.2}
|
76
|
+
s.summary = %q{Exception Handling for Rails 3}
|
77
|
+
|
78
|
+
if s.respond_to? :specification_version then
|
79
|
+
s.specification_version = 3
|
80
|
+
|
81
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
82
|
+
else
|
83
|
+
end
|
84
|
+
else
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|