exception_notification 4.4.0 → 4.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/Appraisals +3 -1
  3. data/CHANGELOG.rdoc +10 -0
  4. data/Gemfile +2 -0
  5. data/README.md +34 -8
  6. data/Rakefile +2 -0
  7. data/examples/sample_app.rb +2 -0
  8. data/examples/sinatra/Gemfile +2 -0
  9. data/examples/sinatra/config.ru +2 -0
  10. data/examples/sinatra/sinatra_app.rb +6 -2
  11. data/exception_notification.gemspec +9 -8
  12. data/gemfiles/rails4_0.gemfile +3 -3
  13. data/gemfiles/rails4_1.gemfile +3 -3
  14. data/gemfiles/rails4_2.gemfile +3 -3
  15. data/gemfiles/rails5_0.gemfile +3 -3
  16. data/gemfiles/rails5_1.gemfile +3 -3
  17. data/gemfiles/rails5_2.gemfile +3 -3
  18. data/lib/exception_notification.rb +2 -0
  19. data/lib/exception_notification/rack.rb +24 -13
  20. data/lib/exception_notification/rails.rb +2 -0
  21. data/lib/exception_notification/resque.rb +2 -0
  22. data/lib/exception_notification/sidekiq.rb +5 -3
  23. data/lib/exception_notification/version.rb +3 -1
  24. data/lib/exception_notifier.rb +46 -7
  25. data/lib/exception_notifier/base_notifier.rb +8 -2
  26. data/lib/exception_notifier/campfire_notifier.rb +2 -0
  27. data/lib/exception_notifier/datadog_notifier.rb +12 -9
  28. data/lib/exception_notifier/email_notifier.rb +11 -3
  29. data/lib/exception_notifier/google_chat_notifier.rb +2 -0
  30. data/lib/exception_notifier/hipchat_notifier.rb +2 -0
  31. data/lib/exception_notifier/irc_notifier.rb +4 -3
  32. data/lib/exception_notifier/mattermost_notifier.rb +10 -0
  33. data/lib/exception_notifier/modules/backtrace_cleaner.rb +2 -0
  34. data/lib/exception_notifier/modules/error_grouping.rb +19 -9
  35. data/lib/exception_notifier/modules/formatter.rb +3 -0
  36. data/lib/exception_notifier/notifier.rb +5 -1
  37. data/lib/exception_notifier/slack_notifier.rb +2 -0
  38. data/lib/exception_notifier/sns_notifier.rb +4 -3
  39. data/lib/exception_notifier/teams_notifier.rb +10 -3
  40. data/lib/exception_notifier/webhook_notifier.rb +3 -3
  41. data/lib/generators/exception_notification/install_generator.rb +8 -2
  42. data/test/exception_notification/rack_test.rb +48 -2
  43. data/test/exception_notification/resque_test.rb +2 -0
  44. data/test/exception_notifier/campfire_notifier_test.rb +8 -1
  45. data/test/exception_notifier/datadog_notifier_test.rb +2 -0
  46. data/test/exception_notifier/email_notifier_test.rb +29 -3
  47. data/test/exception_notifier/google_chat_notifier_test.rb +15 -11
  48. data/test/exception_notifier/hipchat_notifier_test.rb +8 -2
  49. data/test/exception_notifier/irc_notifier_test.rb +2 -0
  50. data/test/exception_notifier/mattermost_notifier_test.rb +73 -24
  51. data/test/exception_notifier/modules/error_grouping_test.rb +2 -0
  52. data/test/exception_notifier/modules/formatter_test.rb +2 -0
  53. data/test/exception_notifier/sidekiq_test.rb +3 -11
  54. data/test/exception_notifier/slack_notifier_test.rb +12 -10
  55. data/test/exception_notifier/sns_notifier_test.rb +9 -7
  56. data/test/exception_notifier/teams_notifier_test.rb +2 -0
  57. data/test/exception_notifier/webhook_notifier_test.rb +6 -4
  58. data/test/exception_notifier_test.rb +112 -6
  59. data/test/support/exception_notifier_helper.rb +14 -0
  60. data/test/test_helper.rb +5 -1
  61. metadata +22 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f61027914f50eed12ef3c115cca8ba7a3f4f3b1e
