Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,14 @@ Cable.configure do |settings|
settings.backend_class = Cable::RedisBackend
settings.backend_ping_interval = 15.seconds
settings.restart_error_allowance = 20
settings.on_error = ->(error : Exception, message : String) do
settings.on_error = ->(error : Exception, message : String, connection : Cable::Connection?) do
# or whichever error reportings you're using
Bugsnag.report(error) do |event|
event.app.app_type = "lucky"
event.meta_data = {
"error_class" => JSON::Any.new(error.class.name),
"message" => JSON::Any.new(message),
"token" => JSON::Any.new(connection.try(&.token).to_s),
}
end
end
Expand Down Expand Up @@ -283,7 +284,7 @@ You can setup a hook to report errors to any 3rd party service you choose.
```crystal
# config/cable.cr
Cable.configure do |settings|
settings.on_error = ->(exception : Exception, message : String) do
settings.on_error = ->(exception : Exception, message : String, connection : Cable::Connection?) do
# new 3rd part service handler
ExceptionService.notify(exception, message: message)
# default logic
Expand All @@ -295,13 +296,13 @@ end

```crystal
Habitat.create do
setting on_error : Proc(Exception, String, Nil) = ->(exception : Exception, message : String) do
setting on_error : Proc(Exception, String, Cable::Connection?, Nil) = ->(exception : Exception, message : String, connection : Cable::Connection?) do
Cable::Logger.error(exception: exception) { message }
end
end
```

> NOTE: The message field will contain details regarding which class/method raised the error
> NOTE: The message field will contain details regarding which class/method raised the error. The connection parameter provides access to the `Cable::Connection` instance (when available), including the `token`, `connection_identifier`, and any fields defined via `identified_by` or `owned_by`.

## Client-Side

Expand Down
6 changes: 4 additions & 2 deletions spec/cable/handler_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,10 @@ describe Cable::Handler do
end

FakeExceptionService.size.should eq(1)
FakeExceptionService.exceptions.first.keys.first.should eq("Cable::Handler#socket.on_message")
FakeExceptionService.exceptions.first.values.first.class.should eq(JSON::SerializableError)
exception = FakeExceptionService.exceptions.first
exception.message.should contain("Cable::Handler#socket.on_message")
exception.exception.class.should eq(JSON::SerializableError)
exception.connection.as(Cable::Connection).token.should eq("1")
end

it "rejected" do
Expand Down
4 changes: 2 additions & 2 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ Cable.configure do |settings|
settings.backend_class = Cable::RedisBackend
settings.backend_ping_interval = 2.seconds
settings.restart_error_allowance = 2
settings.on_error = ->(exception : Exception, message : String) do
FakeExceptionService.notify(exception, message: message)
settings.on_error = ->(exception : Exception, message : String, connection : Cable::Connection?) do
FakeExceptionService.notify(exception, message: message, connection: connection)
end
end

Expand Down
9 changes: 5 additions & 4 deletions spec/support/fake_exception_service.cr
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
class FakeExceptionService
@@exceptions : Array(Hash(String, Exception)) = [] of Hash(String, Exception)
record Report, exception : Exception, message : String, connection : Cable::Connection?
@@exceptions : Array(Report) = [] of Report

def self.clear
@@exceptions = [] of Hash(String, Exception)
@@exceptions = [] of Report
end

def self.size
Expand All @@ -13,7 +14,7 @@ class FakeExceptionService
@@exceptions
end

def self.notify(exception, message)
@@exceptions << {message => exception}
def self.notify(exception, message, connection = nil)
@@exceptions << Report.new(exception: exception, message: message, connection: connection)
end
end
3 changes: 2 additions & 1 deletion src/cable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ module Cable
setting backend_class : Cable::BackendCore.class = Cable::BackendRegistry, example: "Cable::RedisBackend"
setting backend_ping_interval : Time::Span = 15.seconds
setting restart_error_allowance : Int32 = 20
setting on_error : Proc(Exception, String, Nil) = ->(exception : Exception, message : String) do
# ameba:disable Lint/UnusedArgument
setting on_error : Proc(Exception, String, Cable::Connection?, Nil) = ->(exception : Exception, message : String, connection : Cable::Connection?) do
Cable::Logger.error(exception: exception) { message }
end
end
Expand Down
4 changes: 2 additions & 2 deletions src/cable/connection.cr
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ module Cable
channels_to_close.each do |identifier, channel|
channel.close
rescue e : IO::Error
Cable.settings.on_error.call(e, "IO::Error: #{e.message} -> #{self.class.name}#close")
Cable.settings.on_error.call(e, "IO::Error: #{e.message} -> #{self.class.name}#close", self)
end
unsubscribe_from_internal_channel
end
Expand Down Expand Up @@ -185,7 +185,7 @@ module Cable
Cable::Logger.info { "#{channel.class}#receive(#{payload.data})" }
channel.receive(payload.data)
rescue e : TypeCastError
Cable.settings.on_error.call(e, "Exception: #{e.message} -> #{self.class.name}#message(payload) { #{payload.inspect} }")
Cable.settings.on_error.call(e, "Exception: #{e.message} -> #{self.class.name}#message(payload) { #{payload.inspect} }", self)
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions src/cable/handler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ module Cable
ws_pinger.stop
socket.close(HTTP::WebSocket::CloseCode::InvalidFramePayloadData, "Invalid message")
Cable.server.remove_connection(connection_id)
Cable.settings.on_error.call(e, "Cable::Handler#socket.on_message")
Cable.settings.on_error.call(e, "Cable::Handler#socket.on_message -> #{message}", connection)
rescue e : Cable::Connection::UnauthorizedConnectionException
# handle unauthorized connections
# no need to log them
Expand All @@ -69,7 +69,7 @@ module Cable
# handle restart
Cable.server.count_error!
Cable.restart if Cable.server.restart?
Cable.settings.on_error.call(e, "Cable::Handler#socket.on_message")
Cable.settings.on_error.call(e, "Cable::Handler#socket.on_message -> #{message}", connection)
end
end

Expand All @@ -84,7 +84,7 @@ module Cable
if conn_id = connection_id
Cable.server.remove_connection(conn_id)
end
Cable.settings.on_error.call(e, "Cable::Handler#call -> HTTP::WebSocketHandler")
Cable.settings.on_error.call(e, "Cable::Handler#call -> HTTP::WebSocketHandler", connection)
raise e
end

Expand Down
4 changes: 2 additions & 2 deletions src/cable/server.cr
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ module Cable
subscribe
process_subscribed_messages
rescue e
Cable.settings.on_error.call(e, "Cable::Server.initialize")
Cable.settings.on_error.call(e, "Cable::Server.initialize", nil)
raise e
end
end
Expand Down Expand Up @@ -151,7 +151,7 @@ module Cable
end
end
rescue e : IO::Error
Cable.settings.on_error.call(e, "IO::Error Exception: #{e.message}: #{parsed_message} -> Cable::Server#send_to_channels(channel, message)")
Cable.settings.on_error.call(e, "IO::Error Exception: #{e.message}: #{parsed_message} -> Cable::Server#send_to_channels(channel, message)", nil)
end
end

Expand Down
Loading