exception_notification_more_info 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. checksums.yaml +7 -0
  2. data/Appraisals +7 -0
  3. data/CHANGELOG.rdoc +141 -0
  4. data/CODE_OF_CONDUCT.md +22 -0
  5. data/CONTRIBUTING.md +42 -0
  6. data/Gemfile +3 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +839 -0
  9. data/Rakefile +23 -0
  10. data/examples/sinatra/Gemfile +8 -0
  11. data/examples/sinatra/Gemfile.lock +95 -0
  12. data/examples/sinatra/Procfile +2 -0
  13. data/examples/sinatra/README.md +11 -0
  14. data/examples/sinatra/config.ru +3 -0
  15. data/examples/sinatra/sinatra_app.rb +32 -0
  16. data/exception_notification_more_info.gemspec +34 -0
  17. data/gemfiles/rails4_0.gemfile +7 -0
  18. data/gemfiles/rails4_1.gemfile +7 -0
  19. data/gemfiles/rails4_2.gemfile +7 -0
  20. data/lib/exception_notification.rb +11 -0
  21. data/lib/exception_notification/rack.rb +59 -0
  22. data/lib/exception_notification/rails.rb +8 -0
  23. data/lib/exception_notification/resque.rb +24 -0
  24. data/lib/exception_notification/sidekiq.rb +31 -0
  25. data/lib/exception_notifier.rb +121 -0
  26. data/lib/exception_notifier/base_notifier.rb +25 -0
  27. data/lib/exception_notifier/campfire_notifier.rb +36 -0
  28. data/lib/exception_notifier/email_notifier.rb +204 -0
  29. data/lib/exception_notifier/hipchat_notifier.rb +45 -0
  30. data/lib/exception_notifier/irc_notifier.rb +51 -0
  31. data/lib/exception_notifier/modules/backtrace_cleaner.rb +13 -0
  32. data/lib/exception_notifier/notifier.rb +16 -0
  33. data/lib/exception_notifier/slack_notifier.rb +73 -0
  34. data/lib/exception_notifier/views/exception_notifier/_backtrace.html.erb +3 -0
  35. data/lib/exception_notifier/views/exception_notifier/_backtrace.text.erb +1 -0
  36. data/lib/exception_notifier/views/exception_notifier/_data.html.erb +6 -0
  37. data/lib/exception_notifier/views/exception_notifier/_data.text.erb +1 -0
  38. data/lib/exception_notifier/views/exception_notifier/_environment.html.erb +10 -0
  39. data/lib/exception_notifier/views/exception_notifier/_environment.text.erb +5 -0
  40. data/lib/exception_notifier/views/exception_notifier/_request.html.erb +36 -0
  41. data/lib/exception_notifier/views/exception_notifier/_request.text.erb +10 -0
  42. data/lib/exception_notifier/views/exception_notifier/_session.html.erb +10 -0
  43. data/lib/exception_notifier/views/exception_notifier/_session.text.erb +2 -0
  44. data/lib/exception_notifier/views/exception_notifier/_title.html.erb +3 -0
  45. data/lib/exception_notifier/views/exception_notifier/_title.text.erb +3 -0
  46. data/lib/exception_notifier/views/exception_notifier/background_exception_notification.html.erb +53 -0
  47. data/lib/exception_notifier/views/exception_notifier/background_exception_notification.text.erb +14 -0
  48. data/lib/exception_notifier/views/exception_notifier/exception_notification.html.erb +54 -0
  49. data/lib/exception_notifier/views/exception_notifier/exception_notification.text.erb +24 -0
  50. data/lib/exception_notifier/webhook_notifier.rb +47 -0
  51. data/lib/generators/exception_notification/install_generator.rb +15 -0
  52. data/lib/generators/exception_notification/templates/exception_notification.rb +53 -0
  53. data/test/dummy/.gitignore +4 -0
  54. data/test/dummy/Gemfile +34 -0
  55. data/test/dummy/Gemfile.lock +137 -0
  56. data/test/dummy/Rakefile +7 -0
  57. data/test/dummy/app/controllers/application_controller.rb +3 -0
  58. data/test/dummy/app/controllers/posts_controller.rb +30 -0
  59. data/test/dummy/app/helpers/application_helper.rb +2 -0
  60. data/test/dummy/app/helpers/posts_helper.rb +2 -0
  61. data/test/dummy/app/models/post.rb +2 -0
  62. data/test/dummy/app/views/exception_notifier/_new_bkg_section.html.erb +1 -0
  63. data/test/dummy/app/views/exception_notifier/_new_bkg_section.text.erb +1 -0
  64. data/test/dummy/app/views/exception_notifier/_new_section.html.erb +1 -0
  65. data/test/dummy/app/views/exception_notifier/_new_section.text.erb +1 -0
  66. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  67. data/test/dummy/app/views/posts/_form.html.erb +0 -0
  68. data/test/dummy/app/views/posts/new.html.erb +0 -0
  69. data/test/dummy/app/views/posts/show.html.erb +0 -0
  70. data/test/dummy/config.ru +4 -0
  71. data/test/dummy/config/application.rb +42 -0
  72. data/test/dummy/config/boot.rb +6 -0
  73. data/test/dummy/config/database.yml +22 -0
  74. data/test/dummy/config/environment.rb +17 -0
  75. data/test/dummy/config/environments/development.rb +25 -0
  76. data/test/dummy/config/environments/production.rb +50 -0
  77. data/test/dummy/config/environments/test.rb +38 -0
  78. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  79. data/test/dummy/config/initializers/inflections.rb +10 -0
  80. data/test/dummy/config/initializers/mime_types.rb +5 -0
  81. data/test/dummy/config/initializers/secret_token.rb +8 -0
  82. data/test/dummy/config/initializers/session_store.rb +8 -0
  83. data/test/dummy/config/locales/en.yml +5 -0
  84. data/test/dummy/config/routes.rb +3 -0
  85. data/test/dummy/db/migrate/20110729022608_create_posts.rb +15 -0
  86. data/test/dummy/db/schema.rb +24 -0
  87. data/test/dummy/db/seeds.rb +7 -0
  88. data/test/dummy/lib/tasks/.gitkeep +0 -0
  89. data/test/dummy/public/404.html +26 -0
  90. data/test/dummy/public/422.html +26 -0
  91. data/test/dummy/public/500.html +26 -0
  92. data/test/dummy/public/favicon.ico +0 -0
  93. data/test/dummy/public/images/rails.png +0 -0
  94. data/test/dummy/public/index.html +239 -0
  95. data/test/dummy/public/javascripts/application.js +2 -0
  96. data/test/dummy/public/javascripts/controls.js +965 -0
  97. data/test/dummy/public/javascripts/dragdrop.js +974 -0
  98. data/test/dummy/public/javascripts/effects.js +1123 -0
  99. data/test/dummy/public/javascripts/prototype.js +6001 -0
  100. data/test/dummy/public/javascripts/rails.js +191 -0
  101. data/test/dummy/public/robots.txt +5 -0
  102. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  103. data/test/dummy/public/stylesheets/scaffold.css +56 -0
  104. data/test/dummy/script/rails +6 -0
  105. data/test/dummy/test/fixtures/posts.yml +11 -0
  106. data/test/dummy/test/functional/posts_controller_test.rb +224 -0
  107. data/test/dummy/test/test_helper.rb +13 -0
  108. data/test/exception_notification/rack_test.rb +20 -0
  109. data/test/exception_notifier/campfire_notifier_test.rb +100 -0
  110. data/test/exception_notifier/email_notifier_test.rb +185 -0
  111. data/test/exception_notifier/hipchat_notifier_test.rb +177 -0
  112. data/test/exception_notifier/irc_notifier_test.rb +121 -0
  113. data/test/exception_notifier/sidekiq_test.rb +27 -0
  114. data/test/exception_notifier/slack_notifier_test.rb +179 -0
  115. data/test/exception_notifier/webhook_notifier_test.rb +68 -0
  116. data/test/exception_notifier_test.rb +103 -0
  117. data/test/test_helper.rb +18 -0
  118. metadata +428 -0
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ Bundler::GemHelper.install_tasks
4
+ require 'appraisal'
5
+
6
+ require 'rake/testtask'
7
+
8
+ task 'setup_dummy_app' do
9
+ unless File.exists? "test/dummy/db/test.sqlite3"
10
+ Bundler.with_clean_env do
11
+ sh "cd test/dummy; bundle; bundle exec rake db:migrate; bundle exec rake db:test:prepare; cd ../../;"
12
+ end
13
+ end
14
+ end
15
+
16
+ Rake::TestTask.new(:test) do |t|
17
+ t.libs << 'lib'
18
+ t.libs << 'test'
19
+ t.pattern = 'test/**/*_test.rb'
20
+ t.verbose = true
21
+ end
22
+
23
+ task :default => [:setup_dummy_app, :test]
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "exception_notification", path: "../../"
4
+
5
+ gem "thin", "~> 1.5.1"
6
+ gem "sinatra", "~> 1.3.5"
7
+ gem "foreman"
8
+ gem "mailcatcher"
@@ -0,0 +1,95 @@
1
+ PATH
2
+ remote: ../../
3
+ specs:
4
+ exception_notification (3.0.1)
5
+ actionmailer (>= 3.0.4)
6
+ activesupport (>= 3.0.4)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actionmailer (3.2.13)
12
+ actionpack (= 3.2.13)
13
+ mail (~> 2.5.3)
14
+ actionpack (3.2.13)
15
+ activemodel (= 3.2.13)
16
+ activesupport (= 3.2.13)
17
+ builder (~> 3.0.0)
18
+ erubis (~> 2.7.0)
19
+ journey (~> 1.0.4)
20
+ rack (~> 1.4.5)
21
+ rack-cache (~> 1.2)
22
+ rack-test (~> 0.6.1)
23
+ sprockets (~> 2.2.1)
24
+ activemodel (3.2.13)
25
+ activesupport (= 3.2.13)
26
+ builder (~> 3.0.0)
27
+ activesupport (3.2.13)
28
+ i18n (= 0.6.1)
29
+ multi_json (~> 1.0)
30
+ builder (3.0.4)
31
+ daemons (1.1.9)
32
+ erubis (2.7.0)
33
+ eventmachine (1.0.3)
34
+ foreman (0.61.0)
35
+ thor (>= 0.13.6)
36
+ haml (4.0.2)
37
+ tilt
38
+ hike (1.2.1)
39
+ i18n (0.6.1)
40
+ journey (1.0.4)
41
+ mail (2.5.3)
42
+ i18n (>= 0.4.0)
43
+ mime-types (~> 1.16)
44
+ treetop (~> 1.4.8)
45
+ mailcatcher (0.5.11)
46
+ activesupport (~> 3.0)
47
+ eventmachine (~> 1.0.0)
48
+ haml (>= 3.1, < 5)
49
+ mail (~> 2.3)
50
+ sinatra (~> 1.2)
51
+ skinny (~> 0.2.3)
52
+ sqlite3 (~> 1.3)
53
+ thin (~> 1.5.0)
54
+ mime-types (1.22)
55
+ multi_json (1.7.2)
56
+ polyglot (0.3.3)
57
+ rack (1.4.5)
58
+ rack-cache (1.2)
59
+ rack (>= 0.4)
60
+ rack-protection (1.5.0)
61
+ rack
62
+ rack-test (0.6.2)
63
+ rack (>= 1.0)
64
+ sinatra (1.3.6)
65
+ rack (~> 1.4)
66
+ rack-protection (~> 1.3)
67
+ tilt (~> 1.3, >= 1.3.3)
68
+ skinny (0.2.3)
69
+ eventmachine (~> 1.0.0)
70
+ thin (~> 1.5.0)
71
+ sprockets (2.2.2)
72
+ hike (~> 1.2)
73
+ multi_json (~> 1.0)
74
+ rack (~> 1.0)
75
+ tilt (~> 1.1, != 1.3.0)
76
+ sqlite3 (1.3.7)
77
+ thin (1.5.1)
78
+ daemons (>= 1.0.9)
79
+ eventmachine (>= 0.12.6)
80
+ rack (>= 1.0.0)
81
+ thor (0.18.1)
82
+ tilt (1.3.6)
83
+ treetop (1.4.12)
84
+ polyglot
85
+ polyglot (>= 0.3.1)
86
+
87
+ PLATFORMS
88
+ ruby
89
+
90
+ DEPENDENCIES
91
+ exception_notification!
92
+ foreman
93
+ mailcatcher
94
+ sinatra (~> 1.3.5)
95
+ thin (~> 1.5.1)
@@ -0,0 +1,2 @@
1
+ web: bundle exec thin start --port 3000
2
+ mail: bundle exec mailcatcher --foreground --smtp-port 1025 --http-port 1080
@@ -0,0 +1,11 @@
1
+ # Using Exception Notification with Sinatra
2
+
3
+ ## Quick start
4
+
5
+ git clone git@github.com:smartinez87/exception_notification.git
6
+ cd exception_notification/examples/sinatra
7
+ bundle install
8
+ bundle exec foreman start
9
+
10
+
11
+ The last command starts two services, a smtp server and the sinatra app itself. Thus, visit [http://localhost:1080/](http://localhost:1080/) to check the emails sent and, in a separated tab, visit [http://localhost:3000](http://localhost:3000) and cause some errors. For more info, use the [source](https://github.com/smartinez87/exception_notification/blob/master/examples/sinatra/sinatra_app.rb) Luke.
@@ -0,0 +1,3 @@
1
+ require ::File.expand_path('../sinatra_app', __FILE__)
2
+
3
+ run SinatraApp
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'sinatra/base'
4
+ require 'exception_notification'
5
+
6
+ class SinatraApp < Sinatra::Base
7
+ use Rack::Config do |env|
8
+ env["action_dispatch.parameter_filter"] = [:password] # This is highly recommended. It will prevent the ExceptionNotification email from including your users' passwords
9
+ end
10
+
11
+ use ExceptionNotification::Rack,
12
+ :email => {
13
+ :email_prefix => "[Example] ",
14
+ :sender_address => %{"notifier" <notifier@example.com>},
15
+ :exception_recipients => %w{exceptions@example.com},
16
+ :smtp_settings => { :address => "localhost", :port => 1025 }
17
+ }
18
+
19
+ get '/' do
20
+ raise StandardError, "ERROR: #{params[:error]}" unless params[:error].blank?
21
+ 'Everything is fine! Now, lets break things clicking <a href="/?error=ops"> here </a>. Dont forget to see the emails at <a href="http://localhost:1080">mailcatcher</a> !'
22
+ end
23
+
24
+ get '/background_notification' do
25
+ begin
26
+ 1/0
27
+ rescue Exception => e
28
+ ExceptionNotifier.notify_exception(e, :data => {:msg => "Cannot divide by zero!"})
29
+ end
30
+ 'Check email at <a href="http://localhost:1080">mailcatcher</a>.'
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'exception_notification_more_info'
3
+ s.version = '1.0.0'
4
+ s.authors = ["Jamis Buck", "Josh Peek", "Hyunjae Park"]
5
+ s.summary = "Exception notification for Rails apps(+ More Info!)"
6
+ s.homepage = "https://smartinez87.github.io/exception_notification/"
7
+ s.email = "dev.hyunjae.park@gmail.com"
8
+ s.license = "MIT"
9
+
10
+ s.required_ruby_version = '>= 2.0'
11
+ s.required_rubygems_version = '>= 1.8.11'
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.files -= `git ls-files -- .??*`.split("\n")
15
+ s.test_files = `git ls-files -- test`.split("\n")
16
+ s.require_path = 'lib'
17
+
18
+ s.add_dependency("actionmailer", "~> 4.0")
19
+ s.add_dependency("activesupport", "~> 4.0")
20
+
21
+ s.add_development_dependency "rails", "~> 4.0"
22
+ s.add_development_dependency "resque", "~> 1.2.0"
23
+ # Sidekiq 3.2.2 does not support Ruby 1.9.
24
+ s.add_development_dependency "sidekiq", "~> 3.0.0", "< 3.2.2"
25
+ s.add_development_dependency "tinder", "~> 1.8"
26
+ s.add_development_dependency "httparty", "~> 0.10.2"
27
+ s.add_development_dependency "mocha", ">= 0.13.0"
28
+ s.add_development_dependency "sqlite3", ">= 1.3.4"
29
+ s.add_development_dependency "coveralls", "~> 0.8.2"
30
+ s.add_development_dependency "appraisal", "~> 2.0.0"
31
+ s.add_development_dependency "hipchat", ">= 1.0.0"
32
+ s.add_development_dependency "carrier-pigeon", ">= 0.7.0"
33
+ s.add_development_dependency "slack-notifier", ">= 1.0.0"
34
+ end
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.0.5"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.1.1"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.2.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,11 @@
1
+ require 'exception_notifier'
2
+ require 'exception_notification/rack'
3
+
4
+ module ExceptionNotification
5
+ # Alternative way to setup ExceptionNotification.
6
+ # Run 'rails generate exception_notification:install' to create
7
+ # a fresh initializer with all configuration values.
8
+ def self.configure
9
+ yield ExceptionNotifier
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ module ExceptionNotification
2
+ class Rack
3
+ class CascadePassException < Exception; end
4
+
5
+ def initialize(app, options = {})
6
+ @app = app
7
+
8
+ ExceptionNotifier.ignored_exceptions = options.delete(:ignore_exceptions) if options.key?(:ignore_exceptions)
9
+
10
+ if options.key?(:ignore_if)
11
+ rack_ignore = options.delete(:ignore_if)
12
+ ExceptionNotifier.ignore_if do |exception, options|
13
+ options.key?(:env) && rack_ignore.call(options[:env], exception)
14
+ end
15
+ end
16
+
17
+ if options.key?(:ignore_crawlers)
18
+ ignore_crawlers = options.delete(:ignore_crawlers)
19
+ ExceptionNotifier.ignore_if do |exception, options|
20
+ options.key?(:env) && from_crawler(options[:env], ignore_crawlers)
21
+ end
22
+ end
23
+
24
+ @ignore_cascade_pass = options.delete(:ignore_cascade_pass) { true }
25
+
26
+ options.each do |notifier_name, options|
27
+ ExceptionNotifier.register_exception_notifier(notifier_name, options)
28
+ end
29
+ end
30
+
31
+ def call(env)
32
+ _, headers, _ = response = @app.call(env)
33
+
34
+ if !@ignore_cascade_pass && headers['X-Cascade'] == 'pass'
35
+ msg = "This exception means that the preceding Rack middleware set the 'X-Cascade' header to 'pass' -- in " <<
36
+ "Rails, this often means that the route was not found (404 error)."
37
+ raise CascadePassException, msg
38
+ end
39
+
40
+ response
41
+ rescue Exception => exception
42
+ if ExceptionNotifier.notify_exception(exception, :env => env)
43
+ env['exception_notifier.delivered'] = true
44
+ end
45
+
46
+ raise exception unless exception.is_a?(CascadePassException)
47
+ response
48
+ end
49
+
50
+ private
51
+
52
+ def from_crawler(env, ignored_crawlers)
53
+ agent = env['HTTP_USER_AGENT']
54
+ Array(ignored_crawlers).any? do |crawler|
55
+ agent =~ Regexp.new(crawler)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,8 @@
1
+ module ExceptionNotification
2
+ class Engine < ::Rails::Engine
3
+ config.exception_notification = ExceptionNotifier
4
+ config.exception_notification.logger = Rails.logger
5
+
6
+ config.app_middleware.use ExceptionNotification::Rack
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ require 'resque/failure/base'
2
+
3
+ module ExceptionNotification
4
+ class Resque < Resque::Failure::Base
5
+
6
+ def self.count
7
+ Stat[:failed]
8
+ end
9
+
10
+ def save
11
+ data = {
12
+ :failed_at => Time.now.to_s,
13
+ :queue => queue,
14
+ :worker => worker.to_s,
15
+ :payload => payload,
16
+ :error_class => exception.class.name,
17
+ :error_message => exception.message
18
+ }
19
+
20
+ ExceptionNotifier.notify_exception(exception, :data => { :resque => data })
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ require 'sidekiq'
2
+
3
+ # Note: this class is only needed for Sidekiq version < 3.
4
+ module ExceptionNotification
5
+ class Sidekiq
6
+
7
+ def call(worker, msg, queue)
8
+ begin
9
+ yield
10
+ rescue Exception => exception
11
+ ExceptionNotifier.notify_exception(exception, :data => { :sidekiq => msg })
12
+ raise exception
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+
19
+ if ::Sidekiq::VERSION < '3'
20
+ ::Sidekiq.configure_server do |config|
21
+ config.server_middleware do |chain|
22
+ chain.add ::ExceptionNotification::Sidekiq
23
+ end
24
+ end
25
+ else
26
+ ::Sidekiq.configure_server do |config|
27
+ config.error_handlers << Proc.new { |ex, context|
28
+ ExceptionNotifier.notify_exception(ex, :data => { :sidekiq => context })
29
+ }
30
+ end
31
+ end
@@ -0,0 +1,121 @@
1
+ require 'logger'
2
+ require 'active_support/core_ext/string/inflections'
3
+ require 'active_support/core_ext/module/attribute_accessors'
4
+ require 'exception_notifier/base_notifier'
5
+
6
+ module ExceptionNotifier
7
+
8
+ autoload :BacktraceCleaner, 'exception_notifier/modules/backtrace_cleaner'
9
+
10
+ autoload :Notifier, 'exception_notifier/notifier'
11
+ autoload :EmailNotifier, 'exception_notifier/email_notifier'
12
+ autoload :CampfireNotifier, 'exception_notifier/campfire_notifier'
13
+ autoload :HipchatNotifier, 'exception_notifier/hipchat_notifier'
14
+ autoload :WebhookNotifier, 'exception_notifier/webhook_notifier'
15
+ autoload :IrcNotifier, 'exception_notifier/irc_notifier'
16
+ autoload :SlackNotifier, 'exception_notifier/slack_notifier'
17
+
18
+ class UndefinedNotifierError < StandardError; end
19
+
20
+ # Define logger
21
+ mattr_accessor :logger
22
+ @@logger = Logger.new(STDOUT)
23
+
24
+ # Define a set of exceptions to be ignored, ie, dont send notifications when any of them are raised.
25
+ mattr_accessor :ignored_exceptions
26
+ @@ignored_exceptions = %w{ActiveRecord::RecordNotFound AbstractController::ActionNotFound ActionController::RoutingError ActionController::UnknownFormat}
27
+
28
+ mattr_accessor :testing_mode
29
+ @@testing_mode = false
30
+
31
+ class << self
32
+ # Store conditions that decide when exceptions must be ignored or not.
33
+ @@ignores = []
34
+
35
+ # Store notifiers that send notifications when exceptions are raised.
36
+ @@notifiers = {}
37
+
38
+ def testing_mode!
39
+ self.testing_mode = true
40
+ end
41
+
42
+ def notify_exception(exception, options={})
43
+ return false if ignored_exception?(options[:ignore_exceptions], exception)
44
+ return false if ignored?(exception, options)
45
+ selected_notifiers = options.delete(:notifiers) || notifiers
46
+ [*selected_notifiers].each do |notifier|
47
+ fire_notification(notifier, exception, options.dup)
48
+ end
49
+ true
50
+ end
51
+
52
+ def register_exception_notifier(name, notifier_or_options)
53
+ if notifier_or_options.respond_to?(:call)
54
+ @@notifiers[name] = notifier_or_options
55
+ elsif notifier_or_options.is_a?(Hash)
56
+ create_and_register_notifier(name, notifier_or_options)
57
+ else
58
+ raise ArgumentError, "Invalid notifier '#{name}' defined as #{notifier_or_options.inspect}"
59
+ end
60
+ end
61
+ alias add_notifier register_exception_notifier
62
+
63
+ def unregister_exception_notifier(name)
64
+ @@notifiers.delete(name)
65
+ end
66
+
67
+ def registered_exception_notifier(name)
68
+ @@notifiers[name]
69
+ end
70
+
71
+ def notifiers
72
+ @@notifiers.keys
73
+ end
74
+
75
+ # Adds a condition to decide when an exception must be ignored or not.
76
+ #
77
+ # ExceptionNotifier.ignore_if do |exception, options|
78
+ # not Rails.env.production?
79
+ # end
80
+ def ignore_if(&block)
81
+ @@ignores << block
82
+ end
83
+
84
+ def clear_ignore_conditions!
85
+ @@ignores.clear
86
+ end
87
+
88
+ private
89
+ def ignored?(exception, options)
90
+ @@ignores.any?{ |condition| condition.call(exception, options) }
91
+ rescue Exception => e
92
+ raise e if @@testing_mode
93
+
94
+ logger.warn "An error occurred when evaluating an ignore condition. #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
95
+ false
96
+ end
97
+
98
+ def ignored_exception?(ignore_array, exception)
99
+ (Array(ignored_exceptions) + Array(ignore_array)).map(&:to_s).include?(exception.class.name)
100
+ end
101
+
102
+ def fire_notification(notifier_name, exception, options)
103
+ notifier = registered_exception_notifier(notifier_name)
104
+ notifier.call(exception, options)
105
+ rescue Exception => e
106
+ raise e if @@testing_mode
107
+
108
+ logger.warn "An error occurred when sending a notification using '#{notifier_name}' notifier. #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
109
+ false
110
+ end
111
+
112
+ def create_and_register_notifier(name, options)
113
+ notifier_classname = "#{name}_notifier".camelize
114
+ notifier_class = ExceptionNotifier.const_get(notifier_classname)
115
+ notifier = notifier_class.new(options)
116
+ register_exception_notifier(name, notifier)
117
+ rescue NameError => e
118
+ raise UndefinedNotifierError, "No notifier named '#{name}' was found. Please, revise your configuration options. Cause: #{e.message}"
119
+ end
120
+ end
121
+ end