ruby

Set Up Tracing for a Ruby on Rails Application in AppSignal

Daniel Amah

Daniel Amah on

Set Up Tracing for a Ruby on Rails Application in AppSignal

In this guide, we'll harness AppSignal to detect, diagnose, and remove performance bottlenecks and employ proper tracing in a Ruby on Rails application. From setting up tracing to capturing errors and logging, we’ve got you covered.

We'll ensure our application runs smoother than ever, even under the heaviest loads!

But first, let's quickly touch on how to define tracing and its benefits.

What Is Tracing?

Tracing is the process of following a request and operation through an application. In Ruby applications, tracing captures the execution flow, providing deep insights into the performance of various components.

Benefits of Tracing

Tracing has several benefits, including:

  1. Performance Optimization: Tracing identifies slow parts of an application that need performance improvements.
  2. Better Debugging: Detailed traces allow you to quickly pin down code issues and determine their causes.
  3. Improved Reliability: Tracing tracks application behavior, ensuring more reliable and effective system operation.

A Scenario: Our Lagging Rails App

Let's say it's Black Friday, the biggest shopping day of the year. Your Rails-based e-commerce platform is buzzing with thousands of eager customers, their carts brimming with products, ready to check out. Everything seems perfect — until the system starts lagging.

Transactions fail. Abandoned carts skyrocket. Panic ensues. This is every developer's nightmare. But what if you had a secret weapon? A tool that not only alerts you to issues in real-time but also dives deep into the heart of your application, tracing every request, every database query, and every background job? Enter AppSignal.

How to Set Up Tracing for Ruby on Rails Apps using AppSignal

To demonstrate the power of tracing using AppSignal, I have created a sample Rails e-commerce project that we can integrate with AppSignal. This will help you see firsthand how tracing can identify and resolve these problems.

Prerequisites

Now you're ready to set up tracing in your Ruby application using AppSignal.

Step 1: Install the AppSignal Gem

Here's the gem:

Ruby
gem 'appsignal'

Simply run bundle install:

Shell
bundle install

Step 2: Initialize AppSignal

Run the AppSignal installation command to set up the necessary configuration files.

Shell
bundle exec appsignal install <YOUR-PUSH-API-KEY>

Follow the prompt on your terminal. You can choose a config file or environment variables to configure AppSignal in your app. We will choose the config file option.

This command will generate an appsignal.yml configuration file in your config directory. This file will be pre-filled with your AppSignal push API key and some basic configuration settings.

Step 3: Configure AppSignal

Ensure config/appsignal.yml is correctly configured for your environments. This file contains configuration settings for different environments (development, test, production). Ignore actions that provide little value.

And ta da! You're all set up:

AppSignal receives data

Instrumenting our Ruby on Rails Application

AppSignal can automatically instrument several key components of our Rails application, such as database queries and web requests. We can also add custom instrumentation to trace specific parts of our application.

Custom Rails Instrumentation

To instrument specific parts of our code, we will wrap a couple of lines of code with the Appsignal.instrument method. This method can be used to trace specific blocks of code, such as more complex parts of controller actions or background jobs.

Ruby
# app/controllers/orders_controller.rb class OrdersController < ApplicationController def index @orders = Order.all #=> Candidate for N+1 queries Appsignal.instrument('calculate_totals.orders') do @orders.each do |order| order.total_price = order.line_items.sum { |item| item.quantity * item.product.price } end end render json: @orders.as_json(include: { user: { only: [:id, :name, :email] }, line_items: { include: { product: { only: [:id, :name, :price] } }, only: [:id, :quantity, :price] } }) end end

Sending a request to this endpoint will result in an N+1 query being reported.

An N+1 issue occurs when our application makes multiple database queries to load associated records for each object in a collection, instead of using a single, efficient query. This can significantly degrade performance, especially when dealing with large datasets.

The fix in this case is to eager load the get all orders query — simply replace this code:

Ruby
@orders = Order.all

With the following:

Ruby
@orders = Order.includes(:user, line_items: :product).all

Collecting and Reporting Ruby Errors with AppSignal

AppSignal allows you to capture and report application errors using the Appsignal.set_error method.

Here’s an example of capturing and reporting an error in the order controller index action:

Ruby
def index @orders = Order.all #=> Candidate for N+1 queries # Introduce a deliberate error based on order count if @orders.size > 10 raise "Too many orders" end render json: @orders.as_json(include: { user: { only: [:id, :name, :email] }, line_items: { include: { product: { only: [:id, :name, :price] } }, only: [:id, :quantity, :price] } }) rescue => e # Report error to AppSignal Appsignal.set_error(e) render json: { error: e.message }, status: :internal_server_error end

In this example, we demonstrate custom error handling within a specific controller action. However, it's important to note that AppSignal reports errors by default, so explicit error reporting with Appsignal.set_error is not usually necessary.

For consistent and centralized error handling across all controller actions, it’s recommended you use rescue_from at the controller level. This approach ensures that any unhandled exceptions in your controller actions are properly reported and managed.

Here’s an example of how you can implement this:

Ruby
class OrdersController < ApplicationController # Use rescue_from to handle errors for all controller actions rescue_from StandardError, with: :handle_error def index @orders = Order.includes(:user, line_items: :product).all Appsignal.instrument('orders.index.custom_query') do # Fake some complexity @orders.each do |order| order.total = order.line_items.sum { |item| item.quantity * item.product.price } end end render json: @orders.as_json(include: { user: { only: [:id, :name, :email] }, line_items: { include: { product: { only: [:id, :name, :price] } }, only: [:id, :quantity, :price] } }) end private # Error handling method def handle_error(exception) Appsignal.set_error(exception) render json: { error: exception.message }, status: :internal_server_error end end

On the Error > Issue list tab, we can see a list of all errors, the status of each issue, and how long ago the error occurred:

Errors Issue List

Clicking on the RuntimeError, we can see the error log and the code line that is triggering this error:

Error details in AppSignal

Let's touch on some more advanced tracing techniques before we wrap up.

Advanced Tracing Techniques

We'll implement some advanced tracing techniques on the order endpoint of our e-commerce app.

These techniques will help us manage high-traffic environments efficiently, ensure data privacy, and secure access to trace data.

High Traffic Use Cases

In high-throughput environments, collecting trace data for every request can lead to significant overhead. Sampling helps reduce this by collecting trace data for only a subset of requests.

Adding Metadata to Transactions

You can supply extra context on errors and performance issues using tags and sample data. This can help to add information that is not already part of the request, session, or environment parameters. Read more about how to pass and add additional metadata.

Asynchronous Processing

Asynchronous processing is a common technique used to handle tasks that are too time-consuming to be performed within the request-response cycle, such as sending emails, processing background jobs, and handling large data imports. Ruby on Rails applications typically use background job libraries like Sidekiq, Resque, or Delayed Job to manage these tasks.

AppSignal seamlessly integrates with libraries such as Active Job, DelayedJob, Shoryuken, Sidekiq, and Que to provide insights into the performance and errors of your background jobs.

Security Considerations

Security is a very important piece of any production-ready application. We need to ensure that sensitive data is not included in trace data to maintain user privacy. The values of filter parameters will be replaced with [FILTERED] when transmitted to AppSignal.

Let's modify config/appsignal.yml to include several sensitive request params that need masking.

YAML
# config/appsignal.yml default: &defaults push_api_key: "<%= ENV['APPSIGNAL_PUSH_API_KEY'] %>" name: "ECommerceApp" filter_parameters: - password - email - api_token - token

If you use the Rails filter_parameters config option, AppSignal will merge its config with Rails's config, so there's no need to configure it twice.

By implementing these advanced tracing techniques, you can efficiently manage high-traffic environments and ensure the privacy of sensitive data. Read more on filter parameters.

Wrapping Up

We've seen that setting up tracing for a Ruby app using AppSignal involves understanding tracing fundamentals, preparing your application, and following a step-by-step setup process. Advanced methods and debugging techniques can then be used to enhance tracing.

Regularly applying tracing in development and maintenance cycles leads to higher performance and reliability. Get started with AppSignal today for enhanced monitoring and deep performance insights into your Ruby applications.

Happy coding!

Wondering what you can do next?

Finished this article? Here are a few more things you can do:

  • Share this article on social media
Daniel Amah

Daniel Amah

Our guest author Daniel is a Principal Software Engineer and Technical Founder with over a decade of experience building scalable web and mobile platforms using Ruby on Rails, React, and AI-powered architectures. He is the creator of Taskclan and frequently writes about engineering leadership, developer workflows, and startup execution on Medium and LinkedIn.

All articles by Daniel Amah

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