From 7e399e3bb50fcbc2b62ac82b0d333eef54b443b2 Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Wed, 13 Sep 2017 15:13:34 +0100 Subject: [PATCH 01/17] Added unhandled functionality, updated tests and intergrations --- lib/bugsnag.rb | 50 ++++++++++++------ lib/bugsnag/integrations/mailman.rb | 7 ++- lib/bugsnag/integrations/rack.rb | 14 ++++- .../rails/active_record_rescue.rb | 7 ++- lib/bugsnag/integrations/railtie.rb | 7 ++- lib/bugsnag/integrations/rake.rb | 7 ++- lib/bugsnag/integrations/resque.rb | 7 ++- lib/bugsnag/integrations/sidekiq.rb | 10 +++- lib/bugsnag/report.rb | 13 ++++- lib/bugsnag/tasks/bugsnag.rake | 9 +++- spec/report_spec.rb | 52 +++++++++++++++++-- 11 files changed, 155 insertions(+), 28 deletions(-) diff --git a/lib/bugsnag.rb b/lib/bugsnag.rb index 06273b318..51d996435 100644 --- a/lib/bugsnag.rb +++ b/lib/bugsnag.rb @@ -33,12 +33,37 @@ def configure end # Explicitly notify of an exception - def notify(exception, auto_notify=false, &block) - if auto_notify && !configuration.auto_notify + def notify(exception, &block) + report = Report.new(exception, configuration) + send_report(report, false, &block) + end + + # For automatic notification of exceptions + def auto_notify(exception, severity_reason, &block) + if !configuration.auto_notify configuration.debug("Not notifying because auto_notify is disabled") return end + report = Report.new(exception, configuration, true, severity_reason) + + send_report(report, true, &block) + end + + # Configuration getters + def configuration + @configuration = nil unless defined?(@configuration) + @configuration || LOCK.synchronize { @configuration ||= Bugsnag::Configuration.new } + end + + # Allow access to "before notify" callbacks + def before_notify_callbacks + Bugsnag.configuration.request_data[:before_callbacks] ||= [] + end + + private + def send_report(report, auto_notify, &block) + if !configuration.valid_api_key? configuration.debug("Not notifying due to an invalid api_key") return @@ -49,8 +74,6 @@ def notify(exception, auto_notify=false, &block) return end - report = Report.new(exception, configuration) - # If this is an auto_notify we yield the block before the any middleware is run yield(report) if block_given? && auto_notify if report.ignore? @@ -65,6 +88,9 @@ def notify(exception, auto_notify=false, &block) return end + # Store default severity for future reference + initial_severity = report.severity + # Run users middleware configuration.middleware.run(report) do if report.ignore? @@ -80,6 +106,11 @@ def notify(exception, auto_notify=false, &block) return end + # Test whether severity has been changed + if report.severity != initial_severity + report.default_severity = false + end + # Deliver configuration.info("Notifying #{configuration.endpoint} of #{report.exceptions.last[:errorClass]}") payload_string = ::JSON.dump(Bugsnag::Helpers.trim_if_needed(report.as_json)) @@ -87,17 +118,6 @@ def notify(exception, auto_notify=false, &block) Bugsnag::Delivery[configuration.delivery_method].deliver(configuration.endpoint, payload_string, configuration) end end - - # Configuration getters - def configuration - @configuration = nil unless defined?(@configuration) - @configuration || LOCK.synchronize { @configuration ||= Bugsnag::Configuration.new } - end - - # Allow access to "before notify" callbacks - def before_notify_callbacks - Bugsnag.configuration.request_data[:before_callbacks] ||= [] - end end end diff --git a/lib/bugsnag/integrations/mailman.rb b/lib/bugsnag/integrations/mailman.rb index c0dc7cd5f..86f4694ae 100644 --- a/lib/bugsnag/integrations/mailman.rb +++ b/lib/bugsnag/integrations/mailman.rb @@ -13,7 +13,12 @@ def call(mail) yield rescue Exception => ex raise ex if [Interrupt, SystemExit, SignalException].include? ex.class - Bugsnag.notify(ex, true) do |report| + Bugsnag.auto_notify(ex, { + :type => "middleware", + :attributes => { + :name => "mailman" + } + }) do |report| report.severity = "error" end raise diff --git a/lib/bugsnag/integrations/rack.rb b/lib/bugsnag/integrations/rack.rb index 121628146..23c071b25 100644 --- a/lib/bugsnag/integrations/rack.rb +++ b/lib/bugsnag/integrations/rack.rb @@ -33,7 +33,12 @@ def call(env) response = @app.call(env) rescue Exception => raised # Notify bugsnag of rack exceptions - Bugsnag.notify(raised, true) do |report| + Bugsnag.auto_notify(raised, { + :type => "middleware_handler", + :attirbutes => { + :name => "rack" + } + }) do |report| report.severity = "error" end @@ -43,7 +48,12 @@ def call(env) # Notify bugsnag of rack exceptions if env["rack.exception"] - Bugsnag.notify(env["rack.exception"], true) do |report| + Bugsnag.auto_notify(env["rack.exception"], { + :type => "middleware", + :attributes => { + :name => "rack" + } + }) do |report| report.severity = "error" end end diff --git a/lib/bugsnag/integrations/rails/active_record_rescue.rb b/lib/bugsnag/integrations/rails/active_record_rescue.rb index aee619ad7..e4a19748d 100644 --- a/lib/bugsnag/integrations/rails/active_record_rescue.rb +++ b/lib/bugsnag/integrations/rails/active_record_rescue.rb @@ -8,7 +8,12 @@ def run_callbacks(kind, *args, &block) super rescue StandardError => exception # This exception will NOT be escalated, so notify it here. - Bugsnag.notify(exception, true) do |report| + Bugsnag.auto_notify(exception, { + :type => "middleware", + :attributes => { + :name => "active record" + } + }) do |report| report.severity = "error" end raise diff --git a/lib/bugsnag/integrations/railtie.rb b/lib/bugsnag/integrations/railtie.rb index f2047f394..6e65183af 100644 --- a/lib/bugsnag/integrations/railtie.rb +++ b/lib/bugsnag/integrations/railtie.rb @@ -17,7 +17,12 @@ class Railtie < Rails::Railtie runner do at_exit do if $! - Bugsnag.notify($!, true) do |report| + Bugsnag.auto_notify($!, { + :type => "middleware", + :attributes => { + :name => "railtie" + } + }) do |report| report.severity = "error" end end diff --git a/lib/bugsnag/integrations/rake.rb b/lib/bugsnag/integrations/rake.rb index a33e5eaea..3d6a73896 100644 --- a/lib/bugsnag/integrations/rake.rb +++ b/lib/bugsnag/integrations/rake.rb @@ -12,7 +12,12 @@ def execute_with_bugsnag(args=nil) execute_without_bugsnag(args) rescue Exception => ex - Bugsnag.notify(ex, true) do |report| + Bugsnag.auto_notify(ex, { + :type => "middleware", + :attributes => { + :name => "rake" + } + }) do |report| report.severity = "error" end raise diff --git a/lib/bugsnag/integrations/resque.rb b/lib/bugsnag/integrations/resque.rb index 9abba7814..7e3e9110c 100644 --- a/lib/bugsnag/integrations/resque.rb +++ b/lib/bugsnag/integrations/resque.rb @@ -26,7 +26,12 @@ def self.add_failure_backend end def save - Bugsnag.notify(exception, true) do |report| + Bugsnag.auto_notify(exception, { + :type => "middleware", + :attributes => { + :name => "mailman" + } + }) do |report| report.severity = "error" report.meta_data.merge!({:context => "#{payload['class']}@#{queue}", :payload => payload, :delivery_method => :synchronous}) end diff --git a/lib/bugsnag/integrations/sidekiq.rb b/lib/bugsnag/integrations/sidekiq.rb index f403afcb1..236b9f1ed 100644 --- a/lib/bugsnag/integrations/sidekiq.rb +++ b/lib/bugsnag/integrations/sidekiq.rb @@ -16,7 +16,15 @@ def call(worker, msg, queue) yield rescue Exception => ex raise ex if [Interrupt, SystemExit, SignalException].include? ex.class - Bugsnag.notify(ex, true) do |report| + Bugsnag.auto_notify( + ex, + { + :type => 'middleware_handler', + :attributes => { + :name => "sidekiq" + } + } + ) do |report| report.severity = "error" end raise diff --git a/lib/bugsnag/report.rb b/lib/bugsnag/report.rb index 5f3c289eb..43cd8bd1c 100644 --- a/lib/bugsnag/report.rb +++ b/lib/bugsnag/report.rb @@ -17,6 +17,7 @@ class Report attr_accessor :app_version attr_accessor :configuration attr_accessor :context + attr_accessor :default_severity attr_accessor :delivery_method attr_accessor :exceptions attr_accessor :hostname @@ -27,8 +28,10 @@ class Report attr_accessor :severity attr_accessor :user - def initialize(exception, passed_configuration) + def initialize(exception, passed_configuration, unhandled=false, severity_reason=nil) @should_ignore = false + @unhandled = unhandled + @severity_reason = severity_reason self.configuration = passed_configuration @@ -43,6 +46,7 @@ def initialize(exception, passed_configuration) self.meta_data = {} self.release_stage = configuration.release_stage self.severity = "warning" + self.default_severity = true self.user = {} end @@ -77,6 +81,7 @@ def as_json type: app_type }, context: context, + defaultSeverity: default_severity, device: { hostname: hostname }, @@ -84,9 +89,15 @@ def as_json groupingHash: grouping_hash, payloadVersion: CURRENT_PAYLOAD_VERSION, severity: severity, + unhandled: @unhandled, user: user } + # add severity_reason if necessary + if @unhandled + payload_event[:severityReason] = @severity_reason + end + # cleanup character encodings payload_event = Bugsnag::Cleaner.clean_object_encoding(payload_event) diff --git a/lib/bugsnag/tasks/bugsnag.rake b/lib/bugsnag/tasks/bugsnag.rake index 25618bef2..1bc41d25c 100644 --- a/lib/bugsnag/tasks/bugsnag.rake +++ b/lib/bugsnag/tasks/bugsnag.rake @@ -6,7 +6,14 @@ namespace :bugsnag do begin raise RuntimeError.new("Bugsnag test exception") rescue => e - Bugsnag.notify(e, {:context => "rake#test_exception"}) + Bugsnag.auto_notify(e, { + :type => "middleware", + :attributes => { + :name => "rake" + } + }) do |report| + report.context = "rake#test_exception" + end end end end diff --git a/spec/report_spec.rb b/spec/report_spec.rb index 7a0947bc8..f04f43292 100644 --- a/spec/report_spec.rb +++ b/spec/report_spec.rb @@ -388,7 +388,7 @@ def gloops end it "autonotifies errors" do - Bugsnag.notify(BugsnagTestException.new("It crashed"), true) do |report| + Bugsnag.auto_notify(BugsnagTestException.new("It crashed"), {}) do |report| report.severity = "error" end @@ -421,12 +421,12 @@ def gloops } end - it "does not send a notification if auto_notify is false" do + it "does not send an automatic notification if auto_notify is false" do Bugsnag.configure do |config| config.auto_notify = false end - Bugsnag.notify(BugsnagTestException.new("It crashed"), true) + Bugsnag.auto_notify(BugsnagTestException.new("It crashed"), true) expect(Bugsnag).not_to have_sent_notification end @@ -888,6 +888,52 @@ def gloops } end + it 'should use defaults when notify is called' do + Bugsnag.notify(BugsnagTestException.new("It crashed")) + + expect(Bugsnag).to have_sent_notification{ |payload| + event = payload["events"][0] + expect(event["unhandled"]).to be false + expect(event["severityReason"].nil?).to be true + expect(event["defaultSeverity"]).to be true + } + end + + it 'should attach a severity reason when auto_notify is called' do + Bugsnag.auto_notify( + BugsnagTestException.new("It crashed"), + { + :type => "middleware_handler", + :attributes => { + :name => "middleware_test" + } + } + ) + + expect(Bugsnag).to have_sent_notification{ |payload| + event = payload["events"][0] + expect(event["severityReason"]).to eq( + { + "type" => "middleware_handler", + "attributes" => { + "name" => "middleware_test" + } + } + ) + expect(event["unhandled"]).to be true + } + end + + it 'should set default_severity flag if severity is changed' do + Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report| + report.severity = "info" + end + + expect(Bugsnag).to have_sent_notification{ |payload| + expect(payload["events"][0]["defaultSeverity"]).to be false + } + end + if defined?(JRUBY_VERSION) it "should work with java.lang.Throwables" do From 4e40c4b3d500e6dcd71fefb71e3198569519fdcf Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Fri, 15 Sep 2017 10:25:31 +0100 Subject: [PATCH 02/17] Recombined auto_notify and notify, using auto_notify flag --- lib/bugsnag.rb | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/lib/bugsnag.rb b/lib/bugsnag.rb index 51d996435..fbef91f9f 100644 --- a/lib/bugsnag.rb +++ b/lib/bugsnag.rb @@ -33,37 +33,14 @@ def configure end # Explicitly notify of an exception - def notify(exception, &block) + def notify(exception, auto_notify=false, &block) report = Report.new(exception, configuration) - send_report(report, false, &block) - end - # For automatic notification of exceptions - def auto_notify(exception, severity_reason, &block) - if !configuration.auto_notify + if !configuration.auto_notify && auto_notify configuration.debug("Not notifying because auto_notify is disabled") return end - report = Report.new(exception, configuration, true, severity_reason) - - send_report(report, true, &block) - end - - # Configuration getters - def configuration - @configuration = nil unless defined?(@configuration) - @configuration || LOCK.synchronize { @configuration ||= Bugsnag::Configuration.new } - end - - # Allow access to "before notify" callbacks - def before_notify_callbacks - Bugsnag.configuration.request_data[:before_callbacks] ||= [] - end - - private - def send_report(report, auto_notify, &block) - if !configuration.valid_api_key? configuration.debug("Not notifying due to an invalid api_key") return @@ -107,9 +84,7 @@ def send_report(report, auto_notify, &block) end # Test whether severity has been changed - if report.severity != initial_severity - report.default_severity = false - end + report.default_severity = report.severity == initial_severity # Deliver configuration.info("Notifying #{configuration.endpoint} of #{report.exceptions.last[:errorClass]}") @@ -118,6 +93,17 @@ def send_report(report, auto_notify, &block) Bugsnag::Delivery[configuration.delivery_method].deliver(configuration.endpoint, payload_string, configuration) end end + + # Configuration getters + def configuration + @configuration = nil unless defined?(@configuration) + @configuration || LOCK.synchronize { @configuration ||= Bugsnag::Configuration.new } + end + + # Allow access to "before notify" callbacks + def before_notify_callbacks + Bugsnag.configuration.request_data[:before_callbacks] ||= [] + end end end From 5d0411e2def31199b342f878a398c79c42440afc Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Fri, 15 Sep 2017 10:28:43 +0100 Subject: [PATCH 03/17] Made unhandled & severity_reason fields uneditable by user --- lib/bugsnag/report.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/bugsnag/report.rb b/lib/bugsnag/report.rb index 43cd8bd1c..c80027589 100644 --- a/lib/bugsnag/report.rb +++ b/lib/bugsnag/report.rb @@ -28,10 +28,9 @@ class Report attr_accessor :severity attr_accessor :user - def initialize(exception, passed_configuration, unhandled=false, severity_reason=nil) + def initialize(exception, passed_configuration) @should_ignore = false - @unhandled = unhandled - @severity_reason = severity_reason + @unhandled = false self.configuration = passed_configuration @@ -117,6 +116,13 @@ def as_json } end + def set_handled_state(handled_state) + if !@unhandled + @unhandled = true + @severity_reason = handled_state + end + end + def ignore? @should_ignore end From 880affae547d8355928e1d7f9605015871687ccb Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Fri, 15 Sep 2017 10:29:06 +0100 Subject: [PATCH 04/17] Changed auto_notifies to notify with auto_notify enabled --- lib/bugsnag/integrations/mailman.rb | 9 ++++--- lib/bugsnag/integrations/rack.rb | 26 ++++++++++--------- .../rails/active_record_rescue.rb | 13 +++++----- lib/bugsnag/integrations/railtie.rb | 13 +++++----- lib/bugsnag/integrations/rake.rb | 13 +++++----- lib/bugsnag/integrations/resque.rb | 13 +++++----- lib/bugsnag/integrations/sidekiq.rb | 10 +++---- 7 files changed, 51 insertions(+), 46 deletions(-) diff --git a/lib/bugsnag/integrations/mailman.rb b/lib/bugsnag/integrations/mailman.rb index 86f4694ae..3abf4918a 100644 --- a/lib/bugsnag/integrations/mailman.rb +++ b/lib/bugsnag/integrations/mailman.rb @@ -13,13 +13,14 @@ def call(mail) yield rescue Exception => ex raise ex if [Interrupt, SystemExit, SignalException].include? ex.class - Bugsnag.auto_notify(ex, { - :type => "middleware", + Bugsnag.notify(ex, true) do |report| + report.severity = "error" + report.set_handled_state({ + :type => "middleware_handler", :attributes => { :name => "mailman" } - }) do |report| - report.severity = "error" + }) end raise ensure diff --git a/lib/bugsnag/integrations/rack.rb b/lib/bugsnag/integrations/rack.rb index 23c071b25..149aea801 100644 --- a/lib/bugsnag/integrations/rack.rb +++ b/lib/bugsnag/integrations/rack.rb @@ -33,13 +33,14 @@ def call(env) response = @app.call(env) rescue Exception => raised # Notify bugsnag of rack exceptions - Bugsnag.auto_notify(raised, { - :type => "middleware_handler", - :attirbutes => { - :name => "rack" - } - }) do |report| + Bugsnag.notify(raised, true) do |report| report.severity = "error" + report.set_handled_state({ + :type => "middleware_handler", + :attirbutes => { + :name => "rack" + } + }) end # Re-raise the exception @@ -48,13 +49,14 @@ def call(env) # Notify bugsnag of rack exceptions if env["rack.exception"] - Bugsnag.auto_notify(env["rack.exception"], { - :type => "middleware", - :attributes => { - :name => "rack" - } - }) do |report| + Bugsnag.notify(env["rack.exception"], true) do |report| report.severity = "error" + report.set_handled_state({ + :type => "middleware_handler", + :attributes => { + :name => "rack" + } + }) end end diff --git a/lib/bugsnag/integrations/rails/active_record_rescue.rb b/lib/bugsnag/integrations/rails/active_record_rescue.rb index e4a19748d..36b75f6f8 100644 --- a/lib/bugsnag/integrations/rails/active_record_rescue.rb +++ b/lib/bugsnag/integrations/rails/active_record_rescue.rb @@ -8,13 +8,14 @@ def run_callbacks(kind, *args, &block) super rescue StandardError => exception # This exception will NOT be escalated, so notify it here. - Bugsnag.auto_notify(exception, { - :type => "middleware", - :attributes => { - :name => "active record" - } - }) do |report| + Bugsnag.notify(exception, true) do |report| report.severity = "error" + report.set_handled_state({ + :type => "middleware_handler", + :attributes => { + :name => "active record" + } + }) end raise end diff --git a/lib/bugsnag/integrations/railtie.rb b/lib/bugsnag/integrations/railtie.rb index 6e65183af..43bd5f9b2 100644 --- a/lib/bugsnag/integrations/railtie.rb +++ b/lib/bugsnag/integrations/railtie.rb @@ -17,13 +17,14 @@ class Railtie < Rails::Railtie runner do at_exit do if $! - Bugsnag.auto_notify($!, { - :type => "middleware", - :attributes => { - :name => "railtie" - } - }) do |report| + Bugsnag.notify($!, true) do |report| report.severity = "error" + report.set_handled_state({ + :type => "middleware_handler", + :attributes => { + :name => "railtie" + } + }) end end end diff --git a/lib/bugsnag/integrations/rake.rb b/lib/bugsnag/integrations/rake.rb index 3d6a73896..d022eb9c3 100644 --- a/lib/bugsnag/integrations/rake.rb +++ b/lib/bugsnag/integrations/rake.rb @@ -12,13 +12,14 @@ def execute_with_bugsnag(args=nil) execute_without_bugsnag(args) rescue Exception => ex - Bugsnag.auto_notify(ex, { - :type => "middleware", - :attributes => { - :name => "rake" - } - }) do |report| + Bugsnag.notify(ex, true) do |report| report.severity = "error" + report.set_handled_state({ + :type => "middleware_handler", + :attributes => { + :name => "rake" + } + }) end raise ensure diff --git a/lib/bugsnag/integrations/resque.rb b/lib/bugsnag/integrations/resque.rb index 7e3e9110c..143258f3f 100644 --- a/lib/bugsnag/integrations/resque.rb +++ b/lib/bugsnag/integrations/resque.rb @@ -26,13 +26,14 @@ def self.add_failure_backend end def save - Bugsnag.auto_notify(exception, { - :type => "middleware", - :attributes => { - :name => "mailman" - } - }) do |report| + Bugsnag.notify(exception, true) do |report| report.severity = "error" + report.set_handled_state({ + :type => "middleware_handler", + :attributes => { + :name => "mailman" + } + }) report.meta_data.merge!({:context => "#{payload['class']}@#{queue}", :payload => payload, :delivery_method => :synchronous}) end end diff --git a/lib/bugsnag/integrations/sidekiq.rb b/lib/bugsnag/integrations/sidekiq.rb index 236b9f1ed..71e519617 100644 --- a/lib/bugsnag/integrations/sidekiq.rb +++ b/lib/bugsnag/integrations/sidekiq.rb @@ -16,16 +16,14 @@ def call(worker, msg, queue) yield rescue Exception => ex raise ex if [Interrupt, SystemExit, SignalException].include? ex.class - Bugsnag.auto_notify( - ex, - { + Bugsnag.notify(ex, true) do |report| + report.severity = "error" + report.set_handled_states({ :type => 'middleware_handler', :attributes => { :name => "sidekiq" } - } - ) do |report| - report.severity = "error" + }) end raise ensure From cdb80c43b68e7a643d76343274be516f0d32e49c Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Fri, 15 Sep 2017 10:29:27 +0100 Subject: [PATCH 05/17] Removed auto-notify from integration test --- lib/bugsnag/tasks/bugsnag.rake | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/bugsnag/tasks/bugsnag.rake b/lib/bugsnag/tasks/bugsnag.rake index 1bc41d25c..be214b3da 100644 --- a/lib/bugsnag/tasks/bugsnag.rake +++ b/lib/bugsnag/tasks/bugsnag.rake @@ -6,12 +6,7 @@ namespace :bugsnag do begin raise RuntimeError.new("Bugsnag test exception") rescue => e - Bugsnag.auto_notify(e, { - :type => "middleware", - :attributes => { - :name => "rake" - } - }) do |report| + Bugsnag.notify(e) do |report| report.context = "rake#test_exception" end end From 2aa48caf31cb5fcc4858d6beec00def23903ad14 Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Fri, 15 Sep 2017 10:41:37 +0100 Subject: [PATCH 06/17] Tracking auto_notify in report to stop unhandled being set in manual case --- lib/bugsnag.rb | 2 +- lib/bugsnag/report.rb | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/bugsnag.rb b/lib/bugsnag.rb index fbef91f9f..e148cdb89 100644 --- a/lib/bugsnag.rb +++ b/lib/bugsnag.rb @@ -34,7 +34,7 @@ def configure # Explicitly notify of an exception def notify(exception, auto_notify=false, &block) - report = Report.new(exception, configuration) + report = Report.new(exception, configuration, auto_notify) if !configuration.auto_notify && auto_notify configuration.debug("Not notifying because auto_notify is disabled") diff --git a/lib/bugsnag/report.rb b/lib/bugsnag/report.rb index c80027589..ea4fd97e5 100644 --- a/lib/bugsnag/report.rb +++ b/lib/bugsnag/report.rb @@ -28,8 +28,9 @@ class Report attr_accessor :severity attr_accessor :user - def initialize(exception, passed_configuration) + def initialize(exception, passed_configuration, auto_notify=false) @should_ignore = false + @auto_notify = auto_notify @unhandled = false self.configuration = passed_configuration @@ -117,7 +118,7 @@ def as_json end def set_handled_state(handled_state) - if !@unhandled + if @auto_notify @unhandled = true @severity_reason = handled_state end From 4d1770d890593d0414f4003dd74bb79d9868090a Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Fri, 15 Sep 2017 10:43:39 +0100 Subject: [PATCH 07/17] Ensures handledState properties only set once --- lib/bugsnag/report.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bugsnag/report.rb b/lib/bugsnag/report.rb index ea4fd97e5..a2a335d9a 100644 --- a/lib/bugsnag/report.rb +++ b/lib/bugsnag/report.rb @@ -118,7 +118,7 @@ def as_json end def set_handled_state(handled_state) - if @auto_notify + if @auto_notify && !@unhandled @unhandled = true @severity_reason = handled_state end From a7799eed8756af29ea7685d1155a9b5220086077 Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Fri, 15 Sep 2017 10:50:18 +0100 Subject: [PATCH 08/17] Modified and added tests to reflect new layout --- spec/middleware_spec.rb | 64 +++++++++++++++++++++++++++++++++++++++++ spec/report_spec.rb | 42 +++++++++++++++------------ 2 files changed, 87 insertions(+), 19 deletions(-) diff --git a/spec/middleware_spec.rb b/spec/middleware_spec.rb index 24b9f8f91..8821b6f58 100644 --- a/spec/middleware_spec.rb +++ b/spec/middleware_spec.rb @@ -178,4 +178,68 @@ def call(report) } end + it "doesn't allow handledState properties to be changed in middleware" do + HandledStateChanger = Class.new do + def initialize(bugsnag) + @bugsnag = bugsnag + end + + def call(report) + report.set_handled_state({ + :test => "test" + }) + @bugsnag.call(report) + end + end + + Bugsnag.configure do |c| + c.middleware.use HandledStateChanger + end + + Bugsnag.notify(BugsnagTestException.new("It crashed"), true) do |report| + report.set_handled_state({ + :type => "middleware_handler", + :attributes => { + :name => "middleware_test" + } + }) + end + + expect(Bugsnag).to have_sent_notification{ |payload| + event = get_event_from_payload(payload) + expect(event["unhandled"]).to be true + expect(event["severityReason"]).to eq({ + "type" => "middleware_handler", + "attributes" => { + "name" => "middleware_test" + } + }) + } + end + + it "sets defaultSeverity to false if changed in middleware" do + SeverityChanger = Class.new do + def initialize(bugsnag) + @bugsnag = bugsnag + end + + def call(report) + report.severity = "info" + @bugsnag.call(report) + end + end + + Bugsnag.configure do |c| + c.middleware.use SeverityChanger + end + + Bugsnag.notify(BugsnagTestException.new("It crashed")) + + expect(Bugsnag).to have_sent_notification{ |payload| + event = get_event_from_payload(payload) + expect(event["severity"]).to eq("info") + expect(event["defaultSeverity"]).to be false + } + end + end diff --git a/spec/report_spec.rb b/spec/report_spec.rb index f04f43292..fcbe68185 100644 --- a/spec/report_spec.rb +++ b/spec/report_spec.rb @@ -387,18 +387,6 @@ def gloops } end - it "autonotifies errors" do - Bugsnag.auto_notify(BugsnagTestException.new("It crashed"), {}) do |report| - report.severity = "error" - end - - expect(Bugsnag).to have_sent_notification{ |payload| - event = get_event_from_payload(payload) - expect(event["severity"]).to eq("error") - } - end - - it "accepts a context in overrides" do Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report| report.context = 'test_context' @@ -426,7 +414,7 @@ def gloops config.auto_notify = false end - Bugsnag.auto_notify(BugsnagTestException.new("It crashed"), true) + Bugsnag.notify(BugsnagTestException.new("It crashed"), true) expect(Bugsnag).not_to have_sent_notification end @@ -899,16 +887,15 @@ def gloops } end - it 'should attach a severity reason when auto_notify is called' do - Bugsnag.auto_notify( - BugsnagTestException.new("It crashed"), - { + it 'should attach severity reason through a block when auto_notify is true' do + Bugsnag.notify(BugsnagTestException.new("It crashed"), true) do |report| + report.set_handled_state({ :type => "middleware_handler", :attributes => { :name => "middleware_test" } - } - ) + }) + end expect(Bugsnag).to have_sent_notification{ |payload| event = payload["events"][0] @@ -924,6 +911,23 @@ def gloops } end + it 'should not attach severity reason when auto_notify is false' do + Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report| + report.set_handled_state({ + :type => "middleware_handler", + :attributes => { + :name => "middleware_test" + } + }) + end + + expect(Bugsnag).to have_sent_notification{ |payload| + event = payload["events"][0] + expect(event["unhandled"]).to be false + expect(event["severityReason"].nil?).to be true + } + end + it 'should set default_severity flag if severity is changed' do Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report| report.severity = "info" From 725f7f15dd7d72d6064cc09ba75ff9820618bd88 Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Wed, 20 Sep 2017 13:37:08 +0100 Subject: [PATCH 09/17] Added info error classifier middleware --- lib/bugsnag/configuration.rb | 2 ++ lib/bugsnag/middleware/classify_error.rb | 45 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 lib/bugsnag/middleware/classify_error.rb diff --git a/lib/bugsnag/configuration.rb b/lib/bugsnag/configuration.rb index 38b23e4cc..cb922a356 100644 --- a/lib/bugsnag/configuration.rb +++ b/lib/bugsnag/configuration.rb @@ -5,6 +5,7 @@ require "bugsnag/middleware/callbacks" require "bugsnag/middleware/exception_meta_data" require "bugsnag/middleware/ignore_error_class" +require "bugsnag/middleware/classify_error" module Bugsnag class Configuration @@ -73,6 +74,7 @@ def initialize self.internal_middleware = Bugsnag::MiddlewareStack.new self.internal_middleware.use Bugsnag::Middleware::ExceptionMetaData self.internal_middleware.use Bugsnag::Middleware::IgnoreErrorClass + self.internal_middleware.use Bugsnag::Middleware::ClassifyError self.middleware = Bugsnag::MiddlewareStack.new self.middleware.use Bugsnag::Middleware::Callbacks diff --git a/lib/bugsnag/middleware/classify_error.rb b/lib/bugsnag/middleware/classify_error.rb new file mode 100644 index 000000000..1867c2c4f --- /dev/null +++ b/lib/bugsnag/middleware/classify_error.rb @@ -0,0 +1,45 @@ +module Bugsnag::Middleware + class ClassifyError + INFO_CLASSES = [ + "AbstractController::ActionNotFound", + "ActionController::InvalidAuthenticityToken", + "ActionController::ParameterMissing", + "ActionController::UnknownAction", + "ActionController::UnknownFormat", + "ActionController::UnknownHttpMethod", + "ActiveRecord::RecordNotFound", + "CGI::Session::CookieStore::TamperedWithCookie", + "Mongoid::Errors::DocumentNotFound", + "SignalException", + "SystemExit" + ] + + def initialize(bugsnag) + @bugsnag = bugsnag + end + + def call(report) + report.raw_exceptions.each do |ex| + ancestor_chain = ex.class.ancestors.select { |ancestor| ancestor.is_a?(Class) }.map { |ancestor| ancestor.to_s } + + INFO_CLASSES.each do |info_class| + if ancestor_chain.include?(info_class) + report.set_handled_state( + { + :type => "error_class", + :attributes => { + :errorClass => info_class + } + } + ) + report.severity = 'info' + break; + end + end + end + + @bugsnag.call(report) + end + end +end + \ No newline at end of file From 7023dab88512f7140d43b7ec1a6b33706ee49501 Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Wed, 20 Sep 2017 15:22:53 +0100 Subject: [PATCH 10/17] Updated integrations severityReasons --- lib/bugsnag/integrations/delayed_job.rb | 6 ++++++ lib/bugsnag/integrations/mailman.rb | 4 ++-- lib/bugsnag/integrations/rack.rb | 4 ++-- lib/bugsnag/integrations/rails/active_record_rescue.rb | 4 ++-- lib/bugsnag/integrations/railtie.rb | 4 ++-- lib/bugsnag/integrations/rake.rb | 4 ++-- lib/bugsnag/integrations/resque.rb | 4 ++-- lib/bugsnag/integrations/sidekiq.rb | 4 ++-- 8 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/bugsnag/integrations/delayed_job.rb b/lib/bugsnag/integrations/delayed_job.rb index 235ca2098..3db4e1efe 100644 --- a/lib/bugsnag/integrations/delayed_job.rb +++ b/lib/bugsnag/integrations/delayed_job.rb @@ -36,6 +36,12 @@ def error(job, error) ::Bugsnag.notify(error, true) do |report| report.severity = "error" + report.set_handled_state({ + :type => "unhandledExceptionMiddleware", + :attributes => { + :framework => "DelayedJob" + } + }) report.meta_data.merge! overrides end diff --git a/lib/bugsnag/integrations/mailman.rb b/lib/bugsnag/integrations/mailman.rb index 3abf4918a..1ed27a3f6 100644 --- a/lib/bugsnag/integrations/mailman.rb +++ b/lib/bugsnag/integrations/mailman.rb @@ -16,9 +16,9 @@ def call(mail) Bugsnag.notify(ex, true) do |report| report.severity = "error" report.set_handled_state({ - :type => "middleware_handler", + :type => "unhandledExceptionMiddleware", :attributes => { - :name => "mailman" + :framework => "Mailman" } }) end diff --git a/lib/bugsnag/integrations/rack.rb b/lib/bugsnag/integrations/rack.rb index 149aea801..1525b5de9 100644 --- a/lib/bugsnag/integrations/rack.rb +++ b/lib/bugsnag/integrations/rack.rb @@ -36,9 +36,9 @@ def call(env) Bugsnag.notify(raised, true) do |report| report.severity = "error" report.set_handled_state({ - :type => "middleware_handler", + :type => "unhandledExceptionMiddleware", :attirbutes => { - :name => "rack" + :framework => "Rack" } }) end diff --git a/lib/bugsnag/integrations/rails/active_record_rescue.rb b/lib/bugsnag/integrations/rails/active_record_rescue.rb index 36b75f6f8..09247a727 100644 --- a/lib/bugsnag/integrations/rails/active_record_rescue.rb +++ b/lib/bugsnag/integrations/rails/active_record_rescue.rb @@ -11,9 +11,9 @@ def run_callbacks(kind, *args, &block) Bugsnag.notify(exception, true) do |report| report.severity = "error" report.set_handled_state({ - :type => "middleware_handler", + :type => "unhandledExceptionMiddleware", :attributes => { - :name => "active record" + :framework => "Rails" } }) end diff --git a/lib/bugsnag/integrations/railtie.rb b/lib/bugsnag/integrations/railtie.rb index 43bd5f9b2..fa5f355fa 100644 --- a/lib/bugsnag/integrations/railtie.rb +++ b/lib/bugsnag/integrations/railtie.rb @@ -20,9 +20,9 @@ class Railtie < Rails::Railtie Bugsnag.notify($!, true) do |report| report.severity = "error" report.set_handled_state({ - :type => "middleware_handler", + :type => "unhandledExceptionMiddleware", :attributes => { - :name => "railtie" + :framework => "Rails" } }) end diff --git a/lib/bugsnag/integrations/rake.rb b/lib/bugsnag/integrations/rake.rb index d022eb9c3..ee68df354 100644 --- a/lib/bugsnag/integrations/rake.rb +++ b/lib/bugsnag/integrations/rake.rb @@ -15,9 +15,9 @@ def execute_with_bugsnag(args=nil) Bugsnag.notify(ex, true) do |report| report.severity = "error" report.set_handled_state({ - :type => "middleware_handler", + :type => "unhandledExceptionMiddleware", :attributes => { - :name => "rake" + :framework => "Rake" } }) end diff --git a/lib/bugsnag/integrations/resque.rb b/lib/bugsnag/integrations/resque.rb index 143258f3f..d2d5cc5fe 100644 --- a/lib/bugsnag/integrations/resque.rb +++ b/lib/bugsnag/integrations/resque.rb @@ -29,9 +29,9 @@ def save Bugsnag.notify(exception, true) do |report| report.severity = "error" report.set_handled_state({ - :type => "middleware_handler", + :type => "unhandledExceptionMiddleware", :attributes => { - :name => "mailman" + :framework => "Resque" } }) report.meta_data.merge!({:context => "#{payload['class']}@#{queue}", :payload => payload, :delivery_method => :synchronous}) diff --git a/lib/bugsnag/integrations/sidekiq.rb b/lib/bugsnag/integrations/sidekiq.rb index 71e519617..825a51027 100644 --- a/lib/bugsnag/integrations/sidekiq.rb +++ b/lib/bugsnag/integrations/sidekiq.rb @@ -19,9 +19,9 @@ def call(worker, msg, queue) Bugsnag.notify(ex, true) do |report| report.severity = "error" report.set_handled_states({ - :type => 'middleware_handler', + :type => 'unhandledExceptionMiddleware', :attributes => { - :name => "sidekiq" + :framework => "Sidekiq" } }) end From 8cba0715ab7e0266c35b7568477fa6020426cc7e Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Wed, 20 Sep 2017 15:23:37 +0100 Subject: [PATCH 11/17] Removed defaultSeverity and enabled modifying of severity-reason --- lib/bugsnag/report.rb | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/lib/bugsnag/report.rb b/lib/bugsnag/report.rb index a2a335d9a..6329fc640 100644 --- a/lib/bugsnag/report.rb +++ b/lib/bugsnag/report.rb @@ -17,7 +17,6 @@ class Report attr_accessor :app_version attr_accessor :configuration attr_accessor :context - attr_accessor :default_severity attr_accessor :delivery_method attr_accessor :exceptions attr_accessor :hostname @@ -26,12 +25,12 @@ class Report attr_accessor :raw_exceptions attr_accessor :release_stage attr_accessor :severity + attr_accessor :severity_reason attr_accessor :user def initialize(exception, passed_configuration, auto_notify=false) @should_ignore = false - @auto_notify = auto_notify - @unhandled = false + @unhandled = auto_notify self.configuration = passed_configuration @@ -46,7 +45,9 @@ def initialize(exception, passed_configuration, auto_notify=false) self.meta_data = {} self.release_stage = configuration.release_stage self.severity = "warning" - self.default_severity = true + self.severity_reason = { + :type => "handledException" + } self.user = {} end @@ -81,7 +82,6 @@ def as_json type: app_type }, context: context, - defaultSeverity: default_severity, device: { hostname: hostname }, @@ -89,15 +89,11 @@ def as_json groupingHash: grouping_hash, payloadVersion: CURRENT_PAYLOAD_VERSION, severity: severity, + severityReason: severity_reason, unhandled: @unhandled, user: user } - # add severity_reason if necessary - if @unhandled - payload_event[:severityReason] = @severity_reason - end - # cleanup character encodings payload_event = Bugsnag::Cleaner.clean_object_encoding(payload_event) @@ -117,13 +113,6 @@ def as_json } end - def set_handled_state(handled_state) - if @auto_notify && !@unhandled - @unhandled = true - @severity_reason = handled_state - end - end - def ignore? @should_ignore end From f7db8af35a74a1e39bad87f101c1dada160622c9 Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Wed, 20 Sep 2017 16:12:10 +0100 Subject: [PATCH 12/17] Added severityReason changes based on callbacks --- lib/bugsnag.rb | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/bugsnag.rb b/lib/bugsnag.rb index e148cdb89..83c71022d 100644 --- a/lib/bugsnag.rb +++ b/lib/bugsnag.rb @@ -65,8 +65,9 @@ def notify(exception, auto_notify=false, &block) return end - # Store default severity for future reference - initial_severity = report.severity + # Store before_middleware severity reason for future reference + before_middleware_severity = report.severity + before_middleware_reason = report.severity_reason # Run users middleware configuration.middleware.run(report) do @@ -75,6 +76,16 @@ def notify(exception, auto_notify=false, &block) return end + # Update severity reason if necessary + if report.severity != before_middleware_severity + report.severity_reason = { + :type => "userSpecifiedSeverity" + } + end + + # Store severity before user callbacks + before_callback_severity = report.severity + # If this is not an auto_notify then the block was provided by the user. This should be the last # block that is run as it is the users "most specific" block. yield(report) if block_given? && !auto_notify @@ -83,8 +94,15 @@ def notify(exception, auto_notify=false, &block) return end - # Test whether severity has been changed - report.default_severity = report.severity == initial_severity + # Test whether severity has been changed and ensure severity_reason is consistant in auto_notify case + if auto_notify + report.severity = before_middleware_severity + report.severity_reason = before_middleware_reason + elsif report.severity != before_callback_severity + report.severity_reason = { + :type => "userCallbackSeverity" + } + end # Deliver configuration.info("Notifying #{configuration.endpoint} of #{report.exceptions.last[:errorClass]}") From 9d4f95e09f80cb966ac86b755ee3452c4885cc8c Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Thu, 21 Sep 2017 17:10:28 +0100 Subject: [PATCH 13/17] Updated to correct functions calls --- lib/bugsnag.rb | 28 ++++++------------- lib/bugsnag/integrations/delayed_job.rb | 15 ++++++---- lib/bugsnag/integrations/mailman.rb | 15 ++++++---- lib/bugsnag/integrations/rack.rb | 25 +++++++++-------- .../rails/active_record_rescue.rb | 13 +++++---- lib/bugsnag/integrations/railtie.rb | 15 ++++++---- lib/bugsnag/integrations/rake.rb | 14 ++++++---- lib/bugsnag/integrations/resque.rb | 15 ++++++---- lib/bugsnag/integrations/sidekiq.rb | 15 ++++++---- lib/bugsnag/middleware/classify_error.rb | 21 ++++++++------ lib/bugsnag/report.rb | 9 +++++- 11 files changed, 102 insertions(+), 83 deletions(-) diff --git a/lib/bugsnag.rb b/lib/bugsnag.rb index 83c71022d..574805cdb 100644 --- a/lib/bugsnag.rb +++ b/lib/bugsnag.rb @@ -34,8 +34,7 @@ def configure # Explicitly notify of an exception def notify(exception, auto_notify=false, &block) - report = Report.new(exception, configuration, auto_notify) - + if !configuration.auto_notify && auto_notify configuration.debug("Not notifying because auto_notify is disabled") return @@ -51,6 +50,8 @@ def notify(exception, auto_notify=false, &block) return end + report = Report.new(exception, configuration, auto_notify) + # If this is an auto_notify we yield the block before the any middleware is run yield(report) if block_given? && auto_notify if report.ignore? @@ -66,8 +67,8 @@ def notify(exception, auto_notify=false, &block) end # Store before_middleware severity reason for future reference - before_middleware_severity = report.severity - before_middleware_reason = report.severity_reason + initial_severity = report.severity + initial_reason = report.severity_reason # Run users middleware configuration.middleware.run(report) do @@ -76,16 +77,6 @@ def notify(exception, auto_notify=false, &block) return end - # Update severity reason if necessary - if report.severity != before_middleware_severity - report.severity_reason = { - :type => "userSpecifiedSeverity" - } - end - - # Store severity before user callbacks - before_callback_severity = report.severity - # If this is not an auto_notify then the block was provided by the user. This should be the last # block that is run as it is the users "most specific" block. yield(report) if block_given? && !auto_notify @@ -95,13 +86,12 @@ def notify(exception, auto_notify=false, &block) end # Test whether severity has been changed and ensure severity_reason is consistant in auto_notify case - if auto_notify - report.severity = before_middleware_severity - report.severity_reason = before_middleware_reason - elsif report.severity != before_callback_severity + if report.severity != before_callback_severity report.severity_reason = { - :type => "userCallbackSeverity" + :type => Bugsnag::Report::USER_CALLBACK_SET_SEVERITY } + else + report.severity_reason = initial_reason end # Deliver diff --git a/lib/bugsnag/integrations/delayed_job.rb b/lib/bugsnag/integrations/delayed_job.rb index 3db4e1efe..54d2a4547 100644 --- a/lib/bugsnag/integrations/delayed_job.rb +++ b/lib/bugsnag/integrations/delayed_job.rb @@ -9,6 +9,11 @@ module Delayed module Plugins class Bugsnag < Plugin + + FRAMEWORK_ATTRIBUTES = { + :framework => "DelayedJob" + } + module Notify def error(job, error) overrides = { @@ -36,12 +41,10 @@ def error(job, error) ::Bugsnag.notify(error, true) do |report| report.severity = "error" - report.set_handled_state({ - :type => "unhandledExceptionMiddleware", - :attributes => { - :framework => "DelayedJob" - } - }) + report.severity_reason = { + :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + :attributes => FRAMEWORK_ATTRIBUTES + } report.meta_data.merge! overrides end diff --git a/lib/bugsnag/integrations/mailman.rb b/lib/bugsnag/integrations/mailman.rb index 1ed27a3f6..78eeecfd9 100644 --- a/lib/bugsnag/integrations/mailman.rb +++ b/lib/bugsnag/integrations/mailman.rb @@ -2,6 +2,11 @@ module Bugsnag class Mailman + + FRAMEWORK_ATTRIBUTES = { + :framework => "Mailman" + } + def initialize Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Mailman) Bugsnag.configuration.app_type = "mailman" @@ -15,12 +20,10 @@ def call(mail) raise ex if [Interrupt, SystemExit, SignalException].include? ex.class Bugsnag.notify(ex, true) do |report| report.severity = "error" - report.set_handled_state({ - :type => "unhandledExceptionMiddleware", - :attributes => { - :framework => "Mailman" - } - }) + report.severity_reason = { + :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + :attributes => FRAMEWORK_ATTRIBUTES + } end raise ensure diff --git a/lib/bugsnag/integrations/rack.rb b/lib/bugsnag/integrations/rack.rb index 1525b5de9..b23d78a53 100644 --- a/lib/bugsnag/integrations/rack.rb +++ b/lib/bugsnag/integrations/rack.rb @@ -1,5 +1,10 @@ module Bugsnag class Rack + + FRAMEWORK_ATTRIBUTES = { + :framework => "Rack" + } + def initialize(app) @app = app @@ -35,12 +40,10 @@ def call(env) # Notify bugsnag of rack exceptions Bugsnag.notify(raised, true) do |report| report.severity = "error" - report.set_handled_state({ - :type => "unhandledExceptionMiddleware", - :attirbutes => { - :framework => "Rack" - } - }) + report.severity_reason = { + :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + :attributes => Bugsnag::Rack::FRAMEWORK_ATTRIBUTES + } end # Re-raise the exception @@ -51,12 +54,10 @@ def call(env) if env["rack.exception"] Bugsnag.notify(env["rack.exception"], true) do |report| report.severity = "error" - report.set_handled_state({ - :type => "middleware_handler", - :attributes => { - :name => "rack" - } - }) + report.severity_reason = { + :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + :attributes => FRAMEWORK_ATTRIBUTES + } end end diff --git a/lib/bugsnag/integrations/rails/active_record_rescue.rb b/lib/bugsnag/integrations/rails/active_record_rescue.rb index 09247a727..65843e872 100644 --- a/lib/bugsnag/integrations/rails/active_record_rescue.rb +++ b/lib/bugsnag/integrations/rails/active_record_rescue.rb @@ -1,6 +1,9 @@ module Bugsnag::Rails module ActiveRecordRescue KINDS = [:commit, :rollback].freeze + FRAMEWORK_ATTRIBUTES = { + :framework => "Rails" + } def run_callbacks(kind, *args, &block) if KINDS.include?(kind) @@ -10,12 +13,10 @@ def run_callbacks(kind, *args, &block) # This exception will NOT be escalated, so notify it here. Bugsnag.notify(exception, true) do |report| report.severity = "error" - report.set_handled_state({ - :type => "unhandledExceptionMiddleware", - :attributes => { - :framework => "Rails" - } - }) + report.severity_reason = { + :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + :attributes => FRAMEWORK_ATTRIBUTES + } end raise end diff --git a/lib/bugsnag/integrations/railtie.rb b/lib/bugsnag/integrations/railtie.rb index fa5f355fa..126a4495b 100644 --- a/lib/bugsnag/integrations/railtie.rb +++ b/lib/bugsnag/integrations/railtie.rb @@ -7,6 +7,11 @@ module Bugsnag class Railtie < Rails::Railtie + + FRAMEWORK_ATTRIBUTES = { + :framework => "Rails" + } + rake_tasks do require "bugsnag/integrations/rake" load "bugsnag/tasks/bugsnag.rake" @@ -19,12 +24,10 @@ class Railtie < Rails::Railtie if $! Bugsnag.notify($!, true) do |report| report.severity = "error" - report.set_handled_state({ - :type => "unhandledExceptionMiddleware", - :attributes => { - :framework => "Rails" - } - }) + report.severity_reason = { + :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + :attributes => FRAMEWORK_ATTRIBUTES + } end end end diff --git a/lib/bugsnag/integrations/rake.rb b/lib/bugsnag/integrations/rake.rb index ee68df354..4756f2c94 100644 --- a/lib/bugsnag/integrations/rake.rb +++ b/lib/bugsnag/integrations/rake.rb @@ -4,6 +4,10 @@ class Rake::Task + FRAMEWORK_ATTRIBUTES = { + :framework => "Rake" + } + def execute_with_bugsnag(args=nil) Bugsnag.configuration.app_type = "rake" old_task = Bugsnag.configuration.request_data[:bugsnag_running_task] @@ -14,12 +18,10 @@ def execute_with_bugsnag(args=nil) rescue Exception => ex Bugsnag.notify(ex, true) do |report| report.severity = "error" - report.set_handled_state({ - :type => "unhandledExceptionMiddleware", - :attributes => { - :framework => "Rake" - } - }) + report.severity_reason = { + :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + :attributes => FRAMEWORK_ATTRIBUTES + } end raise ensure diff --git a/lib/bugsnag/integrations/resque.rb b/lib/bugsnag/integrations/resque.rb index d2d5cc5fe..409db0e38 100644 --- a/lib/bugsnag/integrations/resque.rb +++ b/lib/bugsnag/integrations/resque.rb @@ -3,6 +3,11 @@ module Bugsnag class Resque < ::Resque::Failure::Base + + FRAMEWORK_ATTRIBUTES = { + :framework => "Resque" + } + def self.configure(&block) add_failure_backend Bugsnag.configure(&block) @@ -28,12 +33,10 @@ def self.add_failure_backend def save Bugsnag.notify(exception, true) do |report| report.severity = "error" - report.set_handled_state({ - :type => "unhandledExceptionMiddleware", - :attributes => { - :framework => "Resque" - } - }) + report.severity_reason = { + :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + :attributes => FRAMEWORK_ATTRIBUTES + } report.meta_data.merge!({:context => "#{payload['class']}@#{queue}", :payload => payload, :delivery_method => :synchronous}) end end diff --git a/lib/bugsnag/integrations/sidekiq.rb b/lib/bugsnag/integrations/sidekiq.rb index 825a51027..ff3e52d7a 100644 --- a/lib/bugsnag/integrations/sidekiq.rb +++ b/lib/bugsnag/integrations/sidekiq.rb @@ -2,6 +2,11 @@ module Bugsnag class Sidekiq + + FRAMEWORK_ATTRIBUTES = { + :framework => "Sidekiq" + } + def initialize Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Sidekiq) Bugsnag.configuration.app_type = "sidekiq" @@ -18,12 +23,10 @@ def call(worker, msg, queue) raise ex if [Interrupt, SystemExit, SignalException].include? ex.class Bugsnag.notify(ex, true) do |report| report.severity = "error" - report.set_handled_states({ - :type => 'unhandledExceptionMiddleware', - :attributes => { - :framework => "Sidekiq" - } - }) + report.severity_reason = { + :type => Bugsnag::Report::UNHANDLED_MIDDLEWARE_EXCEPTION, + :attributes => FRAMEWORK_ATTRIBUTES + } end raise ensure diff --git a/lib/bugsnag/middleware/classify_error.rb b/lib/bugsnag/middleware/classify_error.rb index 1867c2c4f..0867a6432 100644 --- a/lib/bugsnag/middleware/classify_error.rb +++ b/lib/bugsnag/middleware/classify_error.rb @@ -20,20 +20,23 @@ def initialize(bugsnag) def call(report) report.raw_exceptions.each do |ex| - ancestor_chain = ex.class.ancestors.select { |ancestor| ancestor.is_a?(Class) }.map { |ancestor| ancestor.to_s } + + ancestor_chain = ex.class.ancestors.select { + |ancestor| ancestor.is_a?(Class) + }.map { + |ancestor| ancestor.to_s + } INFO_CLASSES.each do |info_class| if ancestor_chain.include?(info_class) - report.set_handled_state( - { - :type => "error_class", - :attributes => { - :errorClass => info_class - } + report.severity_reason = { + :type => Bugsnag::Report::ERROR_CLASS, + :attributes => { + :errorClass => info_class } - ) + } report.severity = 'info' - break; + break end end end diff --git a/lib/bugsnag/report.rb b/lib/bugsnag/report.rb index 6329fc640..aa5b26fc0 100644 --- a/lib/bugsnag/report.rb +++ b/lib/bugsnag/report.rb @@ -8,6 +8,13 @@ class Report NOTIFIER_VERSION = Bugsnag::VERSION NOTIFIER_URL = "http://www.bugsnag.com" + UNHANDLED_EXCEPTION = "unhandledException" + UNHANDLED_EXCEPTION_MIDDLEWARE = "unhandledExceptionMiddleware" + ERROR_CLASS = "errorClass" + HANDLED_EXCEPTION = "handledException" + USER_SPECIFIED_SEVERITY = "userSpecifiedSeverity" + USER_CALLBACK_SET_SEVERITY = "userCallbackSetSeverity" + MAX_EXCEPTIONS_TO_UNWRAP = 5 CURRENT_PAYLOAD_VERSION = "2" @@ -46,7 +53,7 @@ def initialize(exception, passed_configuration, auto_notify=false) self.release_stage = configuration.release_stage self.severity = "warning" self.severity_reason = { - :type => "handledException" + :type => HANDLED_EXCEPTION } self.user = {} end From 391a755b462137567f1fe0919bf0886c936a06ef Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Thu, 21 Sep 2017 17:26:14 +0100 Subject: [PATCH 14/17] updated tests --- lib/bugsnag.rb | 2 +- spec/middleware_spec.rb | 33 ++++----------------------------- spec/report_spec.rb | 25 +++++++------------------ 3 files changed, 12 insertions(+), 48 deletions(-) diff --git a/lib/bugsnag.rb b/lib/bugsnag.rb index 574805cdb..2fbcd2d9f 100644 --- a/lib/bugsnag.rb +++ b/lib/bugsnag.rb @@ -86,7 +86,7 @@ def notify(exception, auto_notify=false, &block) end # Test whether severity has been changed and ensure severity_reason is consistant in auto_notify case - if report.severity != before_callback_severity + if report.severity != initial_severity report.severity_reason = { :type => Bugsnag::Report::USER_CALLBACK_SET_SEVERITY } diff --git a/spec/middleware_spec.rb b/spec/middleware_spec.rb index 8821b6f58..267669e4f 100644 --- a/spec/middleware_spec.rb +++ b/spec/middleware_spec.rb @@ -185,9 +185,9 @@ def initialize(bugsnag) end def call(report) - report.set_handled_state({ + report.severity_reason = { :test => "test" - }) + } @bugsnag.call(report) end end @@ -197,12 +197,12 @@ def call(report) end Bugsnag.notify(BugsnagTestException.new("It crashed"), true) do |report| - report.set_handled_state({ + report.severity_reason = { :type => "middleware_handler", :attributes => { :name => "middleware_test" } - }) + } end expect(Bugsnag).to have_sent_notification{ |payload| @@ -217,29 +217,4 @@ def call(report) } end - it "sets defaultSeverity to false if changed in middleware" do - SeverityChanger = Class.new do - def initialize(bugsnag) - @bugsnag = bugsnag - end - - def call(report) - report.severity = "info" - @bugsnag.call(report) - end - end - - Bugsnag.configure do |c| - c.middleware.use SeverityChanger - end - - Bugsnag.notify(BugsnagTestException.new("It crashed")) - - expect(Bugsnag).to have_sent_notification{ |payload| - event = get_event_from_payload(payload) - expect(event["severity"]).to eq("info") - expect(event["defaultSeverity"]).to be false - } - end - end diff --git a/spec/report_spec.rb b/spec/report_spec.rb index fcbe68185..dbdae3b52 100644 --- a/spec/report_spec.rb +++ b/spec/report_spec.rb @@ -882,19 +882,18 @@ def gloops expect(Bugsnag).to have_sent_notification{ |payload| event = payload["events"][0] expect(event["unhandled"]).to be false - expect(event["severityReason"].nil?).to be true - expect(event["defaultSeverity"]).to be true + expect(event["severityReason"]).to eq({"type" => "handledException"}) } end it 'should attach severity reason through a block when auto_notify is true' do Bugsnag.notify(BugsnagTestException.new("It crashed"), true) do |report| - report.set_handled_state({ + report.severity_reason = { :type => "middleware_handler", :attributes => { :name => "middleware_test" } - }) + } end expect(Bugsnag).to have_sent_notification{ |payload| @@ -911,30 +910,20 @@ def gloops } end - it 'should not attach severity reason when auto_notify is false' do + it 'should not attach severity reason from callback when auto_notify is false' do Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report| - report.set_handled_state({ + report.severity_reason = { :type => "middleware_handler", :attributes => { :name => "middleware_test" } - }) + } end expect(Bugsnag).to have_sent_notification{ |payload| event = payload["events"][0] expect(event["unhandled"]).to be false - expect(event["severityReason"].nil?).to be true - } - end - - it 'should set default_severity flag if severity is changed' do - Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report| - report.severity = "info" - end - - expect(Bugsnag).to have_sent_notification{ |payload| - expect(payload["events"][0]["defaultSeverity"]).to be false + expect(event["severityReason"]).to eq({"type" => "handledException"}) } end From 464ac7f5924958dbd8d7f30b1a31ad4ee185e11f Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Tue, 26 Sep 2017 10:13:29 +0100 Subject: [PATCH 15/17] More sensible defaults in auto_notify cases --- lib/bugsnag/report.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/bugsnag/report.rb b/lib/bugsnag/report.rb index aa5b26fc0..3ed2d78e0 100644 --- a/lib/bugsnag/report.rb +++ b/lib/bugsnag/report.rb @@ -51,10 +51,8 @@ def initialize(exception, passed_configuration, auto_notify=false) self.hostname = configuration.hostname self.meta_data = {} self.release_stage = configuration.release_stage - self.severity = "warning" - self.severity_reason = { - :type => HANDLED_EXCEPTION - } + self.severity = auto_notify ? "error" : "warning" + self.severity_reason = auto_notify ? {:type => UNHANDLED_EXCEPTION} : {:type => HANDLED_EXCEPTION} self.user = {} end From 82519e3943378ab73cf29de640a952deefba6f99 Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Wed, 27 Sep 2017 09:58:27 +0100 Subject: [PATCH 16/17] Incorrect setting --- lib/bugsnag/integrations/sidekiq.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bugsnag/integrations/sidekiq.rb b/lib/bugsnag/integrations/sidekiq.rb index ff3e52d7a..8239e4b2f 100644 --- a/lib/bugsnag/integrations/sidekiq.rb +++ b/lib/bugsnag/integrations/sidekiq.rb @@ -24,7 +24,7 @@ def call(worker, msg, queue) Bugsnag.notify(ex, true) do |report| report.severity = "error" report.severity_reason = { - :type => Bugsnag::Report::UNHANDLED_MIDDLEWARE_EXCEPTION, + :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, :attributes => FRAMEWORK_ATTRIBUTES } end From 085108c7fbbd5fca0d558e4080e5de59d00c85ae Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Wed, 27 Sep 2017 12:08:11 +0100 Subject: [PATCH 17/17] Ensured Bugsnag refered to correctly as const --- lib/bugsnag/integrations/delayed_job.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bugsnag/integrations/delayed_job.rb b/lib/bugsnag/integrations/delayed_job.rb index 54d2a4547..123badce8 100644 --- a/lib/bugsnag/integrations/delayed_job.rb +++ b/lib/bugsnag/integrations/delayed_job.rb @@ -42,7 +42,7 @@ def error(job, error) ::Bugsnag.notify(error, true) do |report| report.severity = "error" report.severity_reason = { - :type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + :type => ::Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, :attributes => FRAMEWORK_ATTRIBUTES } report.meta_data.merge! overrides