ruby

# Debugging in Ruby with AppSignal

Brena Monteiro on

An application monitoring tool (APM) is not just useful for seeing how your application performs through graphs and visuals. We can go deeper and use an APM to understand how your application behaves in a certain environment.

As developers, we should aim to be less reactive to errors and more predictive, avoiding crashes for end-users.

One way to accomplish this is by using monitoring tools to debug our application when an error occurs. AppSignal has advanced features that allow us to intelligently debug our logs and thus reflect this precautionary thinking in our applications.

## Integrate Your Ruby App with AppSignal

To start debugging with AppSignal, you need to have an AppSignal account.

Integrate your Ruby application with AppSignal by following the steps in the article 'Ruby on Rails Application Monitoring with AppSignal'.

## Log Options

Now that you have an application set up to send logs to AppSignal, let's look at the logging options for your Ruby application.

AppSignal offers various log levels for your application according to your project's needs. In this section, we will explore the difference between each log level.

At first glance, we might think these logs are related to the application logs shown in the 'Backtrace' section of AppSignal.

However, changes to log_level won't reflect the information displayed in the backtrace. It is important to understand that setting the log_level value relates to AppSignal's communication logs with your application. It has nothing to do with application logs.

This means that when we set the logging level in our application, we change the type of information that appears in the application logs about execution and integration with AppSignal.

In most cases, you can just set the log_level to error (unless you've had an issue with the AppSignal integration and need to send log information to AppSignal's Support team). The log_level must be defined in config/appsignal.yml, as in the example below:

# config/appsignal.yml

production:
<<: *defaults
log_level: error

Watch the application logs when we set error, warning, or info to log_level. We will see pretty much the same information in the logs.

Now, check out the logs when we set log_level as debug.

Finally, let's see what happens when we set log_level as trace.

## Ignore Your Ruby App's Predicted Errors in AppSignal

You don't need to catch and keep all errors in logs. Some common errors can be expected in an application — we don't need to inflate our logs with 404 errors, for example. Now let's configure AppSignal to ignore errors that are irrelevant to trace.

Ignoring an error is useful when a production bug has already been identified, but the fix is still in development. We already know about the problem's existence, so there's no need to keep filling the error monitoring tool or be notified about it. We can ignore the error's appearance until the patch deployment goes into production. This approach can be applied to all known bugs that have already been cataloged in the issues list.

We can also ignore errors when we know that a service our application communicates with will be down for maintenance. This way, we don't flood the monitoring tool with errors regarding expected maintenance, alerting our monitoring team unnecessarily.

AppSignal's gem provides three ways to handle and ignore predicted errors.

### ignore_errors

ignore_errors helps when you need to ignore all errors generated by a class error or when you don't know the exact error that could happen from there.

Maybe your application needs to do many math calculations, and AppSignal doesn't need to track all related errors. ignore_errors will reduce the errors list and only keep issues that need to be addressed.

### ignore_actions

This is useful if you know exactly which method will generate an error you don't need to trace — for example, a method that will be re-executed until it succeeds. You can also use ignore_actions for queue processing when another monitoring tool handles logs specific to queue item failures.

### ignore_namespace

ignore_namespace is great if you have an application separated by domain and want to group errors. Maybe you have a specific module to treat payments or to process an order in the background. Grouping it in a namespace for jobs makes sense. Then you can ignore the exceptions that occur inside this namespace.

### Configure in Your Ruby App

Include ignore_errors, ignore_actions, and ignore_namespace in your config/appsignal.yml file, with a list of classes to ignore when this error occurs in your application.

# config/appsignal.yml

production:
<<: *defaults
ignore_errors:
- Net::HTTPGatewayTimeout
- NoMethodError
ignore_actions:
- HomeController#index
ignore_namespace:
- jobs
- payment

## Debugging Exceptions in Ruby with AppSignal

Now that we know how to ignore irrelevant errors, it's time to deal with the errors that matter. AppSignal provides functions to configure how an error is sent from your application to the AppSignal monitor. Let's learn about the listen_for_error feature and extract valuable information from exceptions.

Sometimes, we have a situation where an exception may or may not be thrown due to some parameters (if a job is not running or even if an external endpoint is unavailable). We still want to send detailed information to AppSignal, not just the exception itself. For this case, we'll create a custom exception and use it to send helpful information to the monitoring team.

The first thing we need to do is create a class for our custom exception. Make a new folder called exceptions inside your application directory. Then create a new class called CustomException inherited from StandardError. You can define any information as the content of this exception. By default, an exception already has msg as an attribute.

In this example, you need to include an exception name and the content field to report all details about the exception. In the exception constructor, all attributes are set and the method to send to AppSignal is called.

The block inside Appsignal.listen_for_error is where the magic happens! The raise will send the exception content to AppSignal in hash format to facilitate the separation of the different contents.

# app/exceptions/custom_exception.rb

class CustomException < StandardError
attr_accessor :name, :content

def initialize(name, msg, content)
@name = name
@msg = msg
@content = content

send_exception_to_appsignal
end

def send_exception_to_appsignal
Appsignal.listen_for_error do
exception_hash = { name: @name, msg: @msg, content: @content }
raise "#{exception_hash}"
end
end
end

Now you can call this custom exception from anywhere, to use it in your application. For this example, we will include it in the controller inside the Home#index method. We need a block to handle the exception, to avoid sending it to AppSignal. The nil.object code will throw a NoMethodError exception, but this exception is handled, and only CustomException will be sent to AppSignal.

# app/controllers/home_controller.rb

class HomeController < ApplicationController
def index
begin
nil.object
rescue => e
raise CustomException.new("Customized Exception", "A new exception occurred", e)
end
end
end

All done! After starting your application and accessing http://localhost:3000/home#index in your browser, you will see a page with a RuntimeError exception. In AppSignal, access the 'Errors > Issue list' to see the Customized Exception issue.

{:name=>"Customized Exception", :msg=>"A new exception occurred", :content=>#<NoMethodError: undefined method object' for nil:NilClass>}`

## Wrap Up and Next Steps

AppSignal provides a well-documented gem to start truly monitoring your Ruby application. Why not test it? Send data from your local machine without deploying to see if the error configuration is how you want it.

Many other features in AppSignal make it easier to debug Ruby applications, like exception handling. Check AppSignal's Ruby documentation and choose the feature that makes the most sense in your project's context. Make your app easier to debug by decreasing the time your team needs to spend looking into the root cause of bugs in production.

Happy debugging!

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!