super_exception_notifier 2.0.8 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/{README.rdoc → README} +13 -17
  2. data/VERSION.yml +3 -3
  3. data/lib/exception_notification.rb +15 -0
  4. data/rails/init.rb +8 -12
  5. data/super_exception_notifier.gemspec +88 -0
  6. data/test/exception_notifier_helper_test.rb +2 -10
  7. data/test/exception_notifier_test.rb +2 -2
  8. data/test/exception_notify_functional_test.rb +6 -6
  9. data/test/mocks/controllers.rb +22 -15
  10. data/test/notifiable_test.rb +6 -6
  11. data/test/test_helper.rb +6 -4
  12. metadata +35 -33
  13. data/lib/exception_notifiable.rb +0 -177
  14. data/lib/exception_notifier.rb +0 -176
  15. data/lib/exception_notifier_helper.rb +0 -60
  16. data/lib/notifiable.rb +0 -92
  17. data/lib/super_exception_notifier/custom_exception_classes.rb +0 -16
  18. data/lib/super_exception_notifier/custom_exception_methods.rb +0 -50
  19. data/lib/super_exception_notifier/deprecated_methods.rb +0 -60
  20. data/lib/super_exception_notifier/git_blame.rb +0 -52
  21. data/lib/super_exception_notifier/helpful_hashes.rb +0 -66
  22. data/lib/super_exception_notifier/hooks_notifier.rb +0 -55
  23. data/lib/super_exception_notifier/notifiable_helper.rb +0 -80
  24. data/tasks/notified_task.rake +0 -15
  25. data/views/exception_notifier/_backtrace.html.erb +0 -1
  26. data/views/exception_notifier/_environment.html.erb +0 -14
  27. data/views/exception_notifier/_inspect_model.html.erb +0 -16
  28. data/views/exception_notifier/_request.html.erb +0 -8
  29. data/views/exception_notifier/_session.html.erb +0 -6
  30. data/views/exception_notifier/_title.html.erb +0 -3
  31. data/views/exception_notifier/background_exception_notification.text.plain.erb +0 -10
  32. data/views/exception_notifier/exception_notification.text.plain.erb +0 -15
  33. data/views/exception_notifier/rake_exception_notification.text.plain.erb +0 -6
