ruby

Diving into Custom Exceptions in Ruby

Brena Monteiro

Brena Monteiro on

Diving into Custom Exceptions in Ruby

Customizing exceptions is usually not a common concern during software development. But if you catch an error in an observability tool and can't correctly and quickly identify the problem, you may need more information and details about an exception.

This article will show you how to customize exceptions in Ruby and mitigate potential future problems due to a lack of error information.

Let's dive straight in!

A Quick Side-Note

This post is a natural progression to Debugging in Ruby with AppSignal, so we recommend reading that first for an overview of debugging and an introduction to custom exceptions.

How to Rescue an Exception with Ruby

Ruby has a class called Exception, from which error-handling classes inherit. In this section, we'll better understand Ruby's structural exception flow before we create our custom exception.

Exception is the main exception class in Ruby, and rescue Exception will catch all exceptions inherited from Exception. It isn't best practice to use this in our app, as it will catch all exceptions used internally by Ruby.

The easiest way to rescue an exception in Ruby is to create a begin ... rescue block of code like the example below:

ruby
begin call_some_method() rescue Exception => e ... end

Since Exception will catch all exceptions, we should try to use something more specific to filter and get only the error we want. One example is the StandardError class, a standard rescuer used to catch exceptions related to application code errors by ignoring Ruby's internal exceptions.

To catch a StandardException, you can use the example below:

ruby
begin call_some_method() rescue StandardException => e ... end

Or, if no exception class is stated, Ruby will return the exceptions handled by the StandardException class.

ruby
begin call_some_method() rescue => e ... end

To create a custom exception with Ruby, you'll probably create a class that inherits from StandardError. We'll cover that in the next few sections.

To learn more about exceptions and find the best one to inherit, check Ruby's exceptions list.

Adding Additional Information to the Ruby Exception

Creating a new exception without sending relevant information will not effectively help in debugging — identifying and fixing — the problem. You need to add the error details. Here, we will see how to pass information from objects to the custom exception.

Let's create a new custom exception just to receive the data we want to log. As mentioned in the previous section, the custom exception needs to inherit from StandardError.

You can use a simple Rails project to test this implementation. Create a new folder called exceptions inside the app folder and a class called custom_exception.rb in your Rails project.

ruby
# app/exceptions/custom_exception.rb class CustomException < StandardError def initialize(code, name, msg, details) @code = code @name = name @msg = msg @details = details end end

In this exception class, we can define all the information that needs to be shown in the logs and we use four attributes for our data:

  • a code to enumerate the exception
  • a name
  • an informational message
  • internal information details about the exception already provided by Ruby

And now we create a begin...rescue block to raise the CustomException and pass the parameters to the exception attributes.

ruby
# app/models/employee.rb class Employee def calculate_fees begin nil.object # Any code that throws an exception. rescue => exception raise CustomException.new( 333, "My customized exception", "A new exception occurred in the application", exception) end end end

This looks useful, so let's continue the implementation and use this data to customize the exception log.

Extending an Exception in Ruby

Extending an exception allows us to elevate customization to a more specific and reusable level. Just as Ruby has several exceptions for different types of errors, we can also follow this line of programming in our systems.

Next, let's understand how to override our exception and use it in a context that demands an extension.

Finding information in logs is a painful activity. Developers often blame themselves for not including more information about errors or how to search and filter. If you are not using any monitoring tools that provide this, including meaningful data could save you in the foreseeable future.

Pro-tip: Check out the section on 'Debugging Exceptions in Ruby with AppSignal' in our post Debugging in Ruby with AppSignal for information about how to set up and track custom exceptions in AppSignal.

In the example below, we will customize the display of exceptions in the log. Initially, you might think that it's just a light and pretty way to show data. However, the keywords used in the data are key to finding an error and helping whoever will be monitoring the application.

Add the colorize gem to your project by including the code below in your Gemfile.

ruby
# Gemfile gem 'colorize'

Run the bundle to install the colorize gem and launch your Rails application.

bash
$ bundle install $ rails s

Now, in your exception class, import the gem. Print the attributes in a formatted way, so it's easy to see and quickly find an important error.

ruby
# app/exceptions/custom_exception.rb require 'colorize' class CustomException < StandardError def initialize(code, name, msg, details) ... log_exception end def log_exception print_separator STDERR.puts "🚨🚨🚨 #{self.class} 🚨🚨🚨".colorize(:red) self.instance_variables.each do |attr| # All attributes will be printed. STDERR.puts "#{attr}: #{self.instance_variable_get(attr)}".colorize(:red) end print_separator end def print_separator 85.times { print "-".colorize(color: :black, background: :red) } STDERR.puts "\n" end def to_s @name.colorize(:red) end end

Check out the result of this visual customization and test some options to create your format and style.

Custom Exception

Wrapping Up and Next Steps

In this post, we covered how to customize and highlight a Ruby exception in your logs. Many other customizations are possible, but what's most important is that you handle the exceptions relevant to your application. And remember, don't apply the same formatting style to all exceptions, as Rails already does.

Happy coding!

P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, subscribe to our Ruby Magic newsletter and never miss a single post!

Brena Monteiro

Brena Monteiro

Guest author Brena is a Tech Lead passionate about mentoring new developers and experienced in developing and leading high-performing teams. She has experience in building scalable APIs and integrations between cloud services, is enthusiastic about evolutionary architecture, and is an Extended Reality (XR) Apprentice.

All articles by Brena Monteiro

Become our next author!

Find out more

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal
AppSignal monitors your apps