4
- data.tar.gz: 1a63465348250221f3a7d2171979a8d6b35639c1
2
+ SHA256:
3
+ metadata.gz: 93d19f5540efa1d0c1426ec9ab64f1f16e8c6557b4e5bad5890b99bf62686607
4
+ data.tar.gz: de614f07ace037bc1b8fb9d998b97bfcb54f122b7a7560bf2f20693fd382a6ee
5
5
  SHA512:
6
- metadata.gz: 62cf62e3500f7edb53940bbe458a5b2e1936469033c4c643ad47d4ac9d3a913bd406fb7ca37cbf0d27b4f862b5487bd5736abdd37288aaaa904a125a00bd72bf
7
- data.tar.gz: 0ffc513a9fd0ce553254665abfb6da9640a4e9eb3ff5edf39aa838b9baa3caffffd76f0ffab64417721cd7c4828fdabba8c6ab9c9f47077f800845418c38db52
6
+ metadata.gz: 839fb346abb6142cbf5185d947c982fbf1d2516a7289769392926e674ee78550565861b03b3970d75e6c9d5838155316f3a893b44a4d76f9ee4d5dc1f7da0b6f
7
+ data.tar.gz: e6def42195dc5f106e959b1e4601cd6593e799b9e5232b9e0ea08f4b3cfa87e48a3c44300af3d0e0b2de43b1c17cf40ed344557742f28961463865d60423ebdc
data/Appraisals CHANGED
@@ -1,4 +1,6 @@
1
- rails_versions = ['~> 4.0.5', '~> 4.1.1', '~> 4.2.0', '~> 5.0.0', '~> 5.1.0', '~> 5.2.0']
1
+ # frozen_string_literal: true
2
+
3
+ rails_versions = ['~> 4.0.5', '~> 4.1.1', '~> 4.2.0', '~> 5.0.0', '~> 5.1.0', '~> 5.2.0', '~> 6.0.0']
2
4
 
3
5
  rails_versions.each do |rails_version|
4
6
  appraise "rails#{rails_version.slice(/\d+\.\d+/).tr('.', '_')}" do
@@ -1,3 +1,13 @@
1
+ == 4.4.1
2
+
3
+ * enhancements
4
+ * Enhance `ignore_if` option to allow by-notifier customization (by @fursich)
5
+ * Ignore extended modules of ignored exceptions (by @elengine)
6
+ * Add `exception_data` to Mattermost notifier (by @camillof)
7
+
8
+ * bug fixes
9
+ * Fix Rubocop offenses (by @nicolasferraro)
10
+
1
11
  == 4.4.0
2
12
 
3
13
  * enhancements
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # Exception Notification
2
2
 
