Skip to content

Capture exceptions in Action Cable connections and channels#1295

Closed
agrobbin wants to merge 1 commit intogetsentry:masterfrom
agrobbin:rails-action-cable-support
Closed

Capture exceptions in Action Cable connections and channels#1295
agrobbin wants to merge 1 commit intogetsentry:masterfrom
agrobbin:rails-action-cable-support

Conversation

@agrobbin
Copy link
Copy Markdown
Contributor

Hi again @st0012! Sorry it took me a couple weeks to get back to this. I've reissued this now that 4.2 has already been released.

I mentioned this in #470 (comment) (and #470 (comment)), and now that 4.x is reasonably stable, thought it worth getting this PR out there.

There are 5 types of hooks that Action Cable provides:

  • Connection#connect
  • Connection#disconnect
  • Channel#subscribed
  • Channel#unsubscribed
  • Channel actions

Now, any exceptions raised within those hooks are captured by Sentry, and reported as ActionCable/[...] transactions. Additional context is included depending on the hook the exception was raised within.

A note/quirk: the Rack env that's included in the scope is from the Connection, and therefore has a URL of the cable mount_path (usually /cable) as well as the headers from that initial connection request.

Additionally, there is not currently a really clean way to hook in and set user_context. I don't know if that is a blocker for this integration, but wanted to make sure it was noted.

Finally, this is limited to Rails 6+, as the hooks used (action_cable_connection and action_cable_channel) weren't introduced until then (and neither were the test helpers).

Closes #470.

@codecov-io
Copy link
Copy Markdown

codecov-io commented Feb 20, 2021

Codecov Report

Merging #1295 (b572778) into master (6e6bdd5) will decrease coverage by 3.34%.
The diff coverage is 6.66%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1295      +/-   ##
==========================================
- Coverage   97.95%   94.60%   -3.35%     
==========================================
  Files         203       33     -170     
  Lines        8758     1020    -7738     
==========================================
- Hits         8579      965    -7614     
+ Misses        179       55     -124     
Impacted Files Coverage Δ
...entry-rails/spec/sentry/rails/action_cable_spec.rb 2.70% <2.70%> (ø)
sentry-rails/lib/sentry/rails/railtie.rb 90.76% <25.00%> (-9.24%) ⬇️
sentry-rails/app/jobs/sentry/send_event_job.rb 75.00% <0.00%> (-12.50%) ⬇️
sentry-ruby/lib/sentry/configuration.rb
sentry-ruby/lib/sentry/core_ext/object/deep_dup.rb
sentry-ruby/lib/sentry/rack/deprecations.rb
...-ruby/spec/sentry/breadcrumb/sentry_logger_spec.rb
sentry-raven/lib/raven/backtrace.rb
sentry-ruby/spec/sentry/background_worker_spec.rb
sentry-raven/spec/raven/instance_spec.rb
... and 165 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6e6bdd5...b572778. Read the comment docs.

@agrobbin
Copy link
Copy Markdown
Contributor Author

@st0012 I have also included an update to the sentry-rails example app, adding a welcome#appearance controller action that provides buttons to trigger 2 different channel actions (one of which raises an error), as you requested in #1251!

Copy link
Copy Markdown
Contributor

@st0012 st0012 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the PR 👍
I left some questions and it looks like there are some duplications?

module Subscriptions
def self.included(base)
base.class_eval do
set_callback :subscribe, :around, ->(_, block) { sentry_capture(:subscribed, &block) }, prepend: true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't we use rescue_from but around callbacks?

Copy link
Copy Markdown
Contributor Author

@agrobbin agrobbin Feb 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm definitely open to another approach here, @st0012! I went down the around route to allow channels/connections to set any additional context and/or capture their own exceptions, similar to the Sentry::Rack::CaptureExceptions and Sentry::Sidekiq::SentryContextMiddlware middlewares.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, let's keep it this way then. and perhaps adding a test case for this scenario?

@agrobbin agrobbin force-pushed the rails-action-cable-support branch from 5777598 to b572778 Compare February 21, 2021 17:16
@agrobbin
Copy link
Copy Markdown
Contributor Author

@st0012 I have no idea why I somehow ended up with so much duplication in this PR! I must have made a mistake when copy/pasting from the app of ours that is doing this.

Anyways, I've just pushed up a new version that eliminates that.

There are 5 types of hooks that Action Cable provides:

* `Connection#connect`
* `Connection#disconnect`
* `Channel#subscribed`
* `Channel#unsubscribed`
* `Channel` actions

Now, any exceptions raised within those hooks are captured by Sentry, and reported as `ActionCable/[...]` transactions. Additional context is included depending on the hook the exception was raised within.

A note/quirk: the Rack env that's included in the scope is from the `Connection`, and therefore has a URL of the cable `mount_path` (usually `/cable`) as well as the headers from that initial connection request.

Additionally, there is not currently a really clean way to hook in and set `user_context`. I don't know if that is a blocker for this integration, but wanted to make sure it was noted.
@agrobbin agrobbin force-pushed the rails-action-cable-support branch from b572778 to 7b3abed Compare February 21, 2021 17:26
Copy link
Copy Markdown
Contributor

