Better ActionCable Errors

Eli Fatsi, Former Development Director

Article Categories: #Code, #Back-end Engineering

Posted on

A simple monkey patch to make ActionCable errors readable

You know what's neat? Websockets. I'll admit I didn't see their potential beyond making chat applications until I started poking around with a project using a handrolled HTML-over-the-wire approach, and now I'm all in.

You know what's not neat? The default formatting that Rails's ActionCable kicks out whenever you hit a runtime error during the handling of a websocket request.

You know what else is neat? Monkey patching! I wouldn't be surprised if the error formatting got better in future releases of ActionCable. But until then, here's a little snippet of code you can drop into your project to get more readable ActionCable errors:

# in config/initializers/monkey_patches.rb

module BetterActionCableErrors
  # This is replacing the default implementation for `execute_command`:
  # https://github.com/rails/rails/blob/0ff395e1b115c91e286f694a0dbd136ddcde2f2a/actioncable/lib/action_cable/connection/subscriptions.rb#L15-L26

  def execute_command(data)
    case data["command"]
    when "subscribe"   then add data
    when "unsubscribe" then remove data
    when "message"     then perform_action data
    else
      logger.error "Received unrecognized command in #{data.inspect}"
    end
  rescue Exception => e
    @connection.rescue_with_handler(e)

    logger.error ""
    logger.error "\e[31m#{e.class} - #{e.message}\e[0m"
    logger.error "Websocket command: \e[34m#{data.inspect}\e[0m"

    project_name = Rails.root.to_s.split("/").last
    e.backtrace.each do |trace_line|
      if trace_line.include?(project_name)
        logger.error trace_line
      end
    end
  end
end

ActionCable::Connection::Subscriptions.prepend(BetterActionCableErrors)

The only thing new here, compared to the source, is the block below the rescue call. Instead of logging everything out in one line and only outputting the first 5 backtrace lines, I output a blank line, the details of the error (in red of course), then the websocket request data, and finally all backtrace lines within your project directory, which cuts out about 25 boilerplate lines of things that look like this: /Users/efatsi/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/actioncable-6.0.3.4/lib/action_cable/server/worker.rb:59:in 'block in invoke'

I added a raise call in my code to demonstrate how errors look before:

before Can you see the error in there? And how about that backtrace separated with |s?

And after:

after Ahhhhh

I've submitted a Rails PR with this update and will see if they take it! But if not, hopefully you can just monkey patch this in and get yourself some better looking errors.

Related Articles