Skip to content

Commit

Permalink
Track whether an error report is automatic (#368)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cawllec authored and kattrali committed Oct 2, 2017
1 parent 403d8fa commit 643ae3a
Show file tree
Hide file tree
Showing 15 changed files with 247 additions and 18 deletions.
18 changes: 16 additions & 2 deletions lib/bugsnag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ def configure

# Explicitly notify of an exception
def notify(exception, auto_notify=false, &block)
if auto_notify && !configuration.auto_notify

if !configuration.auto_notify && auto_notify
configuration.debug("Not notifying because auto_notify is disabled")
return
end
Expand All @@ -49,7 +50,7 @@ def notify(exception, auto_notify=false, &block)
return
end

report = Report.new(exception, configuration)
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
Expand All @@ -65,6 +66,10 @@ def notify(exception, auto_notify=false, &block)
return
end

# Store before_middleware severity reason for future reference
initial_severity = report.severity
initial_reason = report.severity_reason

# Run users middleware
configuration.middleware.run(report) do
if report.ignore?
Expand All @@ -80,6 +85,15 @@ def notify(exception, auto_notify=false, &block)
return
end

# Test whether severity has been changed and ensure severity_reason is consistant in auto_notify case
if report.severity != initial_severity
report.severity_reason = {
:type => Bugsnag::Report::USER_CALLBACK_SET_SEVERITY
}
else
report.severity_reason = initial_reason
end

# Deliver
configuration.info("Notifying #{configuration.endpoint} of #{report.exceptions.last[:errorClass]}")
payload_string = ::JSON.dump(Bugsnag::Helpers.trim_if_needed(report.as_json))
Expand Down
2 changes: 2 additions & 0 deletions lib/bugsnag/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions lib/bugsnag/integrations/delayed_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
module Delayed
module Plugins
class Bugsnag < Plugin

FRAMEWORK_ATTRIBUTES = {
:framework => "DelayedJob"
}

module Notify
def error(job, error)
overrides = {
Expand Down Expand Up @@ -36,6 +41,10 @@ def error(job, error)

::Bugsnag.notify(error, true) do |report|
report.severity = "error"
report.severity_reason = {
:type => ::Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
:attributes => FRAMEWORK_ATTRIBUTES
}
report.meta_data.merge! overrides
end

Expand Down
9 changes: 9 additions & 0 deletions lib/bugsnag/integrations/mailman.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -15,6 +20,10 @@ def call(mail)
raise ex if [Interrupt, SystemExit, SignalException].include? ex.class
Bugsnag.notify(ex, true) do |report|
report.severity = "error"
report.severity_reason = {
:type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
:attributes => FRAMEWORK_ATTRIBUTES
}
end
raise
ensure
Expand Down
13 changes: 13 additions & 0 deletions lib/bugsnag/integrations/rack.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
module Bugsnag
class Rack

FRAMEWORK_ATTRIBUTES = {
:framework => "Rack"
}

def initialize(app)
@app = app

Expand Down Expand Up @@ -35,6 +40,10 @@ def call(env)
# Notify bugsnag of rack exceptions
Bugsnag.notify(raised, true) do |report|
report.severity = "error"
report.severity_reason = {
:type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
:attributes => Bugsnag::Rack::FRAMEWORK_ATTRIBUTES
}
end

# Re-raise the exception
Expand All @@ -45,6 +54,10 @@ def call(env)
if env["rack.exception"]
Bugsnag.notify(env["rack.exception"], true) do |report|
report.severity = "error"
report.severity_reason = {
:type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
:attributes => FRAMEWORK_ATTRIBUTES
}
end
end

Expand Down
7 changes: 7 additions & 0 deletions lib/bugsnag/integrations/rails/active_record_rescue.rb
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -10,6 +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.severity_reason = {
:type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
:attributes => FRAMEWORK_ATTRIBUTES
}
end
raise
end
Expand Down
9 changes: 9 additions & 0 deletions lib/bugsnag/integrations/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -19,6 +24,10 @@ class Railtie < Rails::Railtie
if $!
Bugsnag.notify($!, true) do |report|
report.severity = "error"
report.severity_reason = {
:type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
:attributes => FRAMEWORK_ATTRIBUTES
}
end
end
end
Expand Down
8 changes: 8 additions & 0 deletions lib/bugsnag/integrations/rake.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -14,6 +18,10 @@ def execute_with_bugsnag(args=nil)
rescue Exception => ex
Bugsnag.notify(ex, true) do |report|
report.severity = "error"
report.severity_reason = {
:type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
:attributes => FRAMEWORK_ATTRIBUTES
}
end
raise
ensure
Expand Down
9 changes: 9 additions & 0 deletions lib/bugsnag/integrations/resque.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -28,6 +33,10 @@ def self.add_failure_backend
def save
Bugsnag.notify(exception, true) do |report|
report.severity = "error"
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
Expand Down
9 changes: 9 additions & 0 deletions lib/bugsnag/integrations/sidekiq.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -18,6 +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.severity_reason = {
:type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
:attributes => FRAMEWORK_ATTRIBUTES
}
end
raise
ensure
Expand Down
48 changes: 48 additions & 0 deletions lib/bugsnag/middleware/classify_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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.severity_reason = {
:type => Bugsnag::Report::ERROR_CLASS,
:attributes => {
:errorClass => info_class
}
}
report.severity = 'info'
break
end
end
end

@bugsnag.call(report)
end
end
end

16 changes: 14 additions & 2 deletions lib/bugsnag/report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -25,10 +32,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)
def initialize(exception, passed_configuration, auto_notify=false)
@should_ignore = false
@unhandled = auto_notify

self.configuration = passed_configuration

Expand All @@ -42,7 +51,8 @@ def initialize(exception, passed_configuration)
self.hostname = configuration.hostname
self.meta_data = {}
self.release_stage = configuration.release_stage
self.severity = "warning"
self.severity = auto_notify ? "error" : "warning"
self.severity_reason = auto_notify ? {:type => UNHANDLED_EXCEPTION} : {:type => HANDLED_EXCEPTION}
self.user = {}
end

Expand Down Expand Up @@ -84,6 +94,8 @@ def as_json
groupingHash: grouping_hash,
payloadVersion: CURRENT_PAYLOAD_VERSION,
severity: severity,
severityReason: severity_reason,
unhandled: @unhandled,
user: user
}

Expand Down
4 changes: 3 additions & 1 deletion lib/bugsnag/tasks/bugsnag.rake
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ namespace :bugsnag do
begin
raise RuntimeError.new("Bugsnag test exception")
rescue => e
Bugsnag.notify(e, {:context => "rake#test_exception"})
Bugsnag.notify(e) do |report|
report.context = "rake#test_exception"
end
end
end
end
Expand Down
Loading

0 comments on commit 643ae3a

Please sign in to comment.