@st0012 st0012 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested it locally and it looks great 👍
After adding more test cases this will be ready to merge 😄
(I may defer the merge a bit to ship other patch versions first, this will go out in 4.3.0)

module Subscriptions
def self.included(base)
base.class_eval do
set_callback :subscribe, :around, ->(_, block) { sentry_capture(:subscribed, &block) }, prepend: true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, let's keep it this way then. and perhaps adding a test case for this scenario?

end

describe ChatChannel do
include ActionCable::Channel::TestCase::Behavior
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use channel spec here instead of mixing with minitest?

it "captures errors during the subscribe" do
expect { subscribe room_id: 42 }.to raise_error('foo')

event = transport.events.last.to_json_compatible
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we also verify that the SDK only captures 1 event? like

expect(transport.events.count).to eq(1)

it "captures errors during the action" do
expect { perform :appear, foo: 'bar' }.to raise_error('foo')

event = transport.events.last.to_json_compatible
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expect(transport.events.count).to eq(1)

end

class ChatChannel < ::ActionCable::Channel::Base
def subscribed
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we also test unsubscribe?

Sentry.with_scope do |scope|
scope.set_rack_env(env)
scope.set_extras(action_cable: extra_context) if extra_context
scope.set_transaction_name("#{ACTION_CABLE_NAME}/#{transaction_name}")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we already prefix it transaction_name with the channel's name, like AppearanceChannel#subscribed, I don't think the additional ActionCable namespace is necessary.

@st0012 st0012 added this to the 4.3.0 milestone Mar 1, 2021
@st0012
Copy link
Copy Markdown
Contributor

st0012 commented Mar 4, 2021

@agrobbin I'll release v4.3.0 next week. so maybe we can push this over the line by the end of this week?

@st0012 st0012 modified the milestones: 4.3.0, 4.4.0 Mar 11, 2021
@st0012 st0012 removed this from the 4.4.0 milestone Apr 23, 2021
@github-actions
Copy link
Copy Markdown

This pull request has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you label it Status: Backlog or Status: In Progress, I will leave it alone ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

@louim
Copy link
Copy Markdown
Contributor

louim commented Nov 30, 2021

Hey @st0012, I think this got closed as stale but it was supposed to be merged, right?

@st0012
Copy link
Copy Markdown
Contributor

st0012 commented Dec 3, 2021

@louim I don't have a strong opinion on adding/rejecting this feature. on adding integration-specific features we usually make decisions based on community's reaction. and at the moment we haven't seen a strong demand on ActionCable integration. given that this PR wasn't complete (very close though), I didn't merge it nor have a plan to finish it.

but if you or other users want to give the integration another try, I'll be happy to work with you 🙂

@agrobbin
Copy link
Copy Markdown
Contributor Author

agrobbin commented Dec 8, 2021

@st0012 @louim my sincere apologies on the slow-walking of this PR. I unfortunately have not had the time to come back to it and resolve the outstanding items. My work has drifted away from Action Cable over the last few months, making this less valuable for me, so it's gotten pushed down my priority list.

I will try to get back to it in the next week or 2, but @louim if you want to take it the rest of the way, I'm totally on board with that!

@choznerol
Copy link
Copy Markdown
Contributor

Thanks @agrobbin and @st0012 for the majority of work so far! I'd like to help bring this PR up to date and finish the last few items :)

choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
getsentry#1295

Co-authored-by: Stan Lo <stan001212@gmail.com>
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 9, 2021
choznerol added a commit to choznerol/sentry-ruby that referenced this pull request Dec 10, 2021
st0012 pushed a commit that referenced this pull request Dec 12, 2021
…tion of #1295) (#1638)

* capture exceptions in Action Cable connections and channels

There are 5 types of hooks that Action Cable provides:

* `Connection#connect`
* `Connection#disconnect`
* `Channel#subscribed`
* `Channel#unsubscribed`
* `Channel` actions

Now, any exceptions raised within those hooks are captured by Sentry, and reported as `ActionCable/[...]` transactions. Additional context is included depending on the hook the exception was raised within.

A note/quirk: the Rack env that's included in the scope is from the `Connection`, and therefore has a URL of the cable `mount_path` (usually `/cable`) as well as the headers from that initial connection request.

Additionally, there is not currently a really clean way to hook in and set `user_context`. I don't know if that is a blocker for this integration, but wanted to make sure it was noted.

* Only extend ActionCable when it's defined

Similar to the update for ActionCableExtensions in #1218 and #1494

* test(ActionCable): Assert only 1 event captured:

#1295
#1295

* Remove namespace 'ActionCable/*'

#1295

* Test ActionCable #unsubscribed

#1295

* test(ActionCable): prefer rspec-rails over mini-test

#1295 (comment)

* test(ActionCable): cleanup duplicated set_callback

* docs(CHANGELOG): update #1295 to #1638

Co-authored-by: Alex Robbin <agrobbin@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ActionCable probably doesn't work

5 participants