@@ -1,60 +0,0 @@
1
- require 'pp'
2
-
3
- module ExceptionNotifierHelper
4
- VIEW_PATH = "views/exception_notifier" unless defined?(VIEW_PATH)
5
- APP_PATH = "#{RAILS_ROOT}/app/#{VIEW_PATH}" unless defined?(APP_PATH)
6
- PARAM_FILTER_REPLACEMENT = "[FILTERED]" unless defined?(PARAM_FILTER_REPLACEMENT)
7
- COMPAT_MODE = defined?(RAILS_GEM_VERSION) ? RAILS_GEM_VERSION < '2' : false unless defined?(COMPAT_MODE)
8
-
9
- def render_section(section)
10
- RAILS_DEFAULT_LOGGER.info("rendering section #{section.inspect}")
11
- summary = render_overridable(section).strip
12
- unless summary.blank?
13
- title = render_overridable(:title, :locals => { :title => section }).strip
14
- "#{title}\n\n#{summary.gsub(/^/, " ")}\n\n"
15
- end
16
- end
17
-
18
- def render_overridable(partial, options={})
19
- if File.exist?(path = "#{APP_PATH}/_#{partial}.html.erb") ||
20
- File.exist?(path = "#{File.dirname(__FILE__)}/../#{VIEW_PATH}/_#{partial}.html.erb") ||
21
- File.exist?(path = "#{APP_PATH}/_#{partial}.rhtml") ||
22
- File.exist?(path = "#{APP_PATH}/_#{partial}.erb")
23
- render(options.merge(:file => path, :use_full_path => false))
24
- else
25
- ""
26
- end
27
- end
28
-
29
- def inspect_model_object(model, locals={})
30
- render_overridable(:inspect_model,
31
- :locals => { :inspect_model => model,
32
- :show_instance_variables => true,
33
- :show_attributes => true }.merge(locals))
34
- end
35
-
36
- def inspect_value(value)
37
- len = 512
38
- result = object_to_yaml(value).gsub(/\n/, "\n ").strip
39
- result = result[0,len] + "... (#{result.length-len} bytes more)" if result.length > len+20
40
- result
41
- end
42
-
43
- def object_to_yaml(object)
44
- object.to_yaml.sub(/^---\s*/m, "")
45
- end
46
-
47
- def exclude_raw_post_parameters?
48
- @controller && @controller.respond_to?(:filter_parameters)
49
- end
50
-
51
- def filter_sensitive_post_data_parameters(parameters)
52
- exclude_raw_post_parameters? ? COMPAT_MODE ? @controller.filter_parameters(parameters) : @controller.__send__(:filter_parameters, parameters) : parameters
53
- end
54
-
55
- def filter_sensitive_post_data_from_env(env_key, env_value)
56
- return env_value unless exclude_raw_post_parameters?
57
- return PARAM_FILTER_REPLACEMENT if (env_key =~ /RAW_POST_DATA/i)
58
- return COMPAT_MODE ? @controller.filter_parameters({env_key => env_value}).values[0] : @controller.__send__(:filter_parameters, {env_key => env_value}).values[0]
59
- end
60
- end
data/lib/notifiable.rb DELETED
@@ -1,92 +0,0 @@
1
- module Notifiable
2
- include SuperExceptionNotifier::NotifiableHelper
3
-
4
- def self.included(base)
5
- base.extend ClassMethods
6
-
7
- # Verbosity of the gem
8
- base.cattr_accessor :notifiable_verbose
9
- base.notifiable_verbose = false
10
- # Do Not Ever send error notification emails for these Error Classes
11
- base.cattr_accessor :notifiable_silent_exceptions
12
- base.notifiable_silent_exceptions = SILENT_EXCEPTIONS
13
- # Notification Level
14
- base.cattr_accessor :notifiable_notification_level
15
- base.notifiable_notification_level = [:email, :web_hooks]
16
-
17
- # Since there is no concept of locality from a request here allow user to explicitly define which env's are noisy (send notifications)
18
- base.cattr_accessor :notifiable_noisy_environments
19
- base.notifiable_noisy_environments = [:production]
20
- end
21
-
22
- module ClassMethods
23
- # set the exception_data deliverer OR retrieve the exception_data
24
- def exception_data(deliverer = nil)
25
- if deliverer
26
- write_inheritable_attribute(:exception_data, deliverer)
27
- else
28
- read_inheritable_attribute(:exception_data)
29
- end
30
- end
31
-
32
- def be_silent_for_exception?(exception)
33
- self.notifiable_silent_exceptions.respond_to?(:any?) && self.notifiable_silent_exceptions.any? {|klass| klass === exception }
34
- end
35
-
36
- end
37
-
38
- # Usage:
39
- # notifiable { Klass.some_method }
40
- # This will rescue any errors that occur within Klass.some_method
41
- def notifiable(&block)
42
- yield
43
- rescue => exception
44
- rescue_with_hooks(exception)
45
- raise
46
- end
47
-
48
- def be_silent_for_exception?(exception)
49
- self.class.be_silent_for_exception?(exception)
50
- end
51
-
52
- private
53
-
54
- def notification_level_sends_email?
55
- self.class.notifiable_notification_level.include?(:email)
56
- end
57
-
58
- def notification_level_sends_web_hooks?
59
- self.class.notifiable_notification_level.include?(:web_hooks)
60
- end
61
-
62
- def rescue_with_hooks(exception)
63
- verbose = self.class.notifiable_verbose
64
- puts "[RESCUE STYLE] rescue_with_hooks" if verbose
65
- data = get_exception_data
66
- # With ExceptionNotifiable you have an inherent request, and using a status code makes sense.
67
- # With Notifiable class to wrap around everything that doesn't have a request,
68
- # the errors you want to be notified of need to be specified either positively or negatively
69
- # 1. positive eg. set ExceptionNotifier.config[:notify_error_classes] to an array of classes
70
- # set ExceptionNotifier.config[:notify_other_errors] to false
71
- # 1. negative eg. set Klass.silent_exceptions to the ones to keep quiet
72
- # set ExceptionNotifier.config[:notify_other_errors] to true
73
- status_code = nil
74
- #We only send email if it has been configured in environment
75
- send_email = should_email_on_exception?(exception, status_code, verbose)
76
- #We only send web hooks if they've been configured in environment
77
- send_web_hooks = should_web_hook_on_exception?(exception, status_code, verbose)
78
- the_blamed = ExceptionNotifier.config[:git_repo_path].nil? ? nil : lay_blame(exception)
79
- rejected_sections = %w(request session)
80
- verbose_output(exception, status_code, "rescued by handler", send_email, send_web_hooks, nil, the_blamed, rejected_sections) if verbose
81
- # Send the exception notification email
82
- perform_exception_notify_mailing(exception, data, nil, the_blamed, verbose, rejected_sections) if send_email
83
- # Send Web Hook requests
84
- HooksNotifier.deliver_exception_to_web_hooks(ExceptionNotifier.config, exception, self, request, data, the_blamed) if send_web_hooks
85
- end
86
-
87
- def is_local? #like asking is_silent?
88
- eenv = defined?(Rails) ? Rails.env : RAILS_ENV
89
- !self.notifiable_noisy_environments.include?(eenv)
90
- end
91
-
92
- end
@@ -1,16 +0,0 @@
1
- #Copyright (c) 2008-2009 Peter H. Boling of 9thBit LLC
2
- #Released under the MIT license
3
-
4
- module SuperExceptionNotifier
5
- module CustomExceptionClasses
6
-
7
- class AccessDenied < StandardError; end
8
- class ResourceGone < StandardError; end
9
- class NotImplemented < StandardError; end
10
- class PageNotFound < StandardError; end
11
- class InvalidMethod < StandardError; end
12
- class CorruptData < StandardError; end
13
- class MethodDisabled < StandardError; end
14
-
15
- end
16
- end
@@ -1,50 +0,0 @@
1
- #Copyright (c) 2008-2009 Peter H. Boling of 9thBit LLC
2
- #Released under the MIT license
3
-
4
- module SuperExceptionNotifier
5
- module CustomExceptionMethods
6
-
7
- protected
8
-
9
- #For a while after disabling a route/URL that had been functional we should set it to resource gone to inform people to remove bookmarks.
10
- def resource_gone
11
- raise ResourceGone
12
- end
13
- #Then for things that have never existed or have not for a long time we call not_implemented
14
- def not_implemented
15
- raise NotImplemented
16
- end
17
- #Resources that must be requested with a specific HTTP Method (GET, PUT, POST, DELETE, AJAX, etc) but are requested otherwise should:
18
- def invalid_method
19
- raise InvalidMethod
20
- end
21
- #If your ever at a spot in the code that should never get reached, but corrupt data might get you there anyways then this is for you:
22
- def corrupt_data
23
- raise CorruptData
24
- end
25
- def page_not_found
26
- raise PageNotFound
27
- end
28
- def record_not_found
29
- raise ActiveRecord::RecordNotFound
30
- end
31
- def method_disabled
32
- raise MethodDisabled
33
- end
34
- #The current user does not have enough privileges to access the requested resource
35
- def access_denied
36
- raise AccessDenied
37
- end
38
-
39
- def generic_error
40
- error_stickie("Sorry, an error has occurred.")
41
- corrupt_data
42
- end
43
-
44
- def invalid_page
45
- error_stickie("Sorry, the page number you requested was not valid.")
46
- page_not_found
47
- end
48
-
49
- end
50
- end
@@ -1,60 +0,0 @@
1
- #Copyright (c) 2008-2009 Peter H. Boling of 9thBit LLC
2
- #Released under the MIT license
3
-
4
- module SuperExceptionNotifier
5
- module DeprecatedMethods
6
- @@namespacing = "Better namespacing to allow for Notifiable and ExceptionNotifiable to have similar APIs"
7
- @@rails2ruby = "An effort to make this a 'Ruby' Gem and not a strictly 'Rails' Gem"
8
-
9
- def http_error_codes
10
- deprecation_warning("http_error_codes", "error_class_status_codes")
11
- error_class_status_codes
12
- end
13
-
14
- def http_error_codes=(arg)
15
- deprecation_warning("http_error_codes", "error_class_status_codes")
16
- error_class_status_codes=(arg)
17
- end
18
-
19
- def rails_error_codes
20
- deprecation_warning("rails_error_codes", "error_class_status_codes", @@rails2ruby)
21
- error_class_status_codes
22
- end
23
-
24
- def rails_error_codes=(arg)
25
- deprecation_warning("rails_error_codes=", "error_class_status_codes=", @@rails2ruby)
26
- error_class_status_codes=(arg)
27
- end
28
-
29
- # Now defined in Object class by init.rb & Notifiable module,
30
- # so we need to override them for with the controller settings
31
- def exception_notifier_verbose
32
- deprecation_warning("exception_notifier_verbose", "exception_notifiable_verbose", @@namespacing)
33
- exception_notifiable_verbose
34
- end
35
- def silent_exceptions
36
- deprecation_warning("silent_exceptions", "exception_notifiable_silent_exceptions", @@namespacing)
37
- exception_notifiable_silent_exceptions
38
- end
39
- def notification_level
40
- deprecation_warning("notification_level", "exception_notifiable_notification_level", @@namespacing)
41
- exception_notifiable_notification_level
42
- end
43
- def exception_notifier_verbose=(arg)
44
- deprecation_warning("exception_notifier_verbose=", "exception_notifiable_verbose=", @@namespacing)
45
- exception_notifiable_verbose = arg
46
- end
47
- def silent_exceptions=(arg)
48
- deprecation_warning("silent_exceptions=", "exception_notifiable_silent_exceptions=", @@namespacing)
49
- exception_notifiable_silent_exceptions = arg
50
- end
51
- def notification_level=(arg)
52
- deprecation_warning("notification_level=", "exception_notifiable_notification_level=", @@namespacing)
53
- exception_notifiable_notification_level = arg
54
- end
55
-
56
- def deprecation_warning(old, new, reason = "")
57
- puts "[DEPRECATION WARNING] ** Method '#{old}' has been replaced by '#{new}', please update your code.#{' Reason for change: ' + reason + '.' if reason}"
58
- end
59
- end
60
- end
@@ -1,52 +0,0 @@
1
- #Copyright (c) 2008-2009 Peter H. Boling of 9thBit LLC
2
- #Released under the MIT license
3
-
4
- module SuperExceptionNotifier
5
- module GitBlame
6
-
7
- def lay_blame(exception)
8
- error = {}
9
- unless(ExceptionNotifier.config[:git_repo_path].nil?)
10
- if(exception.class == ActionView::TemplateError)
11
- blame = blame_output(exception.line_number, "app/views/#{exception.file_name}")
12
- error[:author] = blame[/^author\s.+$/].gsub(/author\s/,'')
13
- error[:line] = exception.line_number
14
- error[:file] = exception.file_name
15
- else
16
- exception.backtrace.each do |line|
17
- file = exception_in_project?(line[/^.+?(?=:)/])
18
- unless(file.nil?)
19
- line_number = line[/:\d+:/].gsub(/[^\d]/,'')
20
- # Use relative path or weird stuff happens
21
- blame = blame_output(line_number, file.gsub(Regexp.new("#{RAILS_ROOT}/"),''))
22
- error[:author] = blame[/^author\s.+$/].sub(/author\s/,'')
23
- error[:line] = line_number
24
- error[:file] = file
25
- break
26
- end
27
- end
28
- end
29
- end
30
- error
31
- end
32
-
33
- def blame_output(line_number, path)
34
- app_directory = Dir.pwd
35
- Dir.chdir ExceptionNotifier.config[:git_repo_path]
36
- blame = `git blame -p -L #{line_number},#{line_number} #{path}`
37
- Dir.chdir app_directory
38
-
39
- blame
40
- end
41
-
42
- def exception_in_project?(path) # should be a path like /path/to/broken/thingy.rb
43
- dir = File.split(path).first rescue ''
44
- if(File.directory?(dir) and !(path =~ /vendor\/plugins/) and !(path =~ /vendor\/gems/) and path.include?(RAILS_ROOT))
45
- path
46
- else
47
- nil
48
- end
49
- end
50
-
51
- end
52
- end
@@ -1,66 +0,0 @@
1
- #Copyright (c) 2008-2009 Peter H. Boling of 9thBit LLC
2
- #Released under the MIT license
3
-
4
- module SuperExceptionNotifier
5
- module HelpfulHashes
6
- unless defined?(SILENT_EXCEPTIONS)
7
- noiseless = []
8
- noiseless << ActiveRecord::RecordNotFound if defined?(ActiveRecord)
9
- if defined?(ActionController)
10
- noiseless << ActionController::UnknownController
11
- noiseless << ActionController::UnknownAction
12
- noiseless << ActionController::RoutingError
13
- noiseless << ActionController::MethodNotAllowed
14
- end
15
- SILENT_EXCEPTIONS = noiseless
16
- end
17
-
18
- # TODO: use ActionController::StatusCodes
19
- HTTP_STATUS_CODES = {
20
- "400" => "Bad Request",
21
- "403" => "Forbidden",
22
- "404" => "Not Found",
23
- "405" => "Method Not Allowed",
24
- "410" => "Gone",
25
- "418" => "I'm a teapot",
26
- "422" => "Unprocessable Entity",
27
- "423" => "Locked",
28
- "500" => "Internal Server Error",
29
- "501" => "Not Implemented",
30
- "503" => "Service Unavailable"
31
- } unless defined?(HTTP_STATUS_CODES)
32
-
33
- def codes_for_error_classes
34
- #TODO: Format whitespace
35
- classes = {
36
- # These are standard errors in rails / ruby
37
- NameError => "503",
38
- TypeError => "503",
39
- RuntimeError => "500",
40
- ArgumentError => "500",
41
- # These are custom error names defined in lib/super_exception_notifier/custom_exception_classes
42
- AccessDenied => "403",
43
- PageNotFound => "404",
44
- InvalidMethod => "405",
45
- ResourceGone => "410",
46
- CorruptData => "422",
47
- NoMethodError => "500",
48
- NotImplemented => "501",
49
- MethodDisabled => "200"
50
- }
51
- # Highly dependent on the verison of rails, so we're very protective about these'
52
- classes.merge!({ ActionView::TemplateError => "500"}) if defined?(ActionView) && ActionView.const_defined?(:TemplateError)
53
- classes.merge!({ ActiveRecord::RecordNotFound => "400" }) if defined?(ActiveRecord) && ActiveRecord.const_defined?(:RecordNotFound)
54
- classes.merge!({ ActiveResource::ResourceNotFound => "404" }) if defined?(ActiveResource) && ActiveResource.const_defined?(:ResourceNotFound)
55
-
56
- if defined?(ActionController)
57
- classes.merge!({ ActionController::UnknownController => "404" }) if ActionController.const_defined?(:UnknownController)
58
- classes.merge!({ ActionController::MissingTemplate => "404" }) if ActionController.const_defined?(:MissingTemplate)
59
- classes.merge!({ ActionController::MethodNotAllowed => "405" }) if ActionController.const_defined?(:MethodNotAllowed)
60
- classes.merge!({ ActionController::UnknownAction => "501" }) if ActionController.const_defined?(:UnknownAction)
61
- classes.merge!({ ActionController::RoutingError => "404" }) if ActionController.const_defined?(:RoutingError)
62
- classes.merge!({ ActionController::InvalidAuthenticityToken => "405" }) if ActionController.const_defined?(:InvalidAuthenticityToken)
63
- end
64
- end
65
- end
66
- end
@@ -1,55 +0,0 @@
1
- require 'net/http'
2
- require 'uri'
3
-
4
- module SuperExceptionNotifier
5
- module HooksNotifier
6
- # Deliver exception data hash to web hooks, if any
7
- #
8
- def self.deliver_exception_to_web_hooks(config, exception, controller, request, data={}, the_blamed = nil)
9
- params = build_web_hook_params(config, exception, controller, request, data, the_blamed)
10
- # TODO: use threads here
11
- config[:web_hooks].each do |address|
12
- post_hook(params, address)
13
- end
14
- end
15
-
16
-
17
- # Parameters hash based on Merb Exceptions example
18
- #
19
- def self.build_web_hook_params(config, exception, controller, request, data={}, the_blamed = nil)
20
- host = (request.env["HTTP_X_FORWARDED_HOST"] || request.env["HTTP_HOST"])
21
- p = {
22
- 'environment' => (defined?(Rails) ? Rails.env : RAILS_ENV),
23
- 'exceptions' => [{
24
- :class => exception.class.to_s,
25
- :backtrace => exception.backtrace,
26
- :message => exception.message
27
- }],
28
- 'app_name' => config[:app_name],
29
- 'version' => config[:version],
30
- 'blame' => "#{the_blamed}"
31
- }
32
- if !request.nil?
33
- p.merge!({'request_url' => "#{request.protocol}#{host}#{request.request_uri}"})
34
- p.merge!({'request_action' => request.parameters['action']})
35
- p.merge!({'request_params' => request.parameters.inspect})
36
- end
37
- p.merge!({'request_controller' => controller.class.name}) if !controller.nil?
38
- p.merge!({'status' => exception.status}) if exception.respond_to?(:status)
39
- return p
40
- end
41
-
42
- def self.post_hook(params, address)
43
- uri = URI.parse(address)
44
- uri.path = '/' if uri.path=='' # set a path if one isn't provided to keep Net::HTTP happy
45
-
46
- headers = { 'Content-Type' => 'text/x-json' }
47
- data = params.to_json
48
- Net::HTTP.start(uri.host, uri.port) do |http|
49
- http.request_post(uri.path, data, headers)
50
- end
51
- data
52
- end
53
-
54
- end
55
- end