3
- [![Gem Version](https://fury-badge.herokuapp.com/rb/exception_notification.png)](http://badge.fury.io/rb/exception_notification)
4
- [![Travis](https://api.travis-ci.org/smartinez87/exception_notification.png)](http://travis-ci.org/smartinez87/exception_notification)
5
- [![Coverage Status](https://coveralls.io/repos/smartinez87/exception_notification/badge.png?branch=master)](https://coveralls.io/r/smartinez87/exception_notification)
6
- [![Code Climate](https://codeclimate.com/github/smartinez87/exception_notification.png)](https://codeclimate.com/github/smartinez87/exception_notification)
3
+ [![Gem Version](https://badge.fury.io/rb/exception_notification.svg)](https://badge.fury.io/rb/exception_notification)
4
+ [![Build Status](https://travis-ci.org/smartinez87/exception_notification.svg?branch=master)](https://travis-ci.org/smartinez87/exception_notification)
5
+ [![Coverage Status](https://coveralls.io/repos/github/smartinez87/exception_notification/badge.svg?branch=master)](https://coveralls.io/github/smartinez87/exception_notification?branch=master)
6
+ [![Maintainability](https://api.codeclimate.com/v1/badges/78a9a12be00a6d305136/maintainability)](https://codeclimate.com/github/smartinez87/exception_notification/maintainability)
7
7
 
8
8
  **THIS README IS FOR THE MASTER BRANCH AND REFLECTS THE WORK CURRENTLY EXISTING ON THE MASTER BRANCH. IF YOU ARE WISHING TO USE A NON-MASTER BRANCH OF EXCEPTION NOTIFICATION, PLEASE CONSULT THAT BRANCH'S README AND NOT THIS ONE.**
9
9
 
@@ -17,11 +17,9 @@ There's a great [Railscast about Exception Notification](http://railscasts.com/e
17
17
 
18
18
  ## Requirements
19
19
 
20
- * Ruby 2.0 or greater
20
+ * Ruby 2.3 or greater
21
21
  * Rails 4.0 or greater, Sinatra or another Rack-based application.
22
22
 
23
- For previous releases, please checkout [this](#versions).
24
-
25
23
  ## Getting Started
26
24
 
27
25
  Add the following line to your application's Gemfile:
@@ -138,6 +136,8 @@ You can choose to ignore certain exceptions, which will make ExceptionNotificati
138
136
 
139
137
  * `:ignore_if` - Custom (i.e. ignore exceptions that satisfy some condition)
140
138
 
139
+ * `:ignore_notifer_if` - Custom (i.e. let each notifier ignore exceptions if by-notifier condition is satisfied)
140
+
141
141
 
142
142
  ### :ignore_exceptions
143
143
 
@@ -177,7 +177,7 @@ Rails.application.config.middleware.use ExceptionNotification::Rack,
177
177
 
178
178
  *Lambda, default: nil*
179
179
 
180
- Last but not least, you can ignore exceptions based on a condition. Take a look:
180
+ You can ignore exceptions based on a condition. Take a look:
181
181
 
182
182
  ```ruby
183
183
  Rails.application.config.middleware.use ExceptionNotification::Rack,
@@ -191,6 +191,32 @@ Rails.application.config.middleware.use ExceptionNotification::Rack,
191
191
 
192
192
  You can make use of both the environment and the exception inside the lambda to decide wether to avoid or not sending the notification.
193
193
 
194
+ ### :ignore_notifier_if
195
+
196
+ * Hash of Lambda, default: nil*
197
+
198
+ In case you want a notifier to ignore certain exceptions, but don't want other notifiers to skip them, you can set by-notifier ignore options.
199
+ By setting below, each notifier will ignore exceptions when its corresponding condition is met.
200
+
201
+ ```ruby
202
+ Rails.application.config.middleware.use ExceptionNotification::Rack,
203
+ ignore_notifier_if: {
204
+ email: ->(env, exception) { !Rails.env.production? },
205
+ slack: ->(env, exception) { exception.message =~ /^Couldn't find Page with ID=/ }
206
+ }
207
+
208
+ email: {
209
+ sender_address: %{"notifier" <notifier@example.com>},
210
+ exception_recipients: %w{exceptions@example.com}
211
+ },
212
+ slack: {
213
+ webhook_url: '[Your webhook url]',
214
+ channel: '#exceptions',
215
+ }
216
+ ```
217
+
218
+ To customize each condition, you can make use of environment and the exception object inside the lambda.
219
+
194
220
  ## Rack X-Cascade Header
195
221
 
196
222
  Some rack apps (Rails in particular) utilize the "X-Cascade" header to pass the request-handling responsibility to the next middleware in the stack.
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rubygems'
2
4
  require 'bundler/setup'
3
5
  Bundler::GemHelper.install_tasks
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # -------------------------------------------
2
4
  # To run the application: ruby examples/sample_app.rb
3
5
  # -------------------------------------------
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gem 'exception_notification', path: '../../'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require ::File.expand_path('../sinatra_app', __FILE__)
2
4
 
3
5
  run SinatraApp
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rubygems'
2
4
  require 'bundler/setup'
3
5
  require 'sinatra/base'
@@ -5,7 +7,8 @@ require 'exception_notification'
5
7
 
6
8
  class SinatraApp < Sinatra::Base
7
9
  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
10
+ # This is highly recommended. It will prevent the ExceptionNotification email from including your users' passwords
11
+ env['action_dispatch.parameter_filter'] = [:password]
9
12
  end
10
13
 
11
14
  use ExceptionNotification::Rack,
@@ -22,7 +25,8 @@ class SinatraApp < Sinatra::Base
22
25
  get '/' do
23
26
  raise StandardError, "ERROR: #{params[:error]}" unless params[:error].blank?
24
27
 
25
- '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> !'
28
+ 'Everything is fine! Now, lets break things clicking <a href="/?error=ops"> here </a>.' \
29
+ 'Dont forget to see the emails at <a href="http://localhost:1080">mailcatcher</a> !'
26
30
  end
27
31
 
28
32
  get '/background_notification' do
@@ -1,16 +1,18 @@
1
- require File.expand_path('../lib/exception_notification/version', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('lib/exception_notification/version', __dir__)
2
4
 
3
5
  Gem::Specification.new do |s|
4
6
  s.name = 'exception_notification'
5
7
  s.version = ExceptionNotification::VERSION
6
8
  s.authors = ['Jamis Buck', 'Josh Peek']
7
- s.date = '2019-08-16'
9
+ s.date = '2020-06-23'
8
10
  s.summary = 'Exception notification for Rails apps'
9
11
  s.homepage = 'https://smartinez87.github.io/exception_notification/'
10
12
  s.email = 'smartinez87@gmail.com'
11
13
  s.license = 'MIT'
12
14
 
13
- s.required_ruby_version = '>= 2.0'
15
+ s.required_ruby_version = '>= 2.3'
14
16
  s.required_rubygems_version = '>= 1.8.11'
15
17
 
16
18
  s.files = `git ls-files`.split("\n")
@@ -28,14 +30,13 @@ Gem::Specification.new do |s|
28
30
  s.add_development_dependency 'dogapi', '>= 1.23.0'
29
31
  s.add_development_dependency 'hipchat', '>= 1.0.0'
30
32
  s.add_development_dependency 'httparty', '~> 0.10.2'
31
- s.add_development_dependency 'mock_redis', '~> 0.18.0'
32
33
  s.add_development_dependency 'mocha', '>= 0.13.0'
34
+ s.add_development_dependency 'mock_redis', '~> 0.19.0'
33
35
  s.add_development_dependency 'rails', '>= 4.0', '< 7'
34
36
  s.add_development_dependency 'resque', '~> 1.8.0'
35
- s.add_development_dependency 'rubocop', '0.50.0'
36
- # Sidekiq 3.2.2 does not support Ruby 1.9.
37
- s.add_development_dependency 'sidekiq', '~> 3.0.0', '< 3.2.2'
37
+ s.add_development_dependency 'rubocop', '0.78.0'
38
+ s.add_development_dependency 'sidekiq', '>= 5.0.4'
38
39
  s.add_development_dependency 'slack-notifier', '>= 1.0.0'
39
- s.add_development_dependency 'timecop', '~>0.9.0'
40
+ s.add_development_dependency 'timecop', '~> 0.9.0'
40
41
  s.add_development_dependency 'tinder', '~> 1.8'
41
42
  end
@@ -1,7 +1,7 @@
1
1
  # This file was generated by Appraisal
2
2
 
3
- source 'https://rubygems.org'
3
+ source "https://rubygems.org"
4
4
 
5
- gem 'rails', '~> 4.0.5'
5
+ gem "rails", "~> 4.0.5"
6
6
 
7
- gemspec path: '../'
7
+ gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  # This file was generated by Appraisal
2
2
 
3
- source 'https://rubygems.org'
3
+ source "https://rubygems.org"
4
4
 
5
- gem 'rails', '~> 4.1.1'
5
+ gem "rails", "~> 4.1.1"
6
6
 
7
- gemspec path: '../'
7
+ gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  # This file was generated by Appraisal
2
2
 
3
- source 'https://rubygems.org'
3
+ source "https://rubygems.org"
4
4
 
5
- gem 'rails', '~> 4.2.0'
5
+ gem "rails", "~> 4.2.0"
6
6
 
7
- gemspec path: '../'
7
+ gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  # This file was generated by Appraisal
2
2
 
3
- source 'https://rubygems.org'
3
+ source "https://rubygems.org"
4
4
 
5
- gem 'rails', '~> 5.0.0'
5
+ gem "rails", "~> 5.0.0"
6
6
 
7
- gemspec path: '../'
7
+ gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  # This file was generated by Appraisal
2
2
 
3
- source 'https://rubygems.org'
3
+ source "https://rubygems.org"
4
4
 
5
- gem 'rails', '~> 5.1.0'
5
+ gem "rails", "~> 5.1.0"
6
6
 
7
- gemspec path: '../'
7
+ gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  # This file was generated by Appraisal
2
2
 
3
- source 'https://rubygems.org'
3
+ source "https://rubygems.org"
4
4
 
5
- gem 'rails', '~> 5.2.0'
5
+ gem "rails", "~> 5.2.0"
6
6
 
7
- gemspec path: '../'
7
+ gemspec path: "../"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'exception_notifier'
2
4
  require 'exception_notification/rack'
3
5
  require 'exception_notification/version'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ExceptionNotification
2
4
  class Rack
3
5
  class CascadePassException < RuntimeError; end
@@ -5,15 +7,17 @@ module ExceptionNotification
5
7
  def initialize(app, options = {})
6
8
  @app = app
7
9
 
8
- ExceptionNotifier.ignored_exceptions = options.delete(:ignore_exceptions) if options.key?(:ignore_exceptions)
9
- ExceptionNotifier.error_grouping = options.delete(:error_grouping) if options.key?(:error_grouping)
10
- ExceptionNotifier.error_grouping_period = options.delete(:error_grouping_period) if options.key?(:error_grouping_period)
11
- ExceptionNotifier.notification_trigger = options.delete(:notification_trigger) if options.key?(:notification_trigger)
10
+ ExceptionNotifier.tap do |en|
11
+ en.ignored_exceptions = options.delete(:ignore_exceptions) if options.key?(:ignore_exceptions)
12
+ en.error_grouping = options.delete(:error_grouping) if options.key?(:error_grouping)
13
+ en.error_grouping_period = options.delete(:error_grouping_period) if options.key?(:error_grouping_period)
14
+ en.notification_trigger = options.delete(:notification_trigger) if options.key?(:notification_trigger)
12
15
 
13
- if options.key?(:error_grouping_cache)
14
- ExceptionNotifier.error_grouping_cache = options.delete(:error_grouping_cache)
15
- elsif defined?(Rails) && Rails.respond_to?(:cache)
16
- ExceptionNotifier.error_grouping_cache = Rails.cache
16
+ if options.key?(:error_grouping_cache)
17
+ en.error_grouping_cache = options.delete(:error_grouping_cache)
18
+ elsif defined?(Rails) && Rails.respond_to?(:cache)
19
+ en.error_grouping_cache = Rails.cache
20
+ end
17
21
  end
18
22
 
19
23
  if options.key?(:ignore_if)
@@ -23,6 +27,15 @@ module ExceptionNotification
23
27
  end
24
28
  end
25
29
 
30
+ if options.key?(:ignore_notifier_if)
31
+ rack_ignore_by_notifier = options.delete(:ignore_notifier_if)
32
+ rack_ignore_by_notifier.each do |notifier, proc|
33
+ ExceptionNotifier.ignore_notifier_if(notifier) do |exception, opts|
34
+ opts.key?(:env) && proc.call(opts[:env], exception)
35
+ end
36
+ end
37
+ end
38
+
26
39
  ExceptionNotifier.ignore_crawlers(options.delete(:ignore_crawlers)) if options.key?(:ignore_crawlers)
27
40
 
28
41
  @ignore_cascade_pass = options.delete(:ignore_cascade_pass) { true }
@@ -42,12 +55,10 @@ module ExceptionNotification
42
55
  end
43
56
 
44
57
  response
45
- rescue Exception => exception
46
- if ExceptionNotifier.notify_exception(exception, env: env)
47
- env['exception_notifier.delivered'] = true
48
- end
58
+ rescue Exception => e
59
+ env['exception_notifier.delivered'] = true if ExceptionNotifier.notify_exception(e, env: env)
49
60
 
50
- raise exception unless exception.is_a?(CascadePassException)
61
+ raise e unless e.is_a?(CascadePassException)
51
62
 
52
63
  response
53
64
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ExceptionNotification
2
4
  class Engine < ::Rails::Engine
3
5
  config.exception_notification = ExceptionNotifier
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'resque/failure/base'
2
4
 
3
5
  module ExceptionNotification
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sidekiq'
2
4
 
3
5
  # Note: this class is only needed for Sidekiq version < 3.
@@ -5,9 +7,9 @@ module ExceptionNotification
5
7
  class Sidekiq
6
8
  def call(_worker, msg, _queue)
7
9
  yield
8
- rescue Exception => exception
9
- ExceptionNotifier.notify_exception(exception, data: { sidekiq: msg })
10
- raise exception
10
+ rescue Exception => e
11
+ ExceptionNotifier.notify_exception(e, data: { sidekiq: msg })
12
+ raise e
11
13
  end
12
14
  end
13
15
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ExceptionNotification
2
- VERSION = '4.4.0'.freeze
4
+ VERSION = '4.4.1'
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
  require 'active_support/core_ext/string/inflections'
3
5
  require 'active_support/core_ext/module/attribute_accessors'
@@ -31,7 +33,10 @@ module ExceptionNotifier
31
33
 
32
34
  # Define a set of exceptions to be ignored, ie, dont send notifications when any of them are raised.
33
35
  mattr_accessor :ignored_exceptions
34
- @@ignored_exceptions = %w[ActiveRecord::RecordNotFound Mongoid::Errors::DocumentNotFound AbstractController::ActionNotFound ActionController::RoutingError ActionController::UnknownFormat ActionController::UrlGenerationError]
36
+ @@ignored_exceptions = %w[
37
+ ActiveRecord::RecordNotFound Mongoid::Errors::DocumentNotFound AbstractController::ActionNotFound
38
+ ActionController::RoutingError ActionController::UnknownFormat ActionController::UrlGenerationError
39
+ ]
35
40
 
36
41
  mattr_accessor :testing_mode
37
42
  @@testing_mode = false
@@ -40,6 +45,9 @@ module ExceptionNotifier
40
45
  # Store conditions that decide when exceptions must be ignored or not.
41
46
  @@ignores = []
42
47
 
48
+ # Store by-notifier conditions that decide when exceptions must be ignored or not.
49
+ @@by_notifier_ignores = {}
50
+
43
51
  # Store notifiers that send notifications when exceptions are raised.
44
52
  @@notifiers = {}
45
53
 
@@ -56,11 +64,16 @@ module ExceptionNotifier
56
64
  return false unless send_notification?(exception, errors_count)
57
65
  end
58
66
 
67
+ notification_fired = false
59
68
  selected_notifiers = options.delete(:notifiers) || notifiers
60
69
  [*selected_notifiers].each do |notifier|
61
- fire_notification(notifier, exception, options.dup, &block)
70
+ unless notifier_ignored?(exception, options, notifier: notifier)
71
+ fire_notification(notifier, exception, options.dup, &block)
72
+ notification_fired = true
73
+ end
62
74
  end
63
- true
75
+
76
+ notification_fired
64
77
  end
65
78
 
66
79
  def register_exception_notifier(name, notifier_or_options)
@@ -95,6 +108,10 @@ module ExceptionNotifier
95
108
  @@ignores << block
96
109
  end
97
110
 
111
+ def ignore_notifier_if(notifier, &block)
112
+ @@by_notifier_ignores[notifier] = block
113
+ end
114
+
98
115
  def ignore_crawlers(crawlers)
99
116
  ignore_if do |_exception, opts|
100
117
  opts.key?(:env) && from_crawler(opts[:env], crawlers)
@@ -103,6 +120,7 @@ module ExceptionNotifier
103
120
 
104
121
  def clear_ignore_conditions!
105
122
  @@ignores.clear
123
+ @@by_notifier_ignores.clear
106
124
  end
107
125
 
108
126
  private
@@ -112,13 +130,30 @@ module ExceptionNotifier
112
130
  rescue Exception => e
113
131
  raise e if @@testing_mode
114
132
 
115
- logger.warn "An error occurred when evaluating an ignore condition. #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
133
+ logger.warn(
134
+ "An error occurred when evaluating an ignore condition. #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
135
+ )
136
+ false
137
+ end
138
+
139
+ def notifier_ignored?(exception, options, notifier:)
140
+ return false unless @@by_notifier_ignores.key?(notifier)
141
+
142
+ condition = @@by_notifier_ignores[notifier]
143
+ condition.call(exception, options)
144
+ rescue Exception => e
145
+ raise e if @@testing_mode
146
+
147
+ logger.warn(<<~"MESSAGE")
148
+ An error occurred when evaluating a by-notifier ignore condition. #{e.class}: #{e.message}
149
+ #{e.backtrace.join("\n")}
150
+ MESSAGE
116
151
  false
117
152
  end
118
153
 
119
154
  def ignored_exception?(ignore_array, exception)
120
155
  all_ignored_exceptions = (Array(ignored_exceptions) + Array(ignore_array)).map(&:to_s)
121
- exception_ancestors = exception.class.ancestors.map(&:to_s)
156
+ exception_ancestors = exception.singleton_class.ancestors.map(&:to_s)
122
157
  !(all_ignored_exceptions & exception_ancestors).empty?
123
158
  end
124
159
 
@@ -128,7 +163,10 @@ module ExceptionNotifier
128
163
  rescue Exception => e
129
164
  raise e if @@testing_mode
130
165
 
131
- logger.warn "An error occurred when sending a notification using '#{notifier_name}' notifier. #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
166
+ logger.warn(
167
+ "An error occurred when sending a notification using '#{notifier_name}' notifier." \
168
+ "#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
169
+ )
132
170
  false
133
171
  end
134
172
 
@@ -138,7 +176,8 @@ module ExceptionNotifier
138
176
  notifier = notifier_class.new(options)
139
177
  register_exception_notifier(name, notifier)
140
178
  rescue NameError => e
141
- raise UndefinedNotifierError, "No notifier named '#{name}' was found. Please, revise your configuration options. Cause: #{e.message}"
179
+ raise UndefinedNotifierError,
180
+ "No notifier named '#{name}' was found. Please, revise your configuration options. Cause: #{e.message}"
142
181
  end
143
182
 
144
183
  def from_crawler(env, ignored_crawlers)