Skip to content

Commit

Permalink
Implement close API that cleans up state and reverses init logic (#1844)
Browse files Browse the repository at this point in the history
* Implement close API that cleans up state and reverses init logic

The only logic not reversed is `apply_patches` which is fine
since they already check if the patch is already applied
and also no-op if `initialized?` is false.

* Specs

* Fix Tracepoint test

* Remove no-ops and fix tracepoint receive

* changelog

* Fix changelog

* Call close on at_exit
  • Loading branch information
sl0thentr0py authored Jul 19, 2022
1 parent 1db9d5f commit 2f3e11a
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 4 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

### Features

- Expose `:values` in `ExceptionInterface`, so that it can be accessed in `before_send` under `event.exception.values` [#1843](https://github.com/getsentry/sentry-ruby/pull/1843)

- Add top level `Sentry.close` API [#1844](https://github.com/getsentry/sentry-ruby/pull/1844)
- Cleans up SDK state and sets it to uninitialized
- No-ops all SDK APIs and also disables the transport layer, so nothing will be sent to Sentry after closing the SDK

- Handle exception with large stacktrace without dropping entire item [#1807](https://github.com/getsentry/sentry-ruby/pull/1807)
- Capture Rails runner's exceptions before exiting [#1820](https://github.com/getsentry/sentry-ruby/pull/1820)

Expand Down
29 changes: 25 additions & 4 deletions sentry-ruby/lib/sentry-ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ def exception_locals_tp
end

# @!attribute [rw] background_worker
# @return [BackgroundWorker]
# @return [BackgroundWorker, nil]
attr_accessor :background_worker

# @!attribute [r] session_flusher
# @return [SessionFlusher]
# @return [SessionFlusher, nil]
attr_reader :session_flusher

##### Patch Registration #####
Expand Down Expand Up @@ -213,10 +213,30 @@ def init(&block)
exception_locals_tp.enable
end

at_exit do
@session_flusher&.kill
at_exit { close }
end

# Flushes pending events and cleans up SDK state.
# SDK will stop sending events and all top-level APIs will be no-ops after this.
#
# @return [void]
def close
if @background_worker
@background_worker.shutdown
@background_worker = nil
end

if @session_flusher
@session_flusher.kill
@session_flusher = nil
end

if configuration&.capture_exception_frame_locals
exception_locals_tp.disable
end

@main_hub = nil
Thread.current.thread_variable_set(THREAD_LOCAL, nil)
end

# Returns true if the SDK is initialized.
Expand Down Expand Up @@ -287,6 +307,7 @@ def get_current_scope
#
# @return [void]
def clone_hub_to_current_thread
return unless initialized?
Thread.current.thread_variable_set(THREAD_LOCAL, get_main_hub.clone)
end

Expand Down
67 changes: 67 additions & 0 deletions sentry-ruby/spec/sentry_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -784,4 +784,71 @@
end
end

describe ".close" do
context "when closing initialized SDK" do
it "not initialized?" do
expect(described_class.initialized?).to eq(true)
described_class.close
expect(described_class.initialized?).to eq(false)
end

it "removes main hub" do
expect(described_class.get_main_hub).to be_a(Sentry::Hub)
described_class.close
expect(described_class.get_main_hub).to eq(nil)
end

it "removes thread local" do
expect(Thread.current.thread_variable_get(described_class::THREAD_LOCAL)).to be_a(Sentry::Hub)
described_class.close
expect(Thread.current.thread_variable_get(described_class::THREAD_LOCAL)).to eq(nil)

end

it "calls background worker shutdown" do
expect(described_class.background_worker).to receive(:shutdown)
described_class.close
expect(described_class.background_worker).to eq(nil)
end

it "kills session flusher" do
expect(described_class.session_flusher).to receive(:kill)
described_class.close
expect(described_class.session_flusher).to eq(nil)
end

it "disables Tracepoint" do
perform_basic_setup do |config|
config.capture_exception_frame_locals = true
end

expect(described_class.exception_locals_tp).to receive(:disable).and_call_original
described_class.close
end
end

it "can reinitialize closed SDK" do
perform_basic_setup

expect do
described_class.capture_event(event)
end.to change { transport.events.count }.by(1)

described_class.close

expect do
described_class.capture_event(event)
end.to change { transport.events.count }.by(0)

perform_basic_setup

expect(described_class.initialized?).to eq(true)

new_transport = described_class.get_current_client.transport

expect do
described_class.capture_event(event)
end.to change { new_transport.events.count }.by(1)
end
end
end

0 comments on commit 2f3e11a

Please sign in to comment.