Skip to content

Commit 6146708

Browse files
st0012jeschneiderhanmrexox
authored
Version 4.2.0 (#1232)
* Add ThreadsInterface (#1178) * Add ThreadsInterface * Update changelog * Inspect exception cause by default & don't exclude ActiveJob::DeserializationError (#1180) * Turn on inspect_exception_causes_for_exclusion by default With this config turned on, we can avoid matching the surface exceptions in integrations, which could cause issues like #1071. Solves #642. * Remove ActiveJob::DeserializationError from ignored list Since the previous commit solves #642, this commit can remove ActiveJob::DeserializationError from the ignored exceptions list. Solves #1071. * Update async document * Update changelog * Make sentry-rails a Rails engine and provide default job class for async (#1181) * Make sentry-rails a Rails engine too * Add Sentry::SendEventJob Instead of letting users defining their SentryJob class, we should provide a default job class for them. * Update document and example for the new job class * Update changelog * Add configuration option for trusted proxies (#1126) * Add configuration option for trusted proxies * Add `trusted_proxies` configuration option to sentry-ruby * Add existing ActionDispatch `trusted_proxies` values * Address some PR feedback * Isolate trusted proxy test configuration * Add comments to explain why we reverse the forwarded_for ip list * Call `uniq` on the trusted proxy list * Rename `filter_local_addresses(ips) to filter_trusted_proxy_addresses(ips) * Remove duplicated hash entry * Update some tests after PR feedback * retrigger checks Co-authored-by: Stan Lo <stan001212@gmail.com> * Only define SendEventJob when ActiveJob is defined * Allow users to configure ActiveJob adapters to ignore (#1256) * Allow users to configure ActiveJob adapters to ignore * Update changelog * Add sidekiq adapter to sentry-rails' ignored adapters list (#1257) * Add sidekiq adapter to sentry-rails' ignored adapters list * Update changelog * Tag queue name and jid on sidekiq events (#1258) * Add queue name and jid to event tags * Update changelog * Tag job_id and provider_job_id on ActiveJob events (#1259) * Refactor/test ActiveJob's context data * Tag job_id and provider_job_id on ActiveJob events * Update changelog * Add ability to have many post initialization callbacks (#1261) * Add ability to have many post initialization callbacks * Revert version bumping, fix codestyle and rewrite rspec test * Remove dependenciy bumping from sentry-sidekiq * Add entries to CHANGELOG * Support config.before_breadcrumb (#1253) * Support config.before_breadcrumb Example: ``` config.before_breadcrumb = lambda do |breadcrumb, hint| breadcrumb.message = "foo" breadcrumb end ``` * Update changelog * Update sentry-ruby's changelog * Update sentry-rails' changelog * Update sentry-sidekiq's changelog * Rename ignored_active_job_adapters to skippable_job_adapters (#1264) * Update sentry-ruby's changelog Co-authored-by: Jon-Erik Schneiderhan <45184220+jeschneiderhan@users.noreply.github.com> Co-authored-by: Valentine Kiselev <mrexox@outlook.com>
1 parent 32d0992 commit 6146708

File tree

34 files changed

+586
-53
lines changed

34 files changed

+586
-53
lines changed

sentry-rails/CHANGELOG.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,42 @@
11
# Changelog
22

3+
## Unreleased (4.2.0)
4+
5+
### Features
6+
7+
- Make sentry-rails a Rails engine and provide default job class for async [#1181](https://github.com/getsentry/sentry-ruby/pull/1181)
8+
9+
`sentry-rails` now provides a default ActiveJob class for sending events asynchronously. You can use it directly without define your own one:
10+
11+
```ruby
12+
config.async = lambda { |event, hint| Sentry::SendEventJob.perform_later(event, hint) }
13+
```
14+
15+
- Add configuration option for trusted proxies [#1126](https://github.com/getsentry/sentry-ruby/pull/1126)
16+
17+
`sentry-rails` now injects `Rails.application.config.action_dispatch.trusted_proxies` into `Sentry.configuration.trusted_proxies` automatically.
18+
19+
- Allow users to configure ActiveJob adapters to ignore [#1256](https://github.com/getsentry/sentry-ruby/pull/1256)
20+
21+
```ruby
22+
# sentry-rails will skip active_job reporting for jobs that use ActiveJob::QueueAdapters::SidekiqAdapter
23+
# you should use this option when:
24+
# - you don't want to see events from a certain adapter
25+
# - you already have a better reporting setup for the adapter (like having `sentry-sidekiq` installed)
26+
config.rails.skippable_job_adapters = ["ActiveJob::QueueAdapters::SidekiqAdapter"]
27+
```
28+
29+
- Tag `job_id` and `provider_job_id` on ActiveJob events [#1259](https://github.com/getsentry/sentry-ruby/pull/1259)
30+
31+
<img width="1330" alt="example of tagged event" src="https://user-images.githubusercontent.com/5079556/106389781-3a03f100-6420-11eb-810c-a99869eb26dd.png">
32+
33+
- Use another method for post initialization callback [#1261](https://github.com/getsentry/sentry-ruby/pull/1261)
34+
35+
### Bug Fixes
36+
37+
- Inspect exception cause by default & don't exclude ActiveJob::DeserializationError [#1180](https://github.com/getsentry/sentry-ruby/pull/1180)
38+
- Fixes [#1071](https://github.com/getsentry/sentry-ruby/issues/1071)
39+
340
## 4.1.7
441

542
- Use env to carry original transaction name [#1255](https://github.com/getsentry/sentry-ruby/pull/1255)
@@ -75,4 +112,3 @@ Release test
75112
## 0.1.0
76113

77114
First version
78-
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
return unless defined?(ActiveJob)
2+
3+
module Sentry
4+
parent_job =
5+
if defined?(ApplicationJob)
6+
ApplicationJob
7+
else
8+
ActiveJob::Base
9+
end
10+
11+
class SendEventJob < parent_job
12+
self.log_arguments = false if ::Rails.version.to_f >= 6.1
13+
discard_on ActiveJob::DeserializationError # this will prevent infinite loop when there's an issue deserializing SentryJob
14+
15+
def perform(event, hint = {})
16+
Sentry.send_event(event, hint)
17+
end
18+
end
19+
end
20+

sentry-rails/examples/rails-6.0/app/jobs/error_job.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
class ErrorJob < ApplicationJob
2+
self.queue_adapter = :async
3+
24
def perform
35
raise "Job failed"
46
end

sentry-rails/examples/rails-6.0/config/initializers/sentry.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@
44
config.traces_sample_rate = 1.0 # set a float between 0.0 and 1.0 to enable performance monitoring
55
config.dsn = 'https://2fb45f003d054a7ea47feb45898f7649@o447951.ingest.sentry.io/5434472'
66
config.release = `git branch --show-current`
7+
config.async = lambda do |event, hint|
8+
Sentry::SendEventJob.perform_later(event, hint)
9+
end
710
end

sentry-rails/lib/sentry/rails.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require "sentry-ruby"
22
require "sentry/integrable"
33
require "sentry/rails/configuration"
4+
require "sentry/rails/engine"
45
require "sentry/rails/railtie"
56
require "sentry/rails/tracing"
67

sentry-rails/lib/sentry/rails/active_job.rb

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
module Sentry
22
module Rails
33
module ActiveJobExtensions
4-
ALREADY_SUPPORTED_SENTRY_ADAPTERS = %w(
5-
ActiveJob::QueueAdapters::SidekiqAdapter
6-
).freeze
7-
84
def self.included(base)
95
base.class_eval do
106
around_perform do |job, block|
@@ -29,28 +25,30 @@ def capture_and_reraise_with_sentry(job, block)
2925
rescue_handler_result = rescue_with_handler(e)
3026
return rescue_handler_result if rescue_handler_result
3127

32-
Sentry::Rails.capture_exception(e, extra: sentry_context(job))
28+
Sentry::Rails.capture_exception(
29+
e,
30+
extra: sentry_context(job),
31+
tags: {
32+
job_id: job.job_id,
33+
provider_job_id: job.provider_job_id
34+
}
35+
)
3336
raise e
3437
end
3538

3639
def already_supported_by_specific_integration?(job)
37-
ALREADY_SUPPORTED_SENTRY_ADAPTERS.include?(job.class.queue_adapter.class.to_s)
40+
Sentry.configuration.rails.skippable_job_adapters.include?(job.class.queue_adapter.class.to_s)
3841
end
3942

4043
def sentry_context(job)
41-
ctx = {
42-
:active_job => job.class.name,
43-
:arguments => job.arguments,
44-
:scheduled_at => job.scheduled_at,
45-
:job_id => job.job_id,
46-
:locale => job.locale
44+
{
45+
active_job: job.class.name,
46+
arguments: job.arguments,
47+
scheduled_at: job.scheduled_at,
48+
job_id: job.job_id,
49+
provider_job_id: job.provider_job_id,
50+
locale: job.locale
4751
}
48-
# Add provider_job_id details if Rails 5
49-
if job.respond_to?(:provider_job_id)
50-
ctx[:provider_job_id] = job.provider_job_id
51-
end
52-
53-
ctx
5452
end
5553
end
5654
end

sentry-rails/lib/sentry/rails/configuration.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module Sentry
22
class Configuration
33
attr_reader :rails
44

5-
def post_initialization_callback
5+
add_post_initialization_callback do
66
@rails = Sentry::Rails::Configuration.new
77
@excluded_exceptions = @excluded_exceptions.concat(Sentry::Rails::IGNORE_DEFAULT)
88
end
@@ -23,7 +23,6 @@ module Rails
2323
'ActionDispatch::Http::MimeNegotiation::InvalidType',
2424
'ActionController::UnknownHttpMethod',
2525
'ActionDispatch::Http::Parameters::ParseError',
26-
'ActiveJob::DeserializationError', # Can cause infinite loops
2726
'ActiveRecord::RecordNotFound'
2827
].freeze
2928
class Configuration
@@ -33,8 +32,14 @@ class Configuration
3332
# will report exceptions even when they are rescued by these middlewares.
3433
attr_accessor :report_rescued_exceptions
3534

35+
# Some adapters, like sidekiq, already have their own sentry integration.
36+
# In those cases, we should skip ActiveJob's reporting to avoid duplicated reports.
37+
attr_accessor :skippable_job_adapters
38+
3639
def initialize
3740
@report_rescued_exceptions = true
41+
# TODO: Remove this in 4.2.0
42+
@skippable_job_adapters = []
3843
end
3944
end
4045
end
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Sentry
2+
class Engine < ::Rails::Engine
3+
isolate_namespace Sentry
4+
end
5+
end

sentry-rails/lib/sentry/rails/railtie.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class Railtie < ::Rails::Railtie
2222

2323
configure_project_root
2424
configure_sentry_logger
25+
configure_trusted_proxies
2526
extend_controller_methods
2627
extend_active_job if defined?(ActiveJob)
2728
override_streaming_reporter
@@ -39,6 +40,10 @@ def configure_sentry_logger
3940
Sentry.configuration.logger = ::Rails.logger
4041
end
4142

43+
def configure_trusted_proxies
44+
Sentry.configuration.trusted_proxies += Array(::Rails.application.config.action_dispatch.trusted_proxies)
45+
end
46+
4247
def extend_active_job
4348
require "sentry/rails/active_job"
4449
ActiveJob::Base.send(:prepend, Sentry::Rails::ActiveJobExtensions)

sentry-rails/spec/sentry/rails/activejob_spec.rb

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def rescue_callback(error); end
3838
end
3939

4040
RSpec.describe "ActiveJob integration" do
41-
before(:all) do
41+
before(:each) do
4242
make_basic_app
4343
end
4444

@@ -58,6 +58,21 @@ def rescue_callback(error); end
5858
MyActiveJob.queue_adapter = :inline
5959
end
6060

61+
it "adds useful context to extra" do
62+
job = FailedJob.new
63+
64+
expect { job.perform_now }.to raise_error(FailedJob::TestError)
65+
66+
event = transport.events.last.to_json_compatible
67+
expect(event.dig("extra", "active_job")).to eq("FailedJob")
68+
expect(event.dig("extra", "job_id")).to be_a(String)
69+
expect(event.dig("extra", "provider_job_id")).to be_nil
70+
expect(event.dig("extra", "arguments")).to eq([])
71+
72+
expect(event.dig("tags", "job_id")).to eq(event.dig("extra", "job_id"))
73+
expect(event.dig("tags", "provider_job_id")).to eq(event.dig("extra", "provider_job_id"))
74+
end
75+
6176
it "clears context" do
6277
job = MyActiveJob.new
6378

@@ -70,6 +85,75 @@ def rescue_callback(error); end
7085
expect(Sentry.get_current_scope.extra).to eq({})
7186
end
7287

88+
context "when DeserializationError happens in user's jobs" do
89+
before do
90+
make_basic_app
91+
end
92+
93+
class DeserializationErrorJob < ActiveJob::Base
94+
def perform
95+
1/0
96+
rescue
97+
raise ActiveJob::DeserializationError
98+
end
99+
end
100+
101+
it "reports the root cause to Sentry" do
102+
expect do
103+
DeserializationErrorJob.perform_now
104+
end.to raise_error(ActiveJob::DeserializationError, /divided by 0/)
105+
106+
expect(transport.events.size).to eq(1)
107+
108+
event = transport.events.last.to_json_compatible
109+
expect(event.dig("exception", "values", 0, "type")).to eq("ZeroDivisionError")
110+
end
111+
112+
context "and in user-defined reporting job too" do
113+
before do
114+
Sentry.configuration.async = lambda do |event, hint|
115+
UserDefinedReportingJob.perform_now(event, hint)
116+
end
117+
end
118+
119+
class UserDefinedReportingJob < ActiveJob::Base
120+
def perform(event, hint)
121+
Post.find(0)
122+
rescue
123+
raise ActiveJob::DeserializationError
124+
end
125+
end
126+
127+
it "doesn't cause infinite loop because of excluded exceptions" do
128+
expect do
129+
DeserializationErrorJob.perform_now
130+
end.to raise_error(ActiveJob::DeserializationError, /divided by 0/)
131+
end
132+
end
133+
134+
context "and in customized SentryJob too" do
135+
before do
136+
class CustomSentryJob < ::Sentry::SendEventJob
137+
def perform(event, hint)
138+
raise "Not excluded exception"
139+
rescue
140+
raise ActiveJob::DeserializationError
141+
end
142+
end
143+
144+
Sentry.configuration.async = lambda do |event, hint|
145+
CustomSentryJob.perform_now(event, hint)
146+
end
147+
end
148+
149+
it "doesn't cause infinite loop" do
150+
expect do
151+
DeserializationErrorJob.perform_now
152+
end.to raise_error(ActiveJob::DeserializationError, /divided by 0/)
153+
end
154+
end
155+
end
156+
73157
context 'using rescue_from' do
74158
it 'does not trigger Sentry' do
75159
job = RescuedActiveJob.new
@@ -83,6 +167,9 @@ def rescue_callback(error); end
83167
end
84168

85169
context "when we are using an adapter which has a specific integration" do
170+
before do
171+
Sentry.configuration.rails.skippable_job_adapters = ["ActiveJob::QueueAdapters::SidekiqAdapter"]
172+
end
86173
it "does not trigger sentry and re-raises" do
87174
MyActiveJob.queue_adapter = :sidekiq
88175
job = MyActiveJob.new

0 commit comments

Comments
 (0)