panmind-exception_notification 2.3.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,148 @@
1
+ = Purpose of this fork
2
+
3
+ Do not escape exception e-mails text as HTML. Plain and simple.
4
+
5
+ = Exception Notifier Plugin for Rails
6
+
7
+ The Exception Notifier plugin provides a mailer object and a default set of
8
+ templates for sending email notifications when errors occur in a Rails
9
+ application. The plugin is configurable, allowing programmers to specify:
10
+
11
+ * the sender address of the email
12
+ * the recipient addresses
13
+ * the text used to prefix the subject line
14
+
15
+ The email includes information about the current request, session, and
16
+ environment, and also gives a backtrace of the exception.
17
+
18
+ == Usage
19
+
20
+ First, include the ExceptionNotifiable mixin in whichever controller you want
21
+ to generate error emails (typically ApplicationController):
22
+
23
+ class ApplicationController < ActionController::Base
24
+ include ExceptionNotification::Notifiable
25
+ ...
26
+ end
27
+
28
+ Then, specify the email recipients in your environment:
29
+
30
+ ExceptionNotification::Notifier.exception_recipients = %w(joe@schmoe.com bill@schmoe.com)
31
+
32
+ And that's it! The defaults take care of the rest.
33
+
34
+ == Configuration
35
+
36
+ You can tweak other values to your liking, as well. In your environment file,
37
+ just set any or all of the following values:
38
+
39
+ # defaults to exception.notifier@default.com
40
+ ExceptionNotification::Notifier.sender_address =
41
+ %("Application Error" <app.error@myapp.com>)
42
+
43
+ # defaults to "[ERROR] "
44
+ ExceptionNotification::Notifier.email_prefix = "[APP] "
45
+
46
+ Even if you have mixed into ApplicationController you can skip notification in
47
+ some controllers by
48
+
49
+ class MyController < ApplicationController
50
+ skip_exception_notifications
51
+ end
52
+
53
+ == Deprecated local_request? overriding
54
+
55
+ Email notifications will only occur when the IP address is determined not to
56
+ be local. You can specify certain addresses to always be local so that you'll
57
+ get a detailed error instead of the generic error page. You do this in your
58
+ controller (or even per-controller):
59
+
60
+ consider_local "64.72.18.143", "14.17.21.25"
61
+
62
+ You can specify subnet masks as well, so that all matching addresses are
63
+ considered local:
64
+
65
+ consider_local "64.72.18.143/24"
66
+
67
+ The address "127.0.0.1" is always considered local. If you want to completely
68
+ reset the list of all addresses (for instance, if you wanted "127.0.0.1" to
69
+ NOT be considered local), you can simply do, somewhere in your controller:
70
+
71
+ local_addresses.clear
72
+
73
+ NOTE: The above functionality has has been pulled out to consider_local.rb,
74
+ as interfering with rails local determination is orthogonal to notification,
75
+ unnecessarily clutters backtraces, and even occasionally errs on odd ip or
76
+ requests bugs. To return original functionality add an initializer with:
77
+
78
+ ActionController::Base.send :include, ConsiderLocal
79
+
80
+ or just include it per controller that wants it
81
+
82
+ class MyController < ApplicationController
83
+ include ExceptionNotification::ConsiderLocal
84
+ end
85
+
86
+ == Customization
87
+
88
+ By default, the notification email includes four parts: request, session,
89
+ environment, and backtrace (in that order). You can customize how each of those
90
+ sections are rendered by placing a partial named for that part in your
91
+ app/views/exception_notifier directory (e.g., _session.rhtml). Each partial has
92
+ access to the following variables:
93
+
94
+ * @controller: the controller that caused the error
95
+ * @request: the current request object
96
+ * @exception: the exception that was raised
97
+ * @host: the name of the host that made the request
98
+ * @backtrace: a sanitized version of the exception's backtrace
99
+ * @rails_root: a sanitized version of RAILS_ROOT
100
+ * @data: a hash of optional data values that were passed to the notifier
101
+ * @sections: the array of sections to include in the email
102
+
103
+ You can reorder the sections, or exclude sections completely, by altering the
104
+ ExceptionNotification::Notifier.sections variable. You can even add new sections that
105
+ describe application-specific data--just add the section's name to the list
106
+ (whereever you'd like), and define the corresponding partial. Then, if your
107
+ new section requires information that isn't available by default, make sure
108
+ it is made available to the email using the exception_data macro:
109
+
110
+ class ApplicationController < ActionController::Base
111
+ ...
112
+ protected
113
+ exception_data :additional_data
114
+
115
+ def additional_data
116
+ { :document => @document,
117
+ :person => @person }
118
+ end
119
+ ...
120
+ end
121
+
122
+ In the above case, @document and @person would be made available to the email
123
+ renderer, allowing your new section(s) to access and display them. See the
124
+ existing sections defined by the plugin for examples of how to write your own.
125
+
126
+ == 404s errors
127
+
128
+ Notification is skipped if you return a 404 status, which happens by default
129
+ for an ActiveRecord::RecordNotFound or ActionController::UnknownAction error.
130
+
131
+ == Manually notifying of error in a rescue block
132
+
133
+ If your controller action manually handles an error, the notifier will never be
134
+ run. To manually notify of an error call notify_about_exception from within the
135
+ rescue block
136
+
137
+ def index
138
+ #risky operation here
139
+ rescue StandardError => error
140
+ #custom error handling here
141
+ notify_about_exception(error)
142
+ end
143
+
144
+ == Support and tickets
145
+
146
+ https://rails.lighthouseapp.com/projects/8995-rails-plugins
147
+
148
+ Copyright (c) 2005 Jamis Buck, released under the MIT license
@@ -0,0 +1,7 @@
1
+ require "action_mailer"
2
+ module ExceptionNotification
3
+ autoload :Notifiable, 'exception_notification/notifiable'
4
+ autoload :Notifier, 'exception_notification/notifier'
5
+ #autoload :NotifierHelper, 'exception_notification/notifier_helper'
6
+ autoload :ConsiderLocal, 'exception_notification/consider_local'
7
+ end
@@ -0,0 +1,31 @@
1
+ #This didn't belong on ExceptionNotifier and made backtraces worse. To keep original functionality in place
2
+ #'ActionController::Base.send :include, ExceptionNotification::ConsiderLocal' or just include in your controller
3
+ module ExceptionNotification::ConsiderLocal
4
+ module ClassMethods
5
+ def self.included(target)
6
+ require 'ipaddr'
7
+ target.extend(ClassMethods)
8
+ end
9
+
10
+ def consider_local(*args)
11
+ local_addresses.concat(args.flatten.map { |a| IPAddr.new(a) })
12
+ end
13
+
14
+ def local_addresses
15
+ addresses = read_inheritable_attribute(:local_addresses)
16
+ unless addresses
17
+ addresses = [IPAddr.new("127.0.0.1")]
18
+ write_inheritable_attribute(:local_addresses, addresses)
19
+ end
20
+ addresses
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def local_request?
27
+ remote = IPAddr.new(request.remote_ip)
28
+ !self.class.local_addresses.detect { |addr| addr.include?(remote) }.nil?
29
+ end
30
+
31
+ end
@@ -0,0 +1,66 @@
1
+ # Copyright (c) 2005 Jamis Buck
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.
21
+ module ExceptionNotification::Notifiable
22
+ def self.included(target)
23
+ target.extend(ClassMethods)
24
+ target.skip_exception_notifications false
25
+ end
26
+
27
+ module ClassMethods
28
+ def exception_data(deliverer=self)
29
+ if deliverer == self
30
+ read_inheritable_attribute(:exception_data)
31
+ else
32
+ write_inheritable_attribute(:exception_data, deliverer)
33
+ end
34
+ end
35
+
36
+ def skip_exception_notifications(boolean=true)
37
+ write_inheritable_attribute(:skip_exception_notifications, boolean)
38
+ end
39
+
40
+ def skip_exception_notifications?
41
+ read_inheritable_attribute(:skip_exception_notifications)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def rescue_action_in_public(exception)
48
+ super
49
+ notify_about_exception(exception) if deliver_exception_notification?
50
+ end
51
+
52
+ def deliver_exception_notification?
53
+ !self.class.skip_exception_notifications? && ![404, "404 Not Found"].include?(response.status)
54
+ end
55
+
56
+ def notify_about_exception(exception)
57
+ deliverer = self.class.exception_data
58
+ data = case deliverer
59
+ when nil then {}
60
+ when Symbol then send(deliverer)
61
+ when Proc then deliverer.call(self)
62
+ end
63
+
64
+ ExceptionNotification::Notifier.deliver_exception_notification(exception, self, request, data)
65
+ end
66
+ end
@@ -0,0 +1,75 @@
1
+ require 'pathname'
2
+
3
+ # Copyright (c) 2005 Jamis Buck
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.
23
+ class ExceptionNotification::Notifier < ActionMailer::Base
24
+ self.mailer_name = 'exception_notifier'
25
+ self.view_paths << "#{File.dirname(__FILE__)}/../../views"
26
+
27
+ @@sender_address = %("Exception Notifier" <exception.notifier@default.com>)
28
+ cattr_accessor :sender_address
29
+
30
+ @@exception_recipients = []
31
+ cattr_accessor :exception_recipients
32
+
33
+ @@email_prefix = "[ERROR] "
34
+ cattr_accessor :email_prefix
35
+
36
+ @@sections = %w(request session environment backtrace)
37
+ cattr_accessor :sections
38
+
39
+ def self.reloadable?() false end
40
+
41
+ def exception_notification(exception, controller, request, data={})
42
+ source = self.class.exception_source(controller)
43
+ content_type "text/plain"
44
+
45
+ subject "#{email_prefix}#{source} (#{exception.class}) #{exception.message.inspect}"
46
+
47
+ recipients exception_recipients
48
+ from sender_address
49
+
50
+ body data.merge({ :controller => controller, :request => request,
51
+ :exception => exception, :exception_source => source, :host => (request.env["HTTP_X_FORWARDED_HOST"] || request.env["HTTP_HOST"]),
52
+ :backtrace => sanitize_backtrace(exception.backtrace),
53
+ :rails_root => rails_root, :data => data,
54
+ :sections => sections })
55
+ end
56
+
57
+ def self.exception_source(controller)
58
+ if controller.respond_to?(:controller_name)
59
+ "in #{controller.controller_name}##{controller.action_name}"
60
+ else
61
+ "outside of a controller"
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def sanitize_backtrace(trace)
68
+ re = Regexp.new(/^#{Regexp.escape(rails_root)}/)
69
+ trace.map { |line| Pathname.new(line.gsub(re, "[RAILS_ROOT]")).cleanpath.to_s }
70
+ end
71
+
72
+ def rails_root
73
+ @rails_root ||= Pathname.new(RAILS_ROOT).cleanpath.to_s
74
+ end
75
+ end
@@ -0,0 +1,66 @@
1
+ require 'pp'
2
+
3
+ # Copyright (c) 2005 Jamis Buck
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.
23
+ module ExceptionNotification::NotifierHelper
24
+ PARAM_FILTER_REPLACEMENT = "[FILTERED]"
25
+
26
+ def render_section(section)
27
+ summary = render("exception_notifier/#{section}").strip
28
+ unless summary.blank?
29
+ title = render("exception_notifier/title", :title => section).strip
30
+ "#{title}\n\n#{summary.gsub(/^/, " ")}\n\n"
31
+ end
32
+ end
33
+
34
+ def inspect_model_object(model, locals={})
35
+ render('exception_notifier/inspect_model',
36
+ :locals => { :inspect_model => model,
37
+ :show_instance_variables => true,
38
+ :show_attributes => true }.merge(locals))
39
+ end
40
+
41
+ def inspect_value(value)
42
+ len = 512
43
+ result = object_to_yaml(value).gsub(/\n/, "\n ").strip
44
+ result = result[0,len] + "... (#{result.length-len} bytes more)" if result.length > len+20
45
+ result
46
+ end
47
+
48
+ def object_to_yaml(object)
49
+ object.to_yaml.sub(/^---\s*/m, "")
50
+ end
51
+
52
+ def exclude_raw_post_parameters?
53
+ @controller && @controller.respond_to?(:filter_parameters)
54
+ end
55
+
56
+ def filter_sensitive_post_data_parameters(parameters)
57
+ exclude_raw_post_parameters? ? @controller.__send__(:filter_parameters, parameters) : parameters
58
+ end
59
+
60
+ def filter_sensitive_post_data_from_env(env_key, env_value)
61
+ return env_value unless exclude_raw_post_parameters?
62
+ return PARAM_FILTER_REPLACEMENT if (env_key =~ /RAW_POST_DATA/i)
63
+ return @controller.__send__(:filter_parameters, {env_key => env_value}).values[0]
64
+ end
65
+
66
+ end
@@ -0,0 +1 @@
1
+ <%=raw @backtrace.join "\n" %>
@@ -0,0 +1,7 @@
1
+ <% max = @request.env.keys.max { |a,b| a.length <=> b.length } -%>
2
+ <% @request.env.keys.sort.each do |key| -%>
3
+ * <%=raw "%-*s: %s" % [max.length, key, filter_sensitive_post_data_from_env(key, @request.env[key].to_s.strip)] %>
4
+ <% end -%>
5
+
6
+ * Process: <%=raw $$ %>
7
+ * Server : <%=raw `hostname -s`.chomp %>
@@ -0,0 +1,16 @@
1
+ <% if show_attributes -%>
2
+ [attributes]
3
+ <% attrs = inspect_model.attributes -%>
4
+ <% max = attrs.keys.max { |a,b| a.length <=> b.length } -%>
5
+ <% attrs.keys.sort.each do |attr| -%>
6
+ * <%= "%*-s: %s" % [max.length, attr, object_to_yaml(attrs[attr]).gsub(/\n/, "\n ").strip] %>
7
+ <% end -%>
8
+ <% end -%>
9
+
10
+ <% if show_instance_variables -%>
11
+ [instance variables]
12
+ <% inspect_model.instance_variables.sort.each do |variable| -%>
13
+ <%- next if variable == "@attributes" -%>
14
+ * <%= variable %>: <%= inspect_value(inspect_model.instance_variable_get(variable)) %>
15
+ <% end -%>
16
+ <% end -%>
@@ -0,0 +1,4 @@
1
+ * URL : <%=raw @request.url %>
2
+ * IP address: <%=raw @request.remote_ip %>
3
+ * Parameters: <%=raw filter_sensitive_post_data_parameters(@request.parameters).inspect %>
4
+ * Rails root: <%=raw Rails.root %>
@@ -0,0 +1,2 @@
1
+ * session id: <%=raw @request.session_options[:id] %>
2
+ * data: <%=raw PP.pp @request.session.inspect.gsub(/\n/, "\n ").strip.html_safe %>
@@ -0,0 +1,3 @@
1
+ -------------------------------
2
+ <%=raw title.to_s.humanize %>:
3
+ -------------------------------
@@ -0,0 +1,6 @@
1
+ A <%= @exception.class %> occurred <%= @exception_source %>:
2
+
3
+ <%=raw @exception.message %>
4
+ <%=raw @backtrace.first %>
5
+
6
+ <%=raw @sections.map { |section| render_section(section) }.join %>
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: panmind-exception_notification
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 2
8
+ - 3
9
+ - 10
10
+ version: 2.3.10
11
+ platform: ruby
12
+ authors:
13
+ - Jamis Buck
14
+ - Josh Peek
15
+ - Tim Connor
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2010-11-03 00:00:00 +01:00
21
+ default_executable:
22
+ dependencies: []
23
+
24
+ description:
25
+ email: timocratic@gmail.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - README.rdoc
34
+ - lib/exception_notification/consider_local.rb
35
+ - lib/exception_notification/notifiable.rb
36
+ - lib/exception_notification/notifier.rb
37
+ - lib/exception_notification/notifier_helper.rb
38
+ - lib/exception_notification.rb
39
+ - views/exception_notifier/_backtrace.rhtml
40
+ - views/exception_notifier/_environment.rhtml
41
+ - views/exception_notifier/_inspect_model.rhtml
42
+ - views/exception_notifier/_request.rhtml
43
+ - views/exception_notifier/_session.rhtml
44
+ - views/exception_notifier/_title.rhtml
45
+ - views/exception_notifier/exception_notification.rhtml
46
+ has_rdoc: true
47
+ homepage:
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.7
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Exception notification by email for Rails apps - 2.3-stable compatible version
80
+ test_files: []
81
+