Skip to content

Commit

Permalink
Use mechanism to track crashed sessions for unhandled exceptions (#2281)
Browse files Browse the repository at this point in the history
  • Loading branch information
sl0thentr0py committed Apr 2, 2024
1 parent 5eb6f42 commit 4e46fc1
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@
### Features

- Add `Mechanism` interface and default to unhandled for integration exceptions [#2280](https://github.com/getsentry/sentry-ruby/pull/2280)
- Use `mechanism` to track crashed sessions for unhandled exceptions [#2281](https://github.com/getsentry/sentry-ruby/pull/2281)

### Bug Fixes

Expand Down
2 changes: 1 addition & 1 deletion sentry-ruby/lib/sentry/hub.rb
Expand Up @@ -134,7 +134,7 @@ def capture_exception(exception, **options, &block)

return unless event

current_scope.session&.update_from_exception(event.exception)
current_scope.session&.update_from_error_event(event)

capture_event(event, **options, &block).tap do
# mark the exception as captured so we can use this information to avoid duplicated capturing
Expand Down
13 changes: 7 additions & 6 deletions sentry-ruby/lib/sentry/session.rb
Expand Up @@ -4,9 +4,8 @@ module Sentry
class Session
attr_reader :started, :status, :aggregation_key

# TODO-neel add :crashed after adding handled mechanism
STATUSES = %i[ok errored exited]
AGGREGATE_STATUSES = %i[errored exited]
STATUSES = %i[ok errored crashed exited]
AGGREGATE_STATUSES = %i[errored crashed exited]

def initialize
@started = Sentry.utc_now
Expand All @@ -17,9 +16,11 @@ def initialize
@aggregation_key = Time.utc(@started.year, @started.month, @started.day, @started.hour, @started.min)
end

# TODO-neel add :crashed after adding handled mechanism
def update_from_exception(_exception = nil)
@status = :errored
def update_from_error_event(event)
return unless event.is_a?(ErrorEvent) && event.exception

crashed = event.exception.values.any? { |e| e.mechanism.handled == false }
@status = crashed ? :crashed : :errored
end

def close
Expand Down
21 changes: 17 additions & 4 deletions sentry-ruby/spec/sentry/rack/capture_exceptions_spec.rb
Expand Up @@ -640,8 +640,16 @@ def will_be_sampled_by_sdk
case req.path_info
when /success/
[200, {}, ['ok']]
when /error/
when /crash/
1 / 0
when /error/
begin
1 / 0
rescue => e
Sentry.capture_exception(e)
end

[200, {}, ['error']]
end
end

Expand All @@ -658,12 +666,17 @@ def will_be_sampled_by_sdk
stack.call(env)
end

3.times do
env = Rack::MockRequest.env_for('/crash')
expect { stack.call(env) }.to raise_error(ZeroDivisionError)
end

2.times do
env = Rack::MockRequest.env_for('/error')
expect { stack.call(env) }.to raise_error(ZeroDivisionError)
stack.call(env)
end

expect(sentry_events.count).to eq(2)
expect(sentry_events.count).to eq(5)

Sentry.session_flusher.flush

Expand All @@ -674,7 +687,7 @@ def will_be_sampled_by_sdk
item = envelope.items.first
expect(item.type).to eq('sessions')
expect(item.payload[:attrs]).to eq({ release: 'test-release', environment: 'test' })
expect(item.payload[:aggregates].first).to eq({ exited: 10, errored: 2, started: now_bucket.iso8601 })
expect(item.payload[:aggregates].first).to eq({ exited: 10, errored: 2, crashed: 3, started: now_bucket.iso8601 })
end
end
end
Expand Down
31 changes: 28 additions & 3 deletions sentry-ruby/spec/sentry/session_flusher_spec.rb
Expand Up @@ -5,6 +5,7 @@

let(:configuration) do
Sentry::Configuration.new.tap do |config|
config.dsn = Sentry::TestHelper::DUMMY_DSN
config.release = 'test-release'
config.environment = 'test'
config.transport.transport_class = Sentry::DummyTransport
Expand Down Expand Up @@ -50,6 +51,18 @@
Time.utc(time.year, time.month, time.day, time.hour, time.min)
end

let(:handled_event) do
exception = Exception.new('test')
mech = Sentry::Mechanism.new(type: 'custom', handled: true)
client.event_from_exception(exception, {}, mech)
end

let(:unhandled_event) do
exception = Exception.new('test')
mech = Sentry::Mechanism.new(type: 'custom', handled: false)
client.event_from_exception(exception, {}, mech)
end

before do
Timecop.freeze(now) do
10.times do
Expand All @@ -60,7 +73,14 @@

5.times do
session = Sentry::Session.new
session.update_from_exception
session.update_from_error_event(handled_event)
session.close
subject.add_session(session)
end

2.times do
session = Sentry::Session.new
session.update_from_error_event(unhandled_event)
session.close
subject.add_session(session)
end
Expand All @@ -77,7 +97,12 @@
item = envelope.items.first
expect(item.type).to eq('sessions')
expect(item.payload[:attrs]).to eq({ release: 'test-release', environment: 'test' })
expect(item.payload[:aggregates].first).to eq({ exited: 10, errored: 5, started: now.iso8601 })
expect(item.payload[:aggregates].first).to eq({
started: now.iso8601,
exited: 10,
errored: 5,
crashed: 2
})
end
end
end
Expand Down Expand Up @@ -124,7 +149,7 @@
subject.add_session(session)
pending_aggregates = subject.instance_variable_get(:@pending_aggregates)
expect(pending_aggregates.keys.first).to be_a(Time)
expect(pending_aggregates.values.first).to include({ errored: 0, exited: 1 })
expect(pending_aggregates.values.first).to include({ errored: 0, crashed: 0, exited: 1 })
end

context "when thread creation fails" do
Expand Down

0 comments on commit 4e46fc1

Please sign in to comment.