Search code examples
ruby

Global exception handler in Ruby


Is it possible to create a global listener for an exception in Ruby?

I want to catch all exceptions in my script for StateMachine::InvalidTransition so my application can respond with sending an email with the error.

Normally in Ruby, a rescue block is preceded by begin, but I want to have a central listener method that will catch all exceptions for the above mentioned exception.

Is this possible at all?

I really don't want to place

begin
    # Do some stuff
rescue StateMachine::InvalidTransition => exception
    # Send error in email message
end 

inside every single event I have in my state_machine.

I want something similar to set_exception_handler() in PHP.


Solution

  • Have you looked at the source for Errbit? This might have the functionality you're looking for.

    Errbit

    Personally, I believe you should fail massively and have ways of handling your response (in meatspace) instead of recovering cleverly. There is too much hair pulling that way. :-)

    Also, this might be a good reference for you too.

    Exceptional Ruby Book

    Good Luck!

    Update: 1/23/2017

    I have no idea why this got downvoted recently. I still stand by "fail massively" in production and handle the responses with other systems, which might be counter to the cargo cult of clever Rubyists, but in production, this is the way it's usually done. Mostly because when you start working in other people's production code in the wild, it will lack tests/specs and will be mostly composed on the fly because of a manager breathing down a developer's neck or because of an arbitrary deadline.

    IMO it's best not to handle errors too cleverly, i.e. some global catch method that keeps your app up and running when an Exception path is traversed. This results in silent errors in your system of which you may never even become aware of and for which you never get good TDD code coverage. A global catching of errors is great for consistent uptime but blocks you from knowing what is happening in your system. If you implement good integration test code coverage this shouldn't be an issue.

    Also, I've run across this gem, Contracts, since this was written and again with good code coverage this will help you eliminate exceptions related to poor code coverage and random returns immensely.

    Update: 3/8/2017

    I stand by my previous update that this solves the overarching question, how to implement something that will "listen" for exceptions in a ruby application.

    Sometimes you ask a question and the answer is tangential, i.e. there is a better way. Stack Overflow is about capitalizing on other's experience. Code golfing has its place but most times is unnecessary.

    Errbit is an open source exception catcher based on the open source Airbrake API and Rails, which can be stood up fairly quickly and provides many more options than hand-coding a solution. So dismissing it is wrong IMO.

    Regarding the Exceptional Ruby Book being an irrelevant resource because it costs money. In my opinion,it's perfectly acceptable for authors to request to be paid for works that they put time into creating and add value to your toolset. If the price of the book is too steep for you, request your local library obtain a copy, that's why they exist. If that fails, contact Avdi, perhaps an agreement can be reached.

    Update: 2/21/2024

    Revisiting this downvoted answer, I find it interesting that this post has negative votes as a lot of what Contracts did is now being explored in the ruby core language with pattern matching and the continual request for static typing, plus exception tracking has only increased in importance in the general world, i.e. what gets measured gets managed.

    I still stand by this approach of fail fast, so much so that I've mostly left Ruby for Elixir where "let it crash" is the rule of thumb.

    If you are reading this in 2024 and found the above interesting, e.g. fail and handle, contracts typespecs, then you might be happy in the Elixir world. You'll have much less tech debt churn as it's API is for the most part finished and just gets better as the underlying erlang improves, the scale is much faster as you'll work in microseconds vs milliseconds, concurrency and parallelism are built in, and in general you'll spend less time writing more robust error free code. 😎