Skip to content

Commit

Permalink
Merge pull request #620 from bugsnag/more-runtime-versions
Browse files Browse the repository at this point in the history
[PLAT-3702] Report runtime versions for Delayed Job, Mailman and Shoryuken
  • Loading branch information
imjoehaines authored Aug 3, 2020
2 parents 717ec5d + b85f5e3 commit fb4adbd
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 47 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ Changelog
* The `BUGSNAG_RELEASE_STAGE` environment variable can now be used to set the release stage. Previously this was only used in Rails applications
| [#613](https://github.com/bugsnag/bugsnag-ruby/pull/613)

* Add support for runtime versions to Delayed Job, Mailman and Shoryuken integrations
| [#620](https://github.com/bugsnag/bugsnag-ruby/pull/620)

## Fixes

* The `app_type` configuration option should no longer be overwritten by Bugsnag and integrations should set this more consistently
Expand Down
3 changes: 3 additions & 0 deletions features/delayed_job.feature
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Scenario: An unhandled RuntimeError sends a report with arguments
And the event "metaData.job.payload.display_name" equals "TestModel.fail_with_args"
And the event "metaData.job.payload.method_name" equals "fail_with_args"
And the payload field "events.0.metaData.job.payload.args.0" equals "Test"
And the event "device.runtimeVersions.delayed_job" equals "4.1.8"

Scenario: A handled exception sends a report
Given I run the service "delayed_job" with the command "bundle exec rake delayed_job_tests:notify_with_args"
Expand All @@ -34,6 +35,7 @@ Scenario: A handled exception sends a report
And the event "metaData.job.payload.display_name" equals "TestModel.notify_with_args"
And the event "metaData.job.payload.method_name" equals "notify_with_args"
And the payload field "events.0.metaData.job.payload.args.0" equals "Test"
And the event "device.runtimeVersions.delayed_job" equals "4.1.8"

Scenario: The report context uses the class name if no display name is available
Given I run the service "delayed_job" with the command "bundle exec rake delayed_job_tests:report_context"
Expand All @@ -51,3 +53,4 @@ Scenario: The report context uses the class name if no display name is available
And the event "metaData.job.max_attempts" equals 1
And the event "metaData.job.payload.display_name" is null
And the event "metaData.job.payload.method_name" is null
And the event "device.runtimeVersions.delayed_job" equals "4.1.8"
12 changes: 12 additions & 0 deletions lib/bugsnag/integrations/delayed_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,23 @@
module Delayed
module Plugins
class Bugsnag < ::Delayed::Plugin
##
# DelayedJob doesn't have an easy way to fetch its version, but we can use
# Gem.loaded_specs to get the version instead
def self.delayed_job_version
::Gem.loaded_specs['delayed_job'].version.to_s
rescue StandardError
# Explicitly return nil to prevent Rubocop complaining of a suppressed exception
nil
end

callbacks do |lifecycle|
lifecycle.around(:invoke_job) do |job, *args, &block|
begin
::Bugsnag.configuration.detected_app_type = 'delayed_job'
::Bugsnag.configuration.runtime_versions['delayed_job'] = delayed_job_version if defined?(::Gem)
::Bugsnag.configuration.set_request_data(:delayed_job, job)

block.call(job, *args)
rescue Exception => exception
::Bugsnag.notify(exception, true) do |report|
Expand Down
1 change: 1 addition & 0 deletions lib/bugsnag/integrations/mailman.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Mailman
def initialize
Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Mailman)
Bugsnag.configuration.detected_app_type = "mailman"
Bugsnag.configuration.runtime_versions["mailman"] = ::Mailman::VERSION
end

##
Expand Down
1 change: 1 addition & 0 deletions lib/bugsnag/integrations/shoryuken.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def initialize
Bugsnag.configure do |config|
config.detected_app_type = "shoryuken"
config.default_delivery_method = :synchronous
config.runtime_versions["shoryuken"] = ::Shoryuken::VERSION
end
end

Expand Down
49 changes: 27 additions & 22 deletions spec/integrations/mailman_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
unless defined?(::Mailman)
@mocked_mailman = true
class Mailman
VERSION = '9.8.7'
end
module Kernel
alias_method :old_require, :require
Expand All @@ -19,38 +20,42 @@ def require(path)
config = double('mailman-config')
allow(Mailman).to receive(:config).and_return(config)
expect(config).to receive(:respond_to?).with(:middleware).and_return(true)
middleware = double('mailman-config-middleware')
middleware = spy('mailman-config-middleware')
expect(config).to receive(:middleware).and_return(middleware)
expect(middleware).to receive(:add).with(any_args)

#Kick off
# Kick off
require './lib/bugsnag/integrations/mailman'

expect(middleware).to have_received(:add).with(Bugsnag::Mailman)
end

it "can be called" do
config = double('config')
allow(Bugsnag).to receive(:configuration).and_return(config)
int_middleware = double('internal_middleware')
expect(config).to receive(:internal_middleware).and_return(int_middleware)
expect(int_middleware).to receive(:use).with(Bugsnag::Middleware::Mailman)
expect(config).to receive(:detected_app_type=).with("mailman")

integration = Bugsnag::Mailman.new

mail = double('mail')
expect(config).to receive(:set_request_data).with(:mailman_msg, mail)
expect(mail).to receive(:to_s).and_return(mail)
allow(config).to receive(:clear_request_data)
# Initialising the middleware should set some config options
expect(Bugsnag.configuration.internal_middleware.last).to eq(Bugsnag::Middleware::Mailman)
expect(Bugsnag.configuration.app_type).to eq('mailman')
expect(Bugsnag.configuration.runtime_versions['mailman']).to eq(Mailman::VERSION)

mail = 'To: My Friend; From: Your Pal; Subject: Hello!'
exception = RuntimeError.new('oops')
report = double('report')
expect(Bugsnag).to receive(:notify).with(exception, true).and_yield(report)
expect(report).to receive(:severity=).with('error')
expect(report).to receive(:severity_reason=).with({
:type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
:attributes => Bugsnag::Mailman::FRAMEWORK_ATTRIBUTES
})
expect{integration.call(mail) {raise exception}}.to raise_error(exception)

expect { integration.call(mail) { raise exception } }.to raise_error(exception)

expect(Bugsnag).to have_sent_notification { |payload, headers|
event = get_event_from_payload(payload)

expect(event['unhandled']).to be(true)
expect(event['severity']).to eq('error')
expect(event['app']['type']).to eq('mailman')
expect(event['device']['runtimeVersions']['mailman']).to eq(Mailman::VERSION)
expect(event['metaData']['mailman']).to eq({ 'message' => mail })

expect(event['severityReason']).to eq({
'type' => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
'attributes' => { 'framework' => 'Mailman' }
})
}
end

after do
Expand Down
55 changes: 30 additions & 25 deletions spec/integrations/shoryuken_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
before do
unless defined?(::Shoryuken)
@mocked_shoryuken = true
class ::Shoryuken
class Shoryuken
VERSION = '1.2.3'
end
module Kernel
alias_method :old_require, :require
Expand All @@ -27,36 +28,40 @@ def require(path)
end

it "calls configure when initialised" do
config = double("config")

expect(config).to receive(:detected_app_type=).with("shoryuken")
expect(config).to receive(:default_delivery_method=).with(:synchronous)
expect(Bugsnag).to receive(:configure).and_yield(config)
Bugsnag::Shoryuken.new

expect(Bugsnag.configuration.app_type).to eq("shoryuken")
expect(Bugsnag.configuration.delivery_method).to eq(:synchronous)
expect(Bugsnag.configuration.runtime_versions["shoryuken"]).to eq(Shoryuken::VERSION)
end

it "calls correct sequence when called" do
queue = 'queue'
body = 'body'
queue = 'a queue name'
body = 'the body of a queued message'
exception = RuntimeError.new('oops')

expect {
Bugsnag::Shoryuken.new.call('_', queue, '_', body) { raise exception }
}.to raise_error(exception)

expect(Bugsnag).to have_sent_notification { |payload, headers|
event = get_event_from_payload(payload)

callbacks = double('callbacks')
expect(callbacks).to receive(:<<) do |func|
report = double('report')
expect(report).to receive(:add_tab).with(:shoryuken, {
queue: queue,
body: body
expect(event['unhandled']).to be(true)
expect(event['severity']).to eq('error')
expect(event['app']['type']).to eq('shoryuken')
expect(event['device']['runtimeVersions']['shoryuken']).to eq(Shoryuken::VERSION)

expect(event['metaData']['shoryuken']).to eq({
'body' => body,
'queue' => queue,
})
func.call(report)
end
config = double('config')
allow(config).to receive(:detected_app_type=).with("shoryuken")
allow(config).to receive(:default_delivery_method=).with(:synchronous)
allow(config).to receive(:clear_request_data)
expect(Bugsnag).to receive(:before_notify_callbacks).and_return(callbacks)
allow(Bugsnag).to receive(:configure).and_yield(config)
allow(Bugsnag).to receive(:configuration).and_return(config)
shoryuken = Bugsnag::Shoryuken.new
expect { |b| shoryuken.call('_', queue, '_', body, &b )}.to yield_control

expect(event['severityReason']).to eq({
'type' => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
'attributes' => { 'framework' => 'Shoryuken' }
})
}
end

after do
Expand Down

0 comments on commit fb4adbd

Please sign in to comment.