ixtlan-error-handler 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.textile +31 -0
- data/lib/ixtlan/errors/error_dumper.rb +75 -50
- data/lib/ixtlan/errors/error_dumper.rb~ +79 -0
- data/lib/ixtlan/errors/error_handler.rb +0 -15
- data/lib/ixtlan/errors/error_handler.rb~ +79 -0
- data/lib/ixtlan/errors/error_handling.rb~ +79 -0
- data/lib/ixtlan/errors/mailer.rb +2 -2
- data/lib/ixtlan/errors/mailer.rb~ +19 -0
- data/lib/ixtlan/errors/railtie.rb +2 -5
- data/lib/ixtlan/errors/railtie.rb~ +20 -0
- data/lib/ixtlan/errors/rescue_module.rb~ +41 -0
- data/spec/error_dumper_spec.rb +49 -15
- data/spec/error_dumper_spec.rb~ +8 -0
- data/spec/error_handler_spec.rb~ +60 -0
- metadata +66 -38
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Kristian Meier
|
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.textile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
h1. Rails Error Dumper
|
2
|
+
|
3
|
+
p. the main idea here is notify the developers on error but in a way to protect privacy of the users of the system. to do so ALL data need to remain on the server and they need to be deleted after period of time.
|
4
|
+
|
5
|
+
* first to dump as much as possible onto the filesystem of the server when an error occurs, i.e. the environment, the request, the response, the session, etc
|
6
|
+
|
7
|
+
* notify the developers
|
8
|
+
|
9
|
+
* delete expired error dumps
|
10
|
+
|
11
|
+
p. the next idea is to collect all possible rails exception and map them to three error pages and pass on a message. for each exception you can decide whether an error dump is needed or not.
|
12
|
+
|
13
|
+
h2. install
|
14
|
+
|
15
|
+
p. in Gemfile add *gem 'ixtlan-error-handler'*
|
16
|
+
|
17
|
+
p. for the configuration add for example in _config/initializers/error_handler.rb_
|
18
|
+
|
19
|
+
bc. config.error_dumper.dump_dir = Rails.root + "/log/errors" # default: log/errors
|
20
|
+
config.error_dumper.email_from = "no-reply@example.com"
|
21
|
+
config.error_dumper.email_to = "developer1@example.com,developer2@example.com"
|
22
|
+
config.error_dumper.keep_dumps = 30 # days
|
23
|
+
config.skip_rescue_module = true # do not include the predefined Rescue
|
24
|
+
|
25
|
+
h2. relation to ixtlan gem
|
26
|
+
|
27
|
+
p. the ixtlan gem provides a setup generator which adds configuration examples for this gem in _config/initializer/ixtlan.rb_ (the dynamic configuration is part of the ixtlan gem and it is just easier to keep that inside that gem !!!)
|
28
|
+
|
29
|
+
h2. relation to ixtlan-audit gem
|
30
|
+
|
31
|
+
p. if that gem is present and loaded than any error will be log with the help of _Ixtlan::Audit::UserLogger_
|
@@ -1,85 +1,110 @@
|
|
1
1
|
require 'ixtlan/errors/mailer'
|
2
2
|
require 'fileutils'
|
3
|
+
require 'slf4r/logger'
|
4
|
+
|
5
|
+
# unless Fixnum.respond_to? :days
|
6
|
+
# class Fixnum
|
7
|
+
# def days
|
8
|
+
# self
|
9
|
+
# end
|
10
|
+
|
11
|
+
# def ago
|
12
|
+
# DateTime.now - 86000 * self
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
# end
|
3
16
|
|
4
17
|
module Ixtlan
|
5
18
|
module Errors
|
6
19
|
class ErrorDumper
|
7
20
|
|
8
|
-
|
21
|
+
private
|
22
|
+
|
23
|
+
include ::Slf4r::Logger
|
24
|
+
|
25
|
+
public
|
26
|
+
|
27
|
+
attr_accessor :from_email, :to_emails, :keep_dumps, :base_url
|
9
28
|
|
10
|
-
def
|
11
|
-
@keep_dumps
|
29
|
+
def initialize
|
30
|
+
@keep_dumps = 30
|
12
31
|
end
|
13
32
|
|
14
33
|
def keep_dumps=(ttl)
|
34
|
+
old = @keep_dumps
|
15
35
|
@keep_dumps = ttl.to_i
|
16
|
-
|
36
|
+
daily_cleanup if old != @keep_dumps
|
17
37
|
end
|
18
|
-
|
19
|
-
def
|
20
|
-
|
21
|
-
@dump_dir = File.join(Rails.root, "log", "errors")
|
22
|
-
FileUtils.mkdir_p(@dump_dir)
|
23
|
-
end
|
24
|
-
@dump_dir
|
38
|
+
|
39
|
+
def error_dump_model(model = nil)
|
40
|
+
@error_dump ||= model.nil? ? ::Error : model.classify
|
25
41
|
end
|
26
42
|
|
27
|
-
def dump_dir=(dir)
|
28
|
-
@dump_dir = dir
|
29
|
-
FileUtils.mkdir_p(@dump_dir) unless dir.blank?
|
30
|
-
@dump_dir
|
31
|
-
end
|
32
|
-
|
33
43
|
def dump(controller, exception)
|
34
|
-
|
35
|
-
log_file = log_filename
|
36
|
-
logger = Logger.new(log_file)
|
44
|
+
daily_cleanup
|
37
45
|
|
38
|
-
dump_environment(
|
39
|
-
Mailer.error_notification(@
|
40
|
-
|
46
|
+
error = dump_environment(exception, controller)
|
47
|
+
Mailer.error_notification(@from_email, @to_emails, exception, "#{@base_url}/#{error.id}").deliver unless (@to_emails.blank? || @from_email.blank?)
|
48
|
+
"#{@base_url}/#{error.id}"
|
41
49
|
end
|
42
50
|
|
43
51
|
private
|
44
52
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
53
|
+
def dump_environment(exception, controller)
|
54
|
+
dump = error_dump_model.new
|
55
|
+
|
56
|
+
dump.request = dump_hashmap(controller.request.env)
|
49
57
|
|
50
|
-
|
51
|
-
logger.error("\n===================================================================\n#{header}\n===================================================================\n");
|
52
|
-
end
|
58
|
+
dump.response = dump_hashmap(controller.response.headers)
|
53
59
|
|
54
|
-
|
55
|
-
dump_environment_header(logger, "REQUEST DUMP");
|
56
|
-
dump_hashmap(logger, controller.request.env)
|
60
|
+
dump.session = dump_hashmap(controller.session)
|
57
61
|
|
58
|
-
|
59
|
-
dump_hashmap(logger, controller.response.headers)
|
62
|
+
dump.parameters = dump_hashmap(controller.params)
|
60
63
|
|
61
|
-
|
62
|
-
|
64
|
+
dump.message = exception.message
|
65
|
+
|
66
|
+
dump.clazz = exception.class.to_s
|
67
|
+
|
68
|
+
dump.backtrace = exception.backtrace.join("\n") if exception.backtrace
|
63
69
|
|
64
|
-
|
65
|
-
|
66
|
-
dump_hashmap(logger, controller.params.each{ |k,v| map[k]=v })
|
70
|
+
dump if dump.save
|
71
|
+
end
|
67
72
|
|
68
|
-
|
69
|
-
|
70
|
-
|
73
|
+
def dump_hashmap(map, indent = '')
|
74
|
+
result = ''
|
75
|
+
map.each do |key,value|
|
76
|
+
if value.is_a? Hash
|
77
|
+
result << "#{indent}#{key} => {\n"
|
78
|
+
result << dump_hashmap(value, "#{indent} ")
|
79
|
+
result << "#{indent}}\n"
|
80
|
+
else
|
81
|
+
result << "#{indent}#{key} => #{value.nil? ? 'nil': value}\n"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
result
|
71
85
|
end
|
72
86
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
87
|
+
def daily_cleanup
|
88
|
+
if(@last_cleanup.nil? || @last_cleanup < 1.days.ago)
|
89
|
+
@last_cleanup = 0.days.ago # to have the right type
|
90
|
+
begin
|
91
|
+
delete_all
|
92
|
+
logger.info("cleaned error dumps")
|
93
|
+
rescue Exception => e
|
94
|
+
logger.error("cleanup error dumps", e)
|
95
|
+
end
|
76
96
|
end
|
77
97
|
end
|
78
98
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
99
|
+
private
|
100
|
+
|
101
|
+
if defined? ::DataMapper
|
102
|
+
def delete_all
|
103
|
+
error_dump_model.all(:created_at.lte => keep_dumps.days.ago).destroy!
|
104
|
+
end
|
105
|
+
else # ActiveRecord
|
106
|
+
def delete_all
|
107
|
+
error_dump_model.all(:conditions => ["created_at <= ?", keep_dumps.days.ago]).each(&:delete)
|
83
108
|
end
|
84
109
|
end
|
85
110
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Errors
|
3
|
+
class ErrorDumper
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
configure
|
7
|
+
end
|
8
|
+
|
9
|
+
def configure(error_dir = nil, email_from = nil, emails_to = nil, time_to_live = 90)
|
10
|
+
if config
|
11
|
+
@email_from = email_from
|
12
|
+
@email_to = emails_to
|
13
|
+
@errors_dir = errors_dir
|
14
|
+
else
|
15
|
+
@email_from = nil
|
16
|
+
@email_to = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
@time_to_live = time_to_live
|
20
|
+
@errors_dir ||= File.join(Rails.root, "log", "errors")
|
21
|
+
FileUtils.mkdir_p(@errors_dir)
|
22
|
+
cleanup
|
23
|
+
end
|
24
|
+
|
25
|
+
def dump(controller, exception)
|
26
|
+
cleanup
|
27
|
+
log_file = log_filename
|
28
|
+
logger = Logger.new(log_file)
|
29
|
+
|
30
|
+
dump_environment(logger, exception, controller)
|
31
|
+
Mailer.deliver_error_notification(@email_from, @email_to, exception, log_file) unless (@email_to.blank? || @email_from.blank?)
|
32
|
+
log_file
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def log_filename(time = Time.now)
|
38
|
+
error_log_id = "#{time.tv_sec}#{time.tv_usec}"
|
39
|
+
File.join(@errors_dir, "error-#{error_log_id}.log")
|
40
|
+
end
|
41
|
+
|
42
|
+
def dump_environment_header(logger, header)
|
43
|
+
logger.error("\n===================================================================\n#{header}\n===================================================================\n");
|
44
|
+
end
|
45
|
+
|
46
|
+
def dump_environment(logger, exception, controller)
|
47
|
+
dump_environment_header(logger, "REQUEST DUMP");
|
48
|
+
dump_hashmap(logger, controller.request.env)
|
49
|
+
|
50
|
+
dump_environment_header(logger, "RESPONSE DUMP");
|
51
|
+
dump_hashmap(logger, controller.response.headers)
|
52
|
+
|
53
|
+
dump_environment_header(logger, "SESSION DUMP");
|
54
|
+
dump_hashmap(logger, controller.session)
|
55
|
+
|
56
|
+
dump_environment_header(logger, "PARAMETER DUMP");
|
57
|
+
map = {}
|
58
|
+
dump_hashmap(logger, controller.params.each{ |k,v| map[k]=v })
|
59
|
+
|
60
|
+
dump_environment_header(logger, "EXCEPTION");
|
61
|
+
logger.error("#{exception.class}:#{exception.message}")
|
62
|
+
logger.error("\t" + exception.backtrace.join("\n\t"))
|
63
|
+
end
|
64
|
+
|
65
|
+
def dump_hashmap(logger, map)
|
66
|
+
for key,value in map
|
67
|
+
logger.error("\t#{key} => #{value.inspect}")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def cleanup
|
72
|
+
ref_log_file = log_filename(@time_to_live.ago)
|
73
|
+
Dir[File.join(@error_dir, "error-*.log")].each do |f|
|
74
|
+
FileUtils.rm(f) if File.basename(f) < ref_log_file
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -14,23 +14,12 @@ module Ixtlan
|
|
14
14
|
log_user_error(exception)
|
15
15
|
error_page(:not_found, "page not found")
|
16
16
|
end
|
17
|
-
#status = rescue_responses[exception.class.name]
|
18
|
-
#status = status == :internal_server_error ? :not_found : status
|
19
|
-
#error_page(status, "page not found")
|
20
17
|
|
21
18
|
def stale_resource(exception)
|
22
19
|
log_user_error(exception)
|
23
20
|
error_page(:conflict, "stale resource")
|
24
21
|
end
|
25
22
|
|
26
|
-
# respond_to do |format|
|
27
|
-
# format.html {
|
28
|
-
# render_stale_error_page
|
29
|
-
# }
|
30
|
-
# format.xml { head :conflict }
|
31
|
-
# end
|
32
|
-
# end
|
33
|
-
|
34
23
|
def render_error_page_with_session(status)
|
35
24
|
render :template => "errors/error_with_session", :status => status
|
36
25
|
end
|
@@ -39,10 +28,6 @@ module Ixtlan
|
|
39
28
|
render :template => "errors/error", :status => status
|
40
29
|
end
|
41
30
|
|
42
|
-
# def render_stale_error_page
|
43
|
-
# render :template => "errors/stale", :status => :conflict
|
44
|
-
# end
|
45
|
-
|
46
31
|
private
|
47
32
|
|
48
33
|
if defined? ::Ixtlan::Audit
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Errors
|
3
|
+
module ErrorHandling
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def internal_server_error(exception)
|
8
|
+
dump_error(exception)
|
9
|
+
status = :internal_server_error
|
10
|
+
error_page(:internal_server_error, exception, "internal server error: #{exception.class.name}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def page_not_found(exception)
|
15
|
+
log_user_error(exception)
|
16
|
+
status = rescue_responses[exception.class.name]
|
17
|
+
status = status == :internal_server_error ? :not_found : status
|
18
|
+
error_page(status, exception, "page not found")
|
19
|
+
end
|
20
|
+
|
21
|
+
def stale_resource(exception)
|
22
|
+
log_user_error(exception)
|
23
|
+
respond_to do |format|
|
24
|
+
format.html {
|
25
|
+
render_stale_error_page
|
26
|
+
}
|
27
|
+
format.xml { head :conflict }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def render_error_page_with_session(status)
|
32
|
+
render :template => "errors/error_with_session", :status => status
|
33
|
+
end
|
34
|
+
|
35
|
+
def render_error_page(status)
|
36
|
+
render :template => "errors/error", :status => status
|
37
|
+
end
|
38
|
+
|
39
|
+
def render_stale_error_page
|
40
|
+
render :template => "errors/stale", :status => :conflict
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
if defined? ::Ixtlan::Audit
|
46
|
+
def user_logger
|
47
|
+
@user_logger ||= Ixtlan::Audit::UserLogger.new(ErrorHandling)
|
48
|
+
end
|
49
|
+
|
50
|
+
def log_user_error(exception)
|
51
|
+
user_logger.log_action(self, " - #{exception.class} - #{exception.message}")
|
52
|
+
log_error(exception)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
alias :log_user_error :log_error
|
56
|
+
end
|
57
|
+
|
58
|
+
def error_page(status, exception, notice)
|
59
|
+
respond_to do |format|
|
60
|
+
format.html {
|
61
|
+
@notice = notice
|
62
|
+
if logged_in?
|
63
|
+
render_error_page_with_session(status)
|
64
|
+
else
|
65
|
+
render_error_page(status)
|
66
|
+
end
|
67
|
+
}
|
68
|
+
# TODO add the notice there as well. how ?
|
69
|
+
format.xml { head status }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def dump_error(exception)
|
74
|
+
log_user_error(exception)
|
75
|
+
Rails.configuration.error_dumper.dump(self, exception)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Errors
|
3
|
+
module ErrorHandling
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def internal_server_error(exception)
|
8
|
+
dump_error(exception)
|
9
|
+
status = :internal_server_error
|
10
|
+
error_page(:internal_server_error, exception) do |exception|
|
11
|
+
"internal server error: #{exception.class.name}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def page_not_found(exception)
|
16
|
+
log_user_error(exception)
|
17
|
+
status = rescue_responses[exception.class.name]
|
18
|
+
status = status == :internal_server_error ? :not_found : status
|
19
|
+
error_page(status, exception) { "page not found" }
|
20
|
+
end
|
21
|
+
|
22
|
+
def stale_resource(exception)
|
23
|
+
log_user_error(exception)
|
24
|
+
respond_to do |format|
|
25
|
+
format.html {
|
26
|
+
render_stale_error_page
|
27
|
+
}
|
28
|
+
format.xml { head :conflict }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def render_error_page_with_session(status)
|
33
|
+
render :template => "errors/error_with_session", :status => status
|
34
|
+
end
|
35
|
+
|
36
|
+
def render_error_page(status)
|
37
|
+
render :template => "errors/error", :status => status
|
38
|
+
end
|
39
|
+
|
40
|
+
def render_stale_error_page
|
41
|
+
render :template => "errors/stale", :status => :conflict
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
if defined? ::Ixtlan::Audit
|
47
|
+
def user_logger
|
48
|
+
@user_logger ||= Ixtlan::UserLogger.new(Ixtlan::Core::ErrorHandling)
|
49
|
+
end
|
50
|
+
def log_user_error(exception)
|
51
|
+
user_logger.log_action(self, " - #{exception.class} - #{exception.message}")
|
52
|
+
log_error(exception)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
alias :log_user_error :log_error
|
56
|
+
end
|
57
|
+
|
58
|
+
def error_page(status, exception, &block)
|
59
|
+
respond_to do |format|
|
60
|
+
format.html {
|
61
|
+
@notice = block.call(exception)
|
62
|
+
if logged_in?
|
63
|
+
render_error_page_with_session(status)
|
64
|
+
else
|
65
|
+
render_error_page(status)
|
66
|
+
end
|
67
|
+
}
|
68
|
+
format.xml { head status }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def dump_error(exception)
|
73
|
+
log_user_error(exception)
|
74
|
+
dumper = Rails.configuration.error_dumper
|
75
|
+
dumper.dump(self, exception)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/ixtlan/errors/mailer.rb
CHANGED
@@ -2,9 +2,9 @@ module Ixtlan
|
|
2
2
|
module Errors
|
3
3
|
class Mailer < ActionMailer::Base
|
4
4
|
|
5
|
-
def error_notification(email_from, emails_to, exception,
|
5
|
+
def error_notification(email_from, emails_to, exception, error_url)
|
6
6
|
@subject = exception.message
|
7
|
-
@text = "#{
|
7
|
+
@text = "#{error_url}"
|
8
8
|
@recipients = emails_to
|
9
9
|
@from = email_from
|
10
10
|
@sent_on = Time.now
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Errors
|
3
|
+
class Mailer < ActionMailer::Base
|
4
|
+
|
5
|
+
path = File.join(File.dirname(__FILE__), "mailer")
|
6
|
+
view_paths << path unless view_paths.member? path
|
7
|
+
|
8
|
+
|
9
|
+
def error_notification(email_from, emails_to, exception, error_file)
|
10
|
+
@subject = exception.message
|
11
|
+
@body = {:text => "#{error_file}"}
|
12
|
+
@recipients = emails_to
|
13
|
+
@from = email_from
|
14
|
+
@sent_on = Time.now
|
15
|
+
@headers = {}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -14,11 +14,8 @@ module Ixtlan
|
|
14
14
|
ActionMailer::Base.view_paths= [ActionMailer::Base.view_paths, path].flatten
|
15
15
|
end
|
16
16
|
|
17
|
-
app.config.
|
18
|
-
|
19
|
-
app.config.error_dumper = ErrorDumper.new
|
20
|
-
app.config.skip_rescue_module = false
|
21
|
-
end
|
17
|
+
app.config.error_dumper = ErrorDumper.new
|
18
|
+
app.config.skip_rescue_module = false
|
22
19
|
end
|
23
20
|
|
24
21
|
config.after_initialize do |app|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rails'
|
2
|
+
|
3
|
+
module Ixtlan
|
4
|
+
module Errors
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
|
7
|
+
config.before_configuration do |app|
|
8
|
+
app.config.class.class_eval do
|
9
|
+
attr_accessor :error_dumper
|
10
|
+
app.config.error_dumper = Ixtlan::Errors::ErrorDumper.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
config.after_initialization do |app|
|
15
|
+
::ActionController::Base.send(:include, Ixtlan::Errors::RescueModule)
|
16
|
+
::ActionController::Base.send(:include, Ixtlan::Errors::ErrorHandler)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Errors
|
3
|
+
module RescueModule
|
4
|
+
def self.included(controller)
|
5
|
+
if defined? ::Ixtlan::ModifiedBy
|
6
|
+
# needs 'optimistic_persistence'
|
7
|
+
controller.rescue_from ::Ixtlan::ModifiedBy::StaleResourceError, :with => :stale_resource
|
8
|
+
end
|
9
|
+
|
10
|
+
if defined? ::Ixtlan::Guard
|
11
|
+
# needs 'guard'
|
12
|
+
controller.rescue_from ::Ixtlan::Guard::GuardException, :with => :page_not_found
|
13
|
+
controller.rescue_from ::Ixtlan::Guard::PermissionDenied, :with => :page_not_found
|
14
|
+
end
|
15
|
+
|
16
|
+
if defined? ::DataMapper
|
17
|
+
# datamapper
|
18
|
+
controller.rescue_from ::DataMapper::ObjectNotFoundError, :with => :page_not_found
|
19
|
+
end
|
20
|
+
|
21
|
+
if defined? ::ActiveRecord
|
22
|
+
# activerecord
|
23
|
+
controller.rescue_from ::ActiveRecord::RecordNotFound, :with => :page_not_found
|
24
|
+
end
|
25
|
+
|
26
|
+
# standard rails controller
|
27
|
+
controller.rescue_from ::ActionController::RoutingError, :with => :page_not_found
|
28
|
+
controller.rescue_from ::ActionController::UnknownAction, :with => :page_not_found
|
29
|
+
controller.rescue_from ::ActionController::MethodNotAllowed, :with => :page_not_found
|
30
|
+
controller.rescue_from ::ActionController::NotImplemented, :with => :page_not_found
|
31
|
+
controller.rescue_from ::ActionController::InvalidAuthenticityToken, :with => :stale_resource
|
32
|
+
|
33
|
+
# have nice stacktraces in development mode
|
34
|
+
unless controller.consider_all_requests_local
|
35
|
+
controller.rescue_from ::ActionView::MissingTemplate, :with => :internal_server_error
|
36
|
+
controller.rescue_from ::ActionView::TemplateError, :with => :internal_server_error
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/spec/error_dumper_spec.rb
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-migrations'
|
3
|
+
require 'slf4r/ruby_logger'
|
4
|
+
|
5
|
+
class Error
|
6
|
+
include DataMapper::Resource
|
7
|
+
|
8
|
+
property :id, Serial
|
9
|
+
|
10
|
+
property :message, String
|
11
|
+
property :clazz, String
|
12
|
+
property :request, Text
|
13
|
+
property :response, Text
|
14
|
+
property :session, Text
|
15
|
+
property :parameters, Text
|
16
|
+
property :backtrace, Text
|
17
|
+
|
18
|
+
property :created_at, DateTime
|
19
|
+
|
20
|
+
before :save do
|
21
|
+
self.created_at = DateTime.now
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
1
25
|
module Ixtlan
|
2
26
|
module Errors
|
3
27
|
module ActionMailer
|
@@ -62,8 +86,11 @@ class String
|
|
62
86
|
end
|
63
87
|
|
64
88
|
class Fixnum
|
89
|
+
def days
|
90
|
+
self
|
91
|
+
end
|
65
92
|
def ago
|
66
|
-
DateTime.now - self
|
93
|
+
DateTime.now - 86000 * self
|
67
94
|
end
|
68
95
|
end
|
69
96
|
|
@@ -76,37 +103,44 @@ class DateTime
|
|
76
103
|
end
|
77
104
|
end
|
78
105
|
|
106
|
+
DataMapper.setup(:default, "sqlite3::memory:")
|
107
|
+
DataMapper.finalize
|
108
|
+
DataMapper.repository.auto_migrate!
|
109
|
+
|
79
110
|
describe Ixtlan::Errors::ErrorDumper do
|
80
111
|
|
81
112
|
before :each do
|
82
113
|
@dumper = Ixtlan::Errors::ErrorDumper.new
|
83
|
-
@dumper.
|
114
|
+
@dumper.base_url = "http://localhost"
|
84
115
|
@controller = Controller.new
|
85
116
|
end
|
86
117
|
|
87
118
|
it "should dump env and not send notification" do
|
88
|
-
|
89
|
-
|
90
|
-
@dumper.
|
91
|
-
|
92
|
-
|
93
|
-
@dumper.
|
94
|
-
@dumper.
|
95
|
-
|
96
|
-
|
119
|
+
url = @dumper.dump(@controller, StandardError.new("dump it"))
|
120
|
+
url.should =~ /http:\/\/localhost\/[0-9]+/
|
121
|
+
@dumper.from_email = "asd"
|
122
|
+
url = @dumper.dump(@controller, StandardError.new("dump it"))
|
123
|
+
url.should =~ /http:\/\/localhost\/[0-9]+/
|
124
|
+
@dumper.from_email = nil
|
125
|
+
@dumper.to_emails = "dsa"
|
126
|
+
url = @dumper.dump(@controller, StandardError.new("dump it"))
|
127
|
+
url.should =~ /http:\/\/localhost\/[0-9]+/
|
97
128
|
end
|
98
129
|
|
99
130
|
it "should clean up error dumps" do
|
131
|
+
Error.create(:message => 'msg')
|
132
|
+
Error.all.size.should > 0
|
100
133
|
@dumper.keep_dumps = 0
|
134
|
+
Error.all.size.should == 0
|
101
135
|
@dumper.dump(@controller, StandardError.new("dump it"))
|
102
|
-
|
136
|
+
Error.all.size.should == 1
|
103
137
|
@dumper.dump(@controller, StandardError.new("dump it"))
|
104
|
-
|
138
|
+
Error.all.size.should == 2
|
105
139
|
end
|
106
140
|
|
107
141
|
it "should send notifications" do
|
108
|
-
@dumper.
|
109
|
-
@dumper.
|
142
|
+
@dumper.to_emails = "das"
|
143
|
+
@dumper.from_email = "asd"
|
110
144
|
@dumper.dump(@controller, StandardError.new("dump it"))
|
111
145
|
Ixtlan::Errors::ActionMailer::Base.delivered.should be_true
|
112
146
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'ixtlan/errors/error_dumper'
|
2
|
+
require 'date'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
class Controller
|
6
|
+
|
7
|
+
def request
|
8
|
+
self
|
9
|
+
end
|
10
|
+
def env
|
11
|
+
@env ||= {:one => 1, :two => 2, :three => 3}
|
12
|
+
end
|
13
|
+
|
14
|
+
def response
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def headers
|
19
|
+
@headers ||= { 'ONE' => 1, "TWO" => 2 }
|
20
|
+
end
|
21
|
+
|
22
|
+
def session
|
23
|
+
@session ||= { "user" => "me" }
|
24
|
+
end
|
25
|
+
|
26
|
+
def params
|
27
|
+
@params ||= { :query => "all" }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class NilClass
|
32
|
+
def blank?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class String
|
38
|
+
def blank?
|
39
|
+
size == 0
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Fixnum
|
44
|
+
def ago
|
45
|
+
DateTime.now - self.to_i
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# class DateTime
|
50
|
+
# def tv_sec
|
51
|
+
# sec
|
52
|
+
# end
|
53
|
+
# def tv_usec
|
54
|
+
# 0
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
|
58
|
+
describe Ixtlan::Errors::ErrorHandler do
|
59
|
+
|
60
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ixtlan-error-handler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 1
|
8
|
-
- 1
|
9
|
-
version: 0.1.1
|
4
|
+
prerelease:
|
5
|
+
version: 0.2.0
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- mkristian
|
@@ -14,51 +10,75 @@ autorequire:
|
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
12
|
|
17
|
-
date: 2011-
|
13
|
+
date: 2011-11-04 00:00:00 +05:30
|
18
14
|
default_executable:
|
19
15
|
dependencies:
|
20
16
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
17
|
+
name: slf4r
|
22
18
|
prerelease: false
|
23
19
|
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
24
21
|
requirements:
|
25
|
-
- -
|
22
|
+
- - ~>
|
26
23
|
- !ruby/object:Gem::Version
|
27
|
-
|
28
|
-
|
29
|
-
- 4
|
30
|
-
- 0
|
31
|
-
version: 2.4.0
|
32
|
-
type: :development
|
24
|
+
version: 0.4.2
|
25
|
+
type: :runtime
|
33
26
|
version_requirements: *id001
|
34
27
|
- !ruby/object:Gem::Dependency
|
35
|
-
name:
|
28
|
+
name: rspec
|
36
29
|
prerelease: false
|
37
30
|
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
38
32
|
requirements:
|
39
33
|
- - "="
|
40
34
|
- !ruby/object:Gem::Version
|
41
|
-
|
42
|
-
- 0
|
43
|
-
- 8
|
44
|
-
- 7
|
45
|
-
version: 0.8.7
|
35
|
+
version: 2.6.0
|
46
36
|
type: :development
|
47
37
|
version_requirements: *id002
|
48
38
|
- !ruby/object:Gem::Dependency
|
49
|
-
name:
|
39
|
+
name: rake
|
50
40
|
prerelease: false
|
51
41
|
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
52
43
|
requirements:
|
53
|
-
- - "
|
44
|
+
- - "="
|
54
45
|
- !ruby/object:Gem::Version
|
55
|
-
|
56
|
-
- 0
|
57
|
-
- 0
|
58
|
-
- 0
|
59
|
-
version: 0.0.0
|
46
|
+
version: 0.8.7
|
60
47
|
type: :development
|
61
48
|
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: dm-core
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - "="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 1.2.0
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: dm-migrations
|
62
|
+
prerelease: false
|
63
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - "="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.2.0
|
69
|
+
type: :development
|
70
|
+
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: dm-sqlite-adapter
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - "="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 1.2.0
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id006
|
62
82
|
description: dump errors on filesystem and notify developers, map different errors to specific pages
|
63
83
|
email:
|
64
84
|
- m.kristian@web.de
|
@@ -69,15 +89,25 @@ extensions: []
|
|
69
89
|
extra_rdoc_files: []
|
70
90
|
|
71
91
|
files:
|
92
|
+
- MIT-LICENSE
|
93
|
+
- README.textile
|
72
94
|
- lib/ixtlan-error-handler.rb
|
73
|
-
- lib/ixtlan/errors/
|
74
|
-
- lib/ixtlan/errors/error_handler.rb
|
95
|
+
- lib/ixtlan/errors/error_dumper.rb
|
75
96
|
- lib/ixtlan/errors/mailer.rb
|
97
|
+
- lib/ixtlan/errors/error_handling.rb~
|
98
|
+
- lib/ixtlan/errors/rescue_module.rb~
|
76
99
|
- lib/ixtlan/errors/rescue_module.rb
|
77
|
-
- lib/ixtlan/errors/error_dumper.rb
|
100
|
+
- lib/ixtlan/errors/error_dumper.rb~
|
101
|
+
- lib/ixtlan/errors/error_handler.rb~
|
102
|
+
- lib/ixtlan/errors/railtie.rb~
|
103
|
+
- lib/ixtlan/errors/error_handler.rb
|
104
|
+
- lib/ixtlan/errors/railtie.rb
|
105
|
+
- lib/ixtlan/errors/mailer.rb~
|
78
106
|
- lib/ixtlan/errors/mailer/error_notification.erb
|
79
|
-
- spec/error_dumper_spec.rb
|
80
107
|
- spec/error_handler_spec.rb
|
108
|
+
- spec/error_dumper_spec.rb~
|
109
|
+
- spec/error_handler_spec.rb~
|
110
|
+
- spec/error_dumper_spec.rb
|
81
111
|
has_rdoc: true
|
82
112
|
homepage: http://github.com/mkristian/ixtlan-error-handler
|
83
113
|
licenses:
|
@@ -89,26 +119,24 @@ rdoc_options:
|
|
89
119
|
require_paths:
|
90
120
|
- lib
|
91
121
|
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
92
123
|
requirements:
|
93
124
|
- - ">="
|
94
125
|
- !ruby/object:Gem::Version
|
95
|
-
segments:
|
96
|
-
- 0
|
97
126
|
version: "0"
|
98
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
99
129
|
requirements:
|
100
130
|
- - ">="
|
101
131
|
- !ruby/object:Gem::Version
|
102
|
-
segments:
|
103
|
-
- 0
|
104
132
|
version: "0"
|
105
133
|
requirements: []
|
106
134
|
|
107
135
|
rubyforge_project:
|
108
|
-
rubygems_version: 1.
|
136
|
+
rubygems_version: 1.5.1
|
109
137
|
signing_key:
|
110
138
|
specification_version: 3
|
111
139
|
summary: dump errors on filesystem and notify developers
|
112
140
|
test_files:
|
113
|
-
- spec/error_dumper_spec.rb
|
114
141
|
- spec/error_handler_spec.rb
|
142
|
+
- spec/error_dumper_spec.rb
|