Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added unhandled functionality, updated tests and integrations #368

Merged
merged 17 commits into from
Oct 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be declared as attr_reader, since its only being set internally by a method (I think that was the intent of set_handled_states). It would prevent accidental overrides by other code/callback writers.

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