From 00950589150111f70b8bb2a81208fe54ab01131d Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 29 Jul 2021 17:41:11 +0100 Subject: [PATCH 1/7] Allow rescue Exception in integrations --- .rubocop.yml | 4 ++++ .rubocop_todo.yml | 10 ---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index f3e7e151e..da9e76eb5 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -7,6 +7,10 @@ AllCops: - 'spec/fixtures/**/*' - 'features/fixtures/**/*' +Lint/RescueException: + Exclude: + - 'lib/bugsnag/integrations/**/*' + # We can't use ".freeze" on our constants in case users are monkey patching # them — this would be a BC break Style/MutableConstant: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index ecec48d88..af75a280a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -229,16 +229,6 @@ Lint/RedundantRequireStatement: - 'lib/bugsnag/delivery/thread_queue.rb' - 'lib/bugsnag/session_tracker.rb' -# Offense count: 7 -Lint/RescueException: - Exclude: - - 'lib/bugsnag/integrations/delayed_job.rb' - - 'lib/bugsnag/integrations/mailman.rb' - - 'lib/bugsnag/integrations/rack.rb' - - 'lib/bugsnag/integrations/rake.rb' - - 'lib/bugsnag/integrations/shoryuken.rb' - - 'lib/bugsnag/integrations/sidekiq.rb' - # Offense count: 1 # Cop supports --auto-correct. Lint/SendWithMixinArgument: From be9f99a4f29acb927f90efe6f94a70a92875d33a Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Mon, 2 Aug 2021 15:30:21 +0100 Subject: [PATCH 2/7] Ignore Configuration class for length metrics --- .rubocop.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index da9e76eb5..cdf3009c1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -11,6 +11,14 @@ Lint/RescueException: Exclude: - 'lib/bugsnag/integrations/**/*' +Metrics/AbcSize: + Exclude: + - 'lib/bugsnag/configuration.rb' + +Metrics/ClassLength: + Exclude: + - 'lib/bugsnag/configuration.rb' + # We can't use ".freeze" on our constants in case users are monkey patching # them — this would be a BC break Style/MutableConstant: From 3d80d3591b8bcbb0820384b21467c36f663f7432 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 29 Jul 2021 16:52:58 +0100 Subject: [PATCH 3/7] Add Active Job support --- lib/bugsnag/integrations/rails/active_job.rb | 102 +++++++++++++++++++ lib/bugsnag/integrations/railtie.rb | 8 ++ lib/bugsnag/middleware/active_job.rb | 18 ++++ spec/middleware/active_job_spec.rb | 38 +++++++ 4 files changed, 166 insertions(+) create mode 100644 lib/bugsnag/integrations/rails/active_job.rb create mode 100644 lib/bugsnag/middleware/active_job.rb create mode 100644 spec/middleware/active_job_spec.rb diff --git a/lib/bugsnag/integrations/rails/active_job.rb b/lib/bugsnag/integrations/rails/active_job.rb new file mode 100644 index 000000000..703abbe52 --- /dev/null +++ b/lib/bugsnag/integrations/rails/active_job.rb @@ -0,0 +1,102 @@ +require 'set' + +module Bugsnag::Rails + module ActiveJob + SEVERITY = 'error' + SEVERITY_REASON = { + type: Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE, + attributes: { framework: 'Active Job' } + } + + EXISTING_INTEGRATIONS = Set[ + 'ActiveJob::QueueAdapters::DelayedJobAdapter', + 'ActiveJob::QueueAdapters::QueAdapter', + 'ActiveJob::QueueAdapters::ResqueAdapter', + 'ActiveJob::QueueAdapters::ShoryukenAdapter', + 'ActiveJob::QueueAdapters::SidekiqAdapter' + ] + + INLINE_ADAPTER = 'ActiveJob::QueueAdapters::InlineAdapter' + + # these methods were added after the first Active Job release so + # may not be present, depending on the Rails version + MAYBE_MISSING_METHODS = [ + :provider_job_id, + :priority, + :executions, + :enqueued_at, + :timezone + ] + + def self.included(base) + base.class_eval do + around_perform do |job, block| + adapter = _bugsnag_get_adapter_name(job) + + # if we have an integration for this queue adapter already then we should + # leave this job alone or we'll end up with duplicate metadata + return block.call if EXISTING_INTEGRATIONS.include?(adapter) + + Bugsnag.configuration.detected_app_type = 'active job' + + begin + Bugsnag.configuration.set_request_data(:active_job, _bugsnag_extract_metadata(job)) + + block.call + rescue Exception => e + Bugsnag.notify(e, true) do |report| + report.severity = SEVERITY + report.severity_reason = SEVERITY_REASON + end + + # when using the "inline" adapter the job is run immediately, which + # will result in our Rack integration catching the re-raised error + # and reporting it a second time if it's run in a web request + if adapter == INLINE_ADAPTER + e.instance_eval do + def skip_bugsnag + true + end + end + end + + raise + ensure + Bugsnag.configuration.clear_request_data + end + end + end + end + + private + + def _bugsnag_get_adapter_name(job) + adapter = job.class.queue_adapter + + # in Rails 4 queue adapters were references to a class. In Rails 5+ + # they are an instance of that class instead + return adapter.name if adapter.is_a?(Class) + + adapter.class.name + end + + def _bugsnag_extract_metadata(job) + metadata = { + job_id: job.job_id, + job_name: job.class.name, + queue: job.queue_name, + arguments: job.arguments, + locale: job.locale + } + + MAYBE_MISSING_METHODS.each do |method_name| + next unless job.respond_to?(method_name) + + metadata[method_name] = job.send(method_name) + end + + metadata.compact! + metadata + end + end +end diff --git a/lib/bugsnag/integrations/railtie.rb b/lib/bugsnag/integrations/railtie.rb index 7a49bce21..660f00165 100644 --- a/lib/bugsnag/integrations/railtie.rb +++ b/lib/bugsnag/integrations/railtie.rb @@ -74,6 +74,14 @@ def event_subscription(event) include Bugsnag::Rails::ActiveRecordRescue end + ActiveSupport.on_load(:active_job) do + require "bugsnag/middleware/active_job" + Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::ActiveJob) + + require "bugsnag/integrations/rails/active_job" + include Bugsnag::Rails::ActiveJob + end + Bugsnag::Rails::DEFAULT_RAILS_BREADCRUMBS.each { |event| event_subscription(event) } # Make sure we don't overwrite the value set by another integration because diff --git a/lib/bugsnag/middleware/active_job.rb b/lib/bugsnag/middleware/active_job.rb new file mode 100644 index 000000000..5ff05ef74 --- /dev/null +++ b/lib/bugsnag/middleware/active_job.rb @@ -0,0 +1,18 @@ +module Bugsnag::Middleware + class ActiveJob + def initialize(bugsnag) + @bugsnag = bugsnag + end + + def call(report) + data = report.request_data[:active_job] + + if data + report.add_tab(:active_job, data) + report.context = "#{data[:job_name]}@#{data[:queue]}" + end + + @bugsnag.call(report) + end + end +end diff --git a/spec/middleware/active_job_spec.rb b/spec/middleware/active_job_spec.rb new file mode 100644 index 000000000..8b49e4b66 --- /dev/null +++ b/spec/middleware/active_job_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' +require 'bugsnag/middleware/active_job' + +describe Bugsnag::Middleware::ActiveJob do + it 'does nothing if there is no active_job request data' do + report = Bugsnag::Report.new(RuntimeError.new, Bugsnag.configuration) + middleware = Bugsnag::Middleware::ActiveJob.new(->(_) {}) + + middleware.call(report) + + expect(report.context).to be_nil + expect(report.meta_data).to eq({}) + end + + it 'attaches active_job request data as metadata and sets the context' do + report = Bugsnag::Report.new(RuntimeError.new, Bugsnag.configuration) + report.request_data[:active_job] = { + abc: 123, + xyz: 456, + job_name: 'ExampleJob', + queue: 'default_queue' + } + + middleware = Bugsnag::Middleware::ActiveJob.new(->(_) {}) + + middleware.call(report) + + expect(report.context).to eq('ExampleJob@default_queue') + expect(report.meta_data).to eq({ + active_job: { + abc: 123, + xyz: 456, + job_name: 'ExampleJob', + queue: 'default_queue' + } + }) + end +end From 05d537947c2f0563fcf963e31a85bd56ddc0760d Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 29 Jul 2021 16:55:01 +0100 Subject: [PATCH 4/7] Add Active Job tests against Rails 4 --- .../app/controllers/active_job_controller.rb | 15 +++++ .../rails4/app/app/jobs/notify_job.rb | 4 +- .../rails4/app/app/jobs/unhandled_job.rb | 17 +++++ features/fixtures/rails4/app/config/routes.rb | 1 + features/rails_features/active_job.feature | 62 +++++++++++++++++++ 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 features/fixtures/rails4/app/app/controllers/active_job_controller.rb create mode 100644 features/fixtures/rails4/app/app/jobs/unhandled_job.rb create mode 100644 features/rails_features/active_job.feature diff --git a/features/fixtures/rails4/app/app/controllers/active_job_controller.rb b/features/fixtures/rails4/app/app/controllers/active_job_controller.rb new file mode 100644 index 000000000..7a62ac467 --- /dev/null +++ b/features/fixtures/rails4/app/app/controllers/active_job_controller.rb @@ -0,0 +1,15 @@ +class ActiveJobController < ActionController::Base + protect_from_forgery + + def handled + NotifyJob.perform_later(1, "hello", { a: "a", b: "b" }, keyword: true) + + render json: {} + end + + def unhandled + UnhandledJob.perform_later(123, { abc: "xyz" }, "abcxyz") + + render json: {} + end +end diff --git a/features/fixtures/rails4/app/app/jobs/notify_job.rb b/features/fixtures/rails4/app/app/jobs/notify_job.rb index 56c5dfcbc..285934618 100644 --- a/features/fixtures/rails4/app/app/jobs/notify_job.rb +++ b/features/fixtures/rails4/app/app/jobs/notify_job.rb @@ -1,5 +1,5 @@ class NotifyJob < ApplicationJob - def perform + def perform(*args, **kwargs) Bugsnag.notify("Failed") end -end \ No newline at end of file +end diff --git a/features/fixtures/rails4/app/app/jobs/unhandled_job.rb b/features/fixtures/rails4/app/app/jobs/unhandled_job.rb new file mode 100644 index 000000000..37f132aa1 --- /dev/null +++ b/features/fixtures/rails4/app/app/jobs/unhandled_job.rb @@ -0,0 +1,17 @@ +# Rails 4 doesn't automatically retry jobs, so this is a quick hack to allow us +# to run the same Rails 5/6 tests against Rails 4 by allowing one retry +$attempts = 0 + +class UnhandledJob < ApplicationJob + rescue_from(RuntimeError) do |exception| + raise exception if $attempts >= 2 + + retry_job + end + + def perform(*args, **kwargs) + $attempts += 1 + + raise 'Oh no!' + end +end diff --git a/features/fixtures/rails4/app/config/routes.rb b/features/fixtures/rails4/app/config/routes.rb index d289c55f1..8fee25c36 100644 --- a/features/fixtures/rails4/app/config/routes.rb +++ b/features/fixtures/rails4/app/config/routes.rb @@ -18,4 +18,5 @@ get "/devise/(:action)", controller: 'devise' get "/breadcrumbs/(:action)", controller: 'breadcrumbs' get "/mongo/(:action)", controller: 'mongo' + get "/active_job/(:action)", controller: 'active_job' end diff --git a/features/rails_features/active_job.feature b/features/rails_features/active_job.feature new file mode 100644 index 000000000..677d31536 --- /dev/null +++ b/features/rails_features/active_job.feature @@ -0,0 +1,62 @@ +Feature: Active Job + +@rails4 +Scenario: A handled error will be delivered + Given I start the rails service + When I navigate to the route "/active_job/handled" on the rails app + And I wait to receive a request + Then the request is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" + And the event "unhandled" is false + And the event "severity" equals "warning" + And the event "context" equals "NotifyJob@default" + And the event "app.type" equals "active job" + And the exception "errorClass" equals "RuntimeError" + And the exception "message" equals "Failed" + And the event "metaData.active_job.job_id" matches "^[0-9a-f-]{36}$" + And the event "metaData.active_job.job_name" equals "NotifyJob" + And the event "metaData.active_job.queue" equals "default" + And the event "metaData.active_job.locale" equals "en" + And the event "metaData.active_job.arguments.0" equals 1 + And the event "metaData.active_job.arguments.1" equals "hello" + And the event "metaData.active_job.arguments.2.a" equals "a" + And the event "metaData.active_job.arguments.2.b" equals "b" + And the event "metaData.active_job.arguments.3.keyword" is true + +@rails4 +Scenario: An unhandled error will be delivered + Given I start the rails service + When I navigate to the route "/active_job/unhandled" on the rails app + And I wait to receive 2 requests + Then the request is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" + And the event "unhandled" is true + And the event "severity" equals "error" + And the event "context" equals "UnhandledJob@default" + And the event "app.type" equals "active job" + And the event "severityReason.type" equals "unhandledExceptionMiddleware" + And the event "severityReason.attributes.framework" equals "Active Job" + And the exception "errorClass" equals "RuntimeError" + And the exception "message" equals "Oh no!" + And the event "metaData.active_job.job_id" matches "^[0-9a-f-]{36}$" + And the event "metaData.active_job.job_name" equals "UnhandledJob" + And the event "metaData.active_job.queue" equals "default" + And the event "metaData.active_job.locale" equals "en" + And the event "metaData.active_job.arguments.0" equals 123 + And the event "metaData.active_job.arguments.1.abc" equals "xyz" + And the event "metaData.active_job.arguments.2" equals "abcxyz" + When I discard the oldest request + Then the request is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" + And the event "unhandled" is true + And the event "severity" equals "error" + And the event "context" equals "UnhandledJob@default" + And the event "app.type" equals "active job" + And the event "severityReason.type" equals "unhandledExceptionMiddleware" + And the event "severityReason.attributes.framework" equals "Active Job" + And the exception "errorClass" equals "RuntimeError" + And the exception "message" equals "Oh no!" + And the event "metaData.active_job.job_id" matches "^[0-9a-f-]{36}$" + And the event "metaData.active_job.job_name" equals "UnhandledJob" + And the event "metaData.active_job.queue" equals "default" + And the event "metaData.active_job.locale" equals "en" + And the event "metaData.active_job.arguments.0" equals 123 + And the event "metaData.active_job.arguments.1.abc" equals "xyz" + And the event "metaData.active_job.arguments.2" equals "abcxyz" From 9af7161e3d09e22a3e58462bcce521a4ea7755e2 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 29 Jul 2021 17:21:57 +0100 Subject: [PATCH 5/7] Add Active Job tests against Rails 5 --- .../app/controllers/active_job_controller.rb | 15 ++++++ .../rails5/app/app/jobs/notify_job.rb | 4 +- .../rails5/app/app/jobs/unhandled_job.rb | 7 +++ features/fixtures/rails5/app/config/routes.rb | 3 ++ features/rails_features/active_job.feature | 10 +++- features/steps/ruby_notifier_steps.rb | 54 +++++++++++++++++++ 6 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 features/fixtures/rails5/app/app/controllers/active_job_controller.rb create mode 100644 features/fixtures/rails5/app/app/jobs/unhandled_job.rb diff --git a/features/fixtures/rails5/app/app/controllers/active_job_controller.rb b/features/fixtures/rails5/app/app/controllers/active_job_controller.rb new file mode 100644 index 000000000..7a62ac467 --- /dev/null +++ b/features/fixtures/rails5/app/app/controllers/active_job_controller.rb @@ -0,0 +1,15 @@ +class ActiveJobController < ActionController::Base + protect_from_forgery + + def handled + NotifyJob.perform_later(1, "hello", { a: "a", b: "b" }, keyword: true) + + render json: {} + end + + def unhandled + UnhandledJob.perform_later(123, { abc: "xyz" }, "abcxyz") + + render json: {} + end +end diff --git a/features/fixtures/rails5/app/app/jobs/notify_job.rb b/features/fixtures/rails5/app/app/jobs/notify_job.rb index 56c5dfcbc..285934618 100644 --- a/features/fixtures/rails5/app/app/jobs/notify_job.rb +++ b/features/fixtures/rails5/app/app/jobs/notify_job.rb @@ -1,5 +1,5 @@ class NotifyJob < ApplicationJob - def perform + def perform(*args, **kwargs) Bugsnag.notify("Failed") end -end \ No newline at end of file +end diff --git a/features/fixtures/rails5/app/app/jobs/unhandled_job.rb b/features/fixtures/rails5/app/app/jobs/unhandled_job.rb new file mode 100644 index 000000000..c14ddd4c2 --- /dev/null +++ b/features/fixtures/rails5/app/app/jobs/unhandled_job.rb @@ -0,0 +1,7 @@ +class UnhandledJob < ApplicationJob + retry_on RuntimeError, wait: 1.second, attempts: 2 + + def perform(*args, **kwargs) + raise 'Oh no!' + end +end diff --git a/features/fixtures/rails5/app/config/routes.rb b/features/fixtures/rails5/app/config/routes.rb index d5e6396d0..ab29f93d0 100644 --- a/features/fixtures/rails5/app/config/routes.rb +++ b/features/fixtures/rails5/app/config/routes.rb @@ -61,4 +61,7 @@ get 'mongo/success_crash', to: 'mongo#success_crash' get 'mongo/get_crash', to: 'mongo#get_crash' get 'mongo/failure_crash', to: 'mongo#failure_crash' + + get 'active_job/handled', to: 'active_job#handled' + get 'active_job/unhandled', to: 'active_job#unhandled' end diff --git a/features/rails_features/active_job.feature b/features/rails_features/active_job.feature index 677d31536..2afa8ce28 100644 --- a/features/rails_features/active_job.feature +++ b/features/rails_features/active_job.feature @@ -1,6 +1,6 @@ Feature: Active Job -@rails4 +@rails4 @rails5 Scenario: A handled error will be delivered Given I start the rails service When I navigate to the route "/active_job/handled" on the rails app @@ -21,8 +21,10 @@ Scenario: A handled error will be delivered And the event "metaData.active_job.arguments.2.a" equals "a" And the event "metaData.active_job.arguments.2.b" equals "b" And the event "metaData.active_job.arguments.3.keyword" is true + And in Rails versions ">=" 5 the event "metaData.active_job.provider_job_id" matches "^[0-9a-f-]{36}$" + And in Rails versions ">=" 5 the event "metaData.active_job.executions" equals 1 -@rails4 +@rails4 @rails5 Scenario: An unhandled error will be delivered Given I start the rails service When I navigate to the route "/active_job/unhandled" on the rails app @@ -43,6 +45,8 @@ Scenario: An unhandled error will be delivered And the event "metaData.active_job.arguments.0" equals 123 And the event "metaData.active_job.arguments.1.abc" equals "xyz" And the event "metaData.active_job.arguments.2" equals "abcxyz" + And in Rails versions ">=" 5 the event "metaData.active_job.provider_job_id" matches "^[0-9a-f-]{36}$" + And in Rails versions ">=" 5 the event "metaData.active_job.executions" equals 1 When I discard the oldest request Then the request is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" And the event "unhandled" is true @@ -60,3 +64,5 @@ Scenario: An unhandled error will be delivered And the event "metaData.active_job.arguments.0" equals 123 And the event "metaData.active_job.arguments.1.abc" equals "xyz" And the event "metaData.active_job.arguments.2" equals "abcxyz" + And in Rails versions ">=" 5 the event "metaData.active_job.provider_job_id" matches "^[0-9a-f-]{36}$" + And in Rails versions ">=" 5 the event "metaData.active_job.executions" equals 2 diff --git a/features/steps/ruby_notifier_steps.rb b/features/steps/ruby_notifier_steps.rb index c8a9f7bb0..0eec1697f 100644 --- a/features/steps/ruby_notifier_steps.rb +++ b/features/steps/ruby_notifier_steps.rb @@ -108,3 +108,57 @@ And the payload field "#{field}" matches the JSON fixture in "features/fixtures/sidekiq/payloads/unhandled_metadata_ca_#{created_at_present}.json" } end + +def rails_version_matches?(operator, version_to_compare) + # send the given operator as a method to the current rails version + # this will evaluate to e.g. '6.send(">=", 5)', which is the same as '6 >= 5' + ENV["RAILS_VERSION"].to_i.send(operator, version_to_compare) +end + +Then("in Rails versions {string} {int} the event {string} equals {string}") do |operator, version, path, expected| + if rails_version_matches?(operator, version) + steps %Q{ + And the event "#{path}" equals "#{expected}" + } + else + steps %Q{ + And the event "#{path}" is null + } + end +end + +Then("in Rails versions {string} {int} the event {string} equals {int}") do |operator, version, path, expected| + if rails_version_matches?(operator, version) + steps %Q{ + And the event "#{path}" equals #{expected} + } + else + steps %Q{ + And the event "#{path}" is null + } + end +end + +Then("in Rails versions {string} {int} the event {string} matches {string}") do |operator, version, path, expected| + if rails_version_matches?(operator, version) + steps %Q{ + And the event "#{path}" matches "#{expected}" + } + else + steps %Q{ + And the event "#{path}" is null + } + end +end + +Then("in Rails versions {string} {int} the event {string} is a timestamp") do |operator, version, path| + if rails_version_matches?(operator, version) + steps %Q{ + And the event "#{path}" is a timestamp + } + else + steps %Q{ + And the event "#{path}" is null + } + end +end From db44351e7bde3c014ccde57cc3fc75a1a432b484 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Thu, 29 Jul 2021 17:40:40 +0100 Subject: [PATCH 6/7] Add Active Job tests against Rails 6 --- .../app/app/controllers/active_job_controller.rb | 15 +++++++++++++++ .../fixtures/rails6/app/app/jobs/notify_job.rb | 4 ++-- .../fixtures/rails6/app/app/jobs/unhandled_job.rb | 7 +++++++ features/fixtures/rails6/app/config/routes.rb | 3 +++ features/rails_features/active_job.feature | 10 ++++++++-- 5 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 features/fixtures/rails6/app/app/controllers/active_job_controller.rb create mode 100644 features/fixtures/rails6/app/app/jobs/unhandled_job.rb diff --git a/features/fixtures/rails6/app/app/controllers/active_job_controller.rb b/features/fixtures/rails6/app/app/controllers/active_job_controller.rb new file mode 100644 index 000000000..7a62ac467 --- /dev/null +++ b/features/fixtures/rails6/app/app/controllers/active_job_controller.rb @@ -0,0 +1,15 @@ +class ActiveJobController < ActionController::Base + protect_from_forgery + + def handled + NotifyJob.perform_later(1, "hello", { a: "a", b: "b" }, keyword: true) + + render json: {} + end + + def unhandled + UnhandledJob.perform_later(123, { abc: "xyz" }, "abcxyz") + + render json: {} + end +end diff --git a/features/fixtures/rails6/app/app/jobs/notify_job.rb b/features/fixtures/rails6/app/app/jobs/notify_job.rb index 56c5dfcbc..285934618 100644 --- a/features/fixtures/rails6/app/app/jobs/notify_job.rb +++ b/features/fixtures/rails6/app/app/jobs/notify_job.rb @@ -1,5 +1,5 @@ class NotifyJob < ApplicationJob - def perform + def perform(*args, **kwargs) Bugsnag.notify("Failed") end -end \ No newline at end of file +end diff --git a/features/fixtures/rails6/app/app/jobs/unhandled_job.rb b/features/fixtures/rails6/app/app/jobs/unhandled_job.rb new file mode 100644 index 000000000..c14ddd4c2 --- /dev/null +++ b/features/fixtures/rails6/app/app/jobs/unhandled_job.rb @@ -0,0 +1,7 @@ +class UnhandledJob < ApplicationJob + retry_on RuntimeError, wait: 1.second, attempts: 2 + + def perform(*args, **kwargs) + raise 'Oh no!' + end +end diff --git a/features/fixtures/rails6/app/config/routes.rb b/features/fixtures/rails6/app/config/routes.rb index d5e6396d0..ab29f93d0 100644 --- a/features/fixtures/rails6/app/config/routes.rb +++ b/features/fixtures/rails6/app/config/routes.rb @@ -61,4 +61,7 @@ get 'mongo/success_crash', to: 'mongo#success_crash' get 'mongo/get_crash', to: 'mongo#get_crash' get 'mongo/failure_crash', to: 'mongo#failure_crash' + + get 'active_job/handled', to: 'active_job#handled' + get 'active_job/unhandled', to: 'active_job#unhandled' end diff --git a/features/rails_features/active_job.feature b/features/rails_features/active_job.feature index 2afa8ce28..ce9bf8f32 100644 --- a/features/rails_features/active_job.feature +++ b/features/rails_features/active_job.feature @@ -1,6 +1,6 @@ Feature: Active Job -@rails4 @rails5 +@rails4 @rails5 @rails6 Scenario: A handled error will be delivered Given I start the rails service When I navigate to the route "/active_job/handled" on the rails app @@ -23,8 +23,10 @@ Scenario: A handled error will be delivered And the event "metaData.active_job.arguments.3.keyword" is true And in Rails versions ">=" 5 the event "metaData.active_job.provider_job_id" matches "^[0-9a-f-]{36}$" And in Rails versions ">=" 5 the event "metaData.active_job.executions" equals 1 + And in Rails versions ">=" 6 the event "metaData.active_job.timezone" equals "UTC" + And in Rails versions ">=" 6 the event "metaData.active_job.enqueued_at" is a timestamp -@rails4 @rails5 +@rails4 @rails5 @rails6 Scenario: An unhandled error will be delivered Given I start the rails service When I navigate to the route "/active_job/unhandled" on the rails app @@ -47,6 +49,8 @@ Scenario: An unhandled error will be delivered And the event "metaData.active_job.arguments.2" equals "abcxyz" And in Rails versions ">=" 5 the event "metaData.active_job.provider_job_id" matches "^[0-9a-f-]{36}$" And in Rails versions ">=" 5 the event "metaData.active_job.executions" equals 1 + And in Rails versions ">=" 6 the event "metaData.active_job.timezone" equals "UTC" + And in Rails versions ">=" 6 the event "metaData.active_job.enqueued_at" is a timestamp When I discard the oldest request Then the request is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" And the event "unhandled" is true @@ -66,3 +70,5 @@ Scenario: An unhandled error will be delivered And the event "metaData.active_job.arguments.2" equals "abcxyz" And in Rails versions ">=" 5 the event "metaData.active_job.provider_job_id" matches "^[0-9a-f-]{36}$" And in Rails versions ">=" 5 the event "metaData.active_job.executions" equals 2 + And in Rails versions ">=" 6 the event "metaData.active_job.timezone" equals "UTC" + And in Rails versions ">=" 6 the event "metaData.active_job.enqueued_at" is a timestamp From a8dcd3a805479ed418bd8404288a8f817ff83d2a Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Tue, 3 Aug 2021 15:12:45 +0100 Subject: [PATCH 7/7] Update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d78c86cd..269fb600f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +## TBD + +### Enhancements + +* Add support for tracking exceptions and capturing metadata in Active Job, when not using an existing integration + | [#670](https://github.com/bugsnag/bugsnag-ruby/pull/670) + ## v6.21.0 (23 June 2021) ### Enhancements