Skip to content

Commit

Permalink
Add ActionCable support
Browse files Browse the repository at this point in the history
Support ActionCable by listening for `perform_action.action_cable`
events. This allows us to create performance and exception samples for
ActionCable events.
  • Loading branch information
tombruijn committed Apr 4, 2017
1 parent 76bbdd4 commit 28a0630
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/appsignal/hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def format_args(args)
end
end

require "appsignal/hooks/action_cable"
require "appsignal/hooks/active_support_notifications"
require "appsignal/hooks/celluloid"
require "appsignal/hooks/delayed_job"
Expand Down
45 changes: 45 additions & 0 deletions lib/appsignal/hooks/action_cable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Appsignal
class Hooks
# @api private
class ActionCableHook < Appsignal::Hooks::Hook
register :action_cable

def dependencies_present?
defined?(::ActiveSupport::Notifications::Instrumenter) &&
defined?(::ActionCable)
end

def install
ActiveSupport::Notifications.subscribe("perform_action.action_cable", Subscriber.new)
end

class Subscriber
def start(_name, id, payload)
# TODO: Set params and filter them, perferably with Rails param
# filtering if present. The params given are not filtered.

# request = ActionDispatch::Request.new(payload)
Appsignal::Transaction.create(
id,
Appsignal::Transaction::ACTION_CABLE,
{}#,
# :params_method => :filtered_parameters
)
end

def finish(_name, _id, payload)
transaction = Appsignal::Transaction.current

exception = payload[:exception_object]
transaction.set_error(exception) if exception

transaction.set_action_if_nil(
"#{payload[:channel_class]}##{payload[:action]}"
)
transaction.set_metadata("method", "websocket")
transaction.complete
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/appsignal/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Appsignal
class Transaction
HTTP_REQUEST = "http_request".freeze
BACKGROUND_JOB = "background_job".freeze
ACTION_CABLE = "action_cable".freeze
FRONTEND = "frontend".freeze
BLANK = "".freeze

Expand Down
106 changes: 106 additions & 0 deletions spec/lib/appsignal/hooks/action_cable_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
describe Appsignal::Hooks::ActionCableHook do
if DependencyHelper.action_cable_present?
context "with ActionCable" do
require "action_cable/engine"

before do
Appsignal.config = project_fixture_config
expect(Appsignal.active?).to be_truthy
Appsignal::Hooks.load_hooks
end

describe ".dependencies_present?" do
subject { described_class.new.dependencies_present? }

it "returns true" do
is_expected.to be_truthy
end
end

describe ".install" do
it "installs the ActionCable subscriber" do
listeners =
ActiveSupport::Notifications.notifier.listeners_for("perform_action.action_cable")
expect(listeners).to_not be_empty
end
end

describe Appsignal::Hooks::ActionCableHook::Subscriber do
context "without action_cable events" do
it "does not register the event" do
expect(Appsignal::Transaction).to_not receive(:create)

ActiveSupport::Notifications.instrument("perform_action.foo") do
# nothing
end
end
end

context "with action_cable events" do
let(:transaction) do
instance_double "Appsignal::Transaction",
:set_http_or_background_action => nil,
:set_http_or_background_queue_start => nil,
:set_metadata => nil,
:set_action => nil,
:set_action_if_nil => nil,
:set_error => nil,
:start_event => nil,
:finish_event => nil,
:complete => nil
end
let(:payload) do
{
:channel_class => "ChannelClass",
:action => "channel_action",
:data => { :foo => :bar }
}.tap do |hash|
hash[:exception_object] = exception if defined?(exception)
end
end
before do
expect(Appsignal::Transaction).to receive(:create)
.with(kind_of(String), Appsignal::Transaction::ACTION_CABLE, kind_of(Hash))
.and_return(transaction)
allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
end
after do
ActiveSupport::Notifications.instrument("perform_action.action_cable", payload) do
# nothing
end
end

shared_examples "a ActionCable transaction" do
it "starts and completes a transaction for perform_action.action_cable events" do
expect(transaction).to receive(:set_action_if_nil).with("ChannelClass#channel_action")
expect(transaction).to receive(:set_metadata).with("method", "websocket")
expect(transaction).to receive(:complete)
end
end

it_behaves_like "a ActionCable transaction"

context "with an exception" do
let(:exception) { VerySpecificError }

it_behaves_like "a ActionCable transaction"

it "registers the exception" do
expect(transaction).to receive(:set_error).with(exception)
end
end
end
end
end
else
context "without ActionCable" do
describe ".dependencies_present?" do
subject { described_class.new.dependencies_present? }

it "returns false" do
is_expected.to be_falsy
end
end
end
end
end
4 changes: 4 additions & 0 deletions spec/support/helpers/dependency_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def redis_present?
dependency_present? "redis"
end

def action_cable_present?
dependency_present? "actioncable"
end

def active_job_present?
dependency_present? "activejob"
end
Expand Down

0 comments on commit 28a0630

Please sign in to comment.