Ruby Features

Note: These docs do not cover out of the box features of the agents, such as metric and trace collection.

Highly Recommended: While we strive for an everything assembled out of the box experience, there are a few features that may need some setup that we highly suggest configuring: Deploy Tracking, Request Queueing, Auto Instruments and Custom Context.

Deploy Tracking

Scout can track deploys, making it easier to correlate changes in your app to performance. To enable deploy tracking, first ensure you are on the latest version of scout_apm. See our upgrade instructions.

Scout identifies deploys via the following:

  1. If you are using Capistrano, no extra configuration is required. Scout reads the contents of the REVISION and/or revisions.log file and parses out the SHA of the most recent release.
  2. If you are using Heroku, enable Dyno Metadata. This adds a HEROKU_SLUG_COMMIT environment variable to your dynos, which Scout then associates with deploys.
  3. If you are deploying via a custom approach, set a SCOUT_REVISION_SHA environment variable equal to the SHA of your latest release.
  4. If the app resides in a Git repo, Scout parses the output of git rev-parse --short HEAD to determine the revision SHA.

Request Queuing

Our Ruby integration can measure the time it takes a request to reach your application from farther upstream (a load balancer or web server). This appears in Scout as “Request Queueing” and provides an indication of your application’s capacity. Large request queueing time is an indication that your app needs more capacity.

Please view request queueing section to learn how to get these insights.

Auto Instruments

In many apps, more than 30% of the time spent in a transaction is within custom code written by your development team. In traces, this shows up as time spent in “Controller” or “Job”. AutoInstruments helps break down the time spent in your custom code without the need to add custom instrumentation on your own.

AutoInstruments instruments code expressions in Ruby on Rails controllers by instrumenting Ruby’s Abstract Syntax Tree (AST) as code is loaded. These code expressions then appear in traces, just like the many libraries Scout already instruments:

auto instruments trace

In the screenshot of a trace above, 68% of the time would be allocated to the Controller without enabling AutoInstruments. With AutoInstruments enabled, Controller time is just 3% of the request and we can clearly see that most of the time is spent inside two method calls.

AutoInstruments is currently available for Ruby on Rails applications.

Enabling AutoInstruments

AutoInstruments is available to apps using Ruby 2.3.1+. To enable:

1. Within your Rails app’s directory, run:

bundle update scout_apm

AutoInstruments was released in scout_apm version 2.6.0.

2. Set the auto_instruments config option to true.

If you are using a config file:

# config/scout_apm.yml
production:
  auto_instruments: true

If you are using environment variables:

SCOUT_AUTO_INSTRUMENTS=true

3. Deploy

A detailed AutoInstruments FAQ is available in our reference area.

Troubleshooting: You may need to add/upgrade the parser gem.

To verify this, set log_level: debug in your scout_apm.yml (SCOUT_LOG_LEVEL: debug if using environment variable configuration – such as for Heroku), then check your log/scout_apm.log (if on Heroku check Logplex or your log drains) for the following debug level log: AutoInstruments is enabled, but Parser::TreeRewriter is missing. Update 'parser' gem to >= 2.5.0.

The parser gem version works best when it matches the version of Ruby running your application (e.g. parser v3.1.1.0 for Ruby 3.1.1) It is great, but we have seen issues even when parser was “ahead” of the Ruby version, so we recommend matching as closely as possible. See parser’s README for more information.

Custom Context

Context lets you see the forest from the trees. For example, you can add custom context to answer critical questions like:

It’s simple to add custom context to your app. There are two types of context:

User Context

For context used to identify users (ex: id):

ScoutApm::Context.add_user({})

Examples:

ScoutApm::Context.add_user(id: @user.id)
ScoutApm::Context.add_user(id: @user.id, location: @user.location.to_s)

General Context

ScoutApm::Context.add({})

Examples:

ScoutApm::Context.add(account: @account.id)
ScoutApm::Context.add(database_shard: @db.shard_id, monthly_spend: @account.monthly_spend)

Default Context

Scout reports the Request URI and the user’s remote IP Address by default.

Context Types

Context values can be any of the following types:

Context Field Name Restrictions

Custom context names may contain alphanumeric characters, dashes, and underscores. Spaces are not allowed.

Attempts to add invalid context will be ignored.

Example: adding the current user’s id as context

Add the following to your ApplicationController class:

before_filter :set_scout_context

Create the following method:

def set_scout_context
  ScoutApm::Context.add_user(id: current_user.id) if current_user.is_a?(User)
end

Example: adding the monthly spend as context

Add the following line to the ApplicationController#set_scout_context method defined above:

ScoutApm::Context.add(monthly_spend: current_org.monthly_spend) if current_org

PII

To better protect your data, we suggest using ids instead of explicit names and emails

Renaming transactions

There may be cases where you require more control over how Scout names transactions for your endpoints and background jobs.

For example, if you have a controller-action that renders both JSON and HTML formats and the rendering time varies significantly between the two, it may make sense to define a unique transaction name for each.

Use ScoutApm::Transaction#rename:

class PostsController < ApplicationController
  def index                              
    ScoutApm::Transaction.rename("posts/foobar")                                   
    @posts = Post.all                    
  end
end

In the example above, the default name for the transaction is posts/index, which appears as PostsController#index in the Scout UI. Renaming the transaction to posts/foobar identifies the transaction as PostsController#foobar in the Scout UI.

Do not generate highly cardinality transaction names (ex: ScoutApm::Transaction.rename("posts/foobar_#{current_user.id}")) as we limit the number of transactions that can be tracked. High-cardinality transaction names can quickly surpass this limit.

GraphQL

If you have a GraphQL endpoint which serves any number of queries, you likely want to have each of those types of queries show up in the Scout UI as different endpoints. You can accomplish this by renaming the transaction during the request like so:

scout_transaction_name = "GraphQL/" + operation_name
ScoutApm::Transaction.rename(scout_transaction_name)

Where operation_name is determined dynamically based on the GraphQL query. E.g. get_profile, find_user, etc.

Do not generate highly cardinality transaction names, like ScoutApm::Transaction.rename("GraphQL/foobar_#{current_user.id}"), as we limit the number of transactions that can be tracked. High-cardinality transaction names can quickly surpass this limit.

Custom Instrumentation

Traces that allocate significant amount of time to Controller or Job are good candidates to add custom instrumentation. This indicates a significant amount of time is falling outside our default instrumentation.

Limits

We limit the number of metrics that can be instrumented. Tracking too many unique metrics can impact the performance of our UI. Do not dynamically generate metric types in your instrumentation (ie self.class.instrument("user_#{user.id}", "generate") { ... }) as this can quickly exceed our rate limits.

Instrumenting method calls

To instrument a method call, add the following to the class containing the method:

  class User
    include ScoutApm::Tracer

    def export_activity
      # Do export work
    end
    instrument_method :export_activity
  end

The call to instrument_method should be after the method definition.

Naming methods instrumented via instrument_method

In the example above, the metric will appear in traces as User#export_activity. On timeseries charts, the time will be allocated to a Custom type.

To modify the type:

instrument_method :export_activity, type: 'Exporting'

A new Exporting metric will now appear on charts. The trace item will be displayed as Exporting/User/export_activity.

To modify the name:

instrument_method :export_activity, type: 'Exporting', name: 'user_activity'

The trace item will now be displayed as Exporting/user_activity.

Instrumenting blocks of code

To instrument a block of code, add the following:

  class User
    include ScoutApm::Tracer

    def generate_profile_pic
      self.class.instrument("User", "generate_profile_pic") do
        # Do work
      end
    end
  end

Naming methods instrumented via instrument(type, name)

In the example above, the metric appear in traces as User/generate_profile_pic. On timeseries charts, the time will be allocated to a User type. To modify the type or simply, simply change the instrument corresponding method arguments.

Environments

It typically makes sense to treat each environment (production, staging, etc) as a separate application within Scout and ignore the development and test environments. Configure a unique app name for each environment as Scout aggregates data by the app name.

There are 2 approaches:

1. Modifying your scout_apm.yml config file

Here’s an example scout_apm.yml configuration to achieve this:

common: &defaults
  name: <%= "YOUR_APP_NAME (#{Rails.env})" %>
  key: YOUR_KEY
  log_level: info
  monitor: true

production:
  <<: *defaults

development:
  <<: *defaults
  monitor: false

test:
  <<: *defaults
  monitor: false

staging:
  <<: *defaults

2. Setting the SCOUT_NAME environment variable

Setting the SCOUT_NAME and SCOUT_MONITOR environment variables will override settings settings your scout_apm.yml config file.

To isolate data for a staging environment: SCOUT_NAME="YOUR_APP_NAME (Staging)".

To disable monitoring: SCOUT_MONITOR=false.

See the full list of configuration options.

Disabling a Node

To disable Scout APM on any node in your environment, just set monitor: false in your scout_apm.yml configuration file on that server, and restart your app server. Example:

common: &defaults
  name: <%= "YOUR_APP_NAME (#{Rails.env})" %>
  key: YOUR_KEY
  log_level: info
  monitor: false

production:
  <<: *defaults

Since the YAML config file allows ERB evaluation, you can even programatically enable/disable nodes based on host name. This example enables Scout APM on web1 through web5:

common: &defaults
  name: <%= "YOUR_APP_NAME (#{Rails.env})" %>
  key: YOUR_KEY
  log_level: info
  monitor: <%= Socket.gethostname.match(/web[1..5]/) %>

production:
  <<: *defaults

After you’ve disabled a node in your configuration file and restarted your app server, the node show up as inactive in the UI after 10 minutes.