AppMap Agent for Ruby

About

appmap is a Gem for recording AppMaps of your code.

The AppMap data format includes code structure (packages, modules, classes, and methods), trace events (function calls, web services, RPC calls, SQL, parameters, return values, exceptions, etc), and code metadata (repo URL, commit SHA, etc). It’s more granular than a performance profile, but it’s less granular than a full debug trace. It’s designed to be optimal for understanding the design intent and structure of code and key data flows.

Supported versions

Supported Rails versions: 5.x, 6.x, 7.x

Supported Ruby versions: 2.6, 2.7, 3.0, 3.1

Support for new versions is added frequently, please check back regularly for updates.

Installation

Add gem 'appmap' to the beginning of your Gemfile. It needs to be the first gem in the list, so that it can observe other gems loading and instrument them if you request it. Also, we recommend you add the appmap gem to the :development, :test group.

Your Gemfile should look something like this:

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

# Optional Ruby version
# ruby '2.7.2'

# The appmap gem is the first gem in the file
group :development, :test do
  gem 'appmap'
end

# The rest of the gems follow
gem 'rails', '6.1.0'

Install with bundle install, as usual.

Configuration

When you run your program, the appmap gem reads configuration settings from appmap.yml. Here’s an extensive example file for a Rails project:

# 'name' should generally be the same as the code repo name.
name: my_project
packages:
- path: app
  # Exclude sub-paths within the package path
  exclude:
  - concerns/accessor
- path :lib
# Include any gems that you want to record.
# These are just examples.
- gem: activerecord
- gem: devise
- gem: aws-sdk
- gem: will_paginate
# Exclude a specific class or function from the map.
exclude:
- MyClass
- MyClass#my_instance_method
- MyClass.my_class_method
# Apply custom labels to gem code.
# (Use source annotations to label your own code).
functions:
- methods:
  - Fluent::Logger::FluentLogger#post
  - Fluent::Logger::FluentLogger#post_with_time
  - Fluent::Logger.post
  - Fluent::Logger.post_with_time
  gem: fluent-logger
  label: log
  • name Provides the project name (required)
  • packages A list of source code directories which should be recorded.
  • exclude A list of classes and/or methods to definitively exclude from recording.
  • functions A list of specific functions, scoped by gem or path and fully qualified method name, to record.

packages

Each entry in the packages list is a YAML object which has the following keys:

  • path The path to the source code directory. The path may be relative to the current working directory, or it may be an absolute path.
  • gem As an alternative to specifying the path, specify the name of a dependency gem. When using gem, don’t specify path. In your Gemfile, the appmap gem must be listed before any gem that you specify in your appmap.yml.
  • exclude A list of files and directories which will be ignored. By default, all modules, classes and public functions are inspected. See also: global exclude list.
  • shallow When set to true, only the first function call entry into a package will be recorded. Subsequent function calls within the same package are not recorded unless code execution leaves the package and re-enters it. Default: true when using gem, false when using path.

exclude

Optional list of fully qualified class and method names. Separate class and method names with period (.) for class methods and hash (#) for instance methods.

functions

Optional list of fully qualified Class#instance_method or Class.class_method method names. The primary use of functions is to apply specific labels to functions whose source code is not accessible (e.g., it’s in a Gem).

  • The gem or path name needs to match the actual location of the method(s)
  • You can specify an individual method or multiple methods and a single label or mutiple labels.

For functions which are part of the application code, use @label or @labels in code comments to apply labels.

Labels

The AppMap data specification provides for class and function labels, which can be used to enhance the AppMap visualizations, and for code analysis.

You can apply function labels using source code comments in your Ruby code. To apply a labels to a function, add a @label or @labels line to the comment which immediately precedes a function.

For example, if you add this comment to your source code:

class ApiKey
  # @labels provider.authentication security
  def authenticate(key)
    # logic to verify the key here...
  end
end

Then the AppMap metadata section for this function will include:

{
  "name": "authenticate",
  "type": "function",
  "labels": [ "provider.authentication", "security" ]
}

Tests recording

RSpec

When you run your RSpec tests, AppMaps will be recorded automatically (as long as the appmap gem is loaded).

If you don’t see this working automatically for some reason, you can try requiring appmap/rspec in your spec_helper.rb:

require 'appmap/rspec'

Note that spec_helper.rb in a Rails project typically loads the application’s classes this way:

require File.expand_path("../../config/environment", __FILE__)

and appmap/rspec should be required before this:

require 'appmap/rspec'
require File.expand_path("../../config/environment", __FILE__)

 

Run the tests:

$ bundle exec rspec

Each RSpec test will output an AppMap file into the directory tmp/appmap/rspec. For example:

$ find tmp/appmap/rspec
Hello_says_hello_when_prompted.appmap.json

Minitest

When you run your Minitest tests, AppMaps will be recorded automatically (as long as the appmap gem is loaded).

If you don’t see this working automatically for some reason, you can try requiring appmap/minitest in your test_helper.rb:

require 'appmap/minitest'

Note that test_helper.rb in a Rails project typically loads the application’s classes this way:

require_relative '../config/environment'

and appmap/minitest must be required before this:

require 'appmap/minitest'
require_relative '../config/environment'

 

Run your tests as you normally would. For example:

$ rails test

or

$ bundle exec rake test

Each Minitest test will output an AppMap file into the directory tmp/appmap/minitest. For example:

$ find tmp/appmap/minitest
Hello_says_hello_when_prompted.appmap.json

Cucumber

To record Cucumber tests, follow these additional steps:

1) Require appmap/cucumber in support/env.rb:

require 'appmap/cucumber'

Be sure to require it before config/environment is required.

2) Create an Around hook in support/hooks.rb to record the scenario:

if AppMap::Cucumber.enabled?
  Around('not @appmap-disable') do |scenario, block|
    appmap = AppMap.record do
      block.call
    end

    AppMap::Cucumber.write_scenario(scenario, appmap)
  end
end

3) Run the tests:

$ bundle exec cucumber

Each Cucumber test will output an AppMap file into the directory tmp/appmap/cucumber. For example:

$ find tmp/appmap/cucumber
Hello_Says_hello_when_prompted.appmap.json

Requests recording

appmap-ruby can automatically record and save an AppMap for each HTTP server request. This functionality is provided by a Rack middleware.

Requests recording in Rails

The appmap middleware is injected automatically into the middleware stack of your Rails app by the AppMap Railtie. To view the middleware stack of your app, and confirm that the AppMap middleware is configured and available, run:

rake middleware

Start your Rails server in the normal way:

$ rails server

If you don’t see a message indicating that “requests” recording is enbled, start the server with APPMAP_RECORD_REQUESTS=true, or ensure that RAILS_ENV=development.

Requests recording in non-Rails apps

For non-Rails apps, add the AppMap middleware using the method provided by the framemwork you’re using. Run your application server in the usual way. If you don’t see a message indicating that “requests” recording is enbled, start the server with APPMAP_RECORD_REQUESTS=true, or ensure that APP_ENV=development.

Remote recording

appmap-ruby supports the AppMap remote recording API. This functionality is provided by a Rack middleware.

Remote recording is enabled automatically when Rails.env == 'development' or ENV['APP_ENV'] == 'development. To enable remote recording in any other environment, set APPMAP_RECORD_REMOTE=true.

To make a remote recording, follow the Remote recording documentation.

Remote recording in Rails

The appmap middleware is injected automatically into the middleware stack of your Rails app by the AppMap Railtie. To view the middleware stack of your app, and confirm that the AppMap middleware is configured and available, run:

rake middleware

Start your Rails server in the normal way:

$ rails server

If you don’t see a message indicating that “remote” recording is enbled, start the server with APPMAP_RECORD_REMOTE=true, or ensure that RAILS_ENV=development.

Remote recording in non-Rails apps

For non-Rails apps, add the AppMap middleware using the method provided by the framemwork you’re using. Run your application server in the usual way. If you don’t see a message indicating that “remote” recording is enbled, start the server with APPMAP_RECORD_REMOTE=true, or ensure that APP_ENV=development.

Enabling and disabling instrumentation

Bundler loads the appmap code when you load a gemset it’s defined in (by default, test and development). When the appmap code is loaded, it will instrument the other Ruby code according to the settings in your appmap.yml file.

If you don’t want appmap code to be loaded automatically by Bundler, you can conditionally disable it like this:

# The appmap gem is the first gem in the file
gem 'appmap', groups: %i[development test], require: ENV['APPMAP'] == 'true'

In this example, the appmap code will only be loaded when environment variable APPMAP=true. If you prevent bundler from loading appmap, you can load it later on by using require in the usual way. For example, in Rails application.rb:

require 'appmap'

Advanced runtime options

APPMAP_PROFILE_HOOK

appmap-ruby inspects and instruments code as it’s loaded by the Ruby virtual machine. Start your program with APPMAP_PROFILE_HOOK=true to see diagnostic information about how much time it’s taking appmap-ruby to instrument each Gem in your appmap.yml.

APPMAP_PROFILE_DISPLAY_STRING

appmap-ruby tries to include a useful string representation of each parameter and return value in the AppMap. In some cases, there are pathological classes that take a long time to stringify. Start your program with APPMAP_PROFILE_DISPLAY_STRING=true to see diagnostic information about how much time it’s taking appmap-ruby to stringify different object classes.

APPMAP_GEM_HOOKS_PATH

appmap-ruby ships with a default set of hooks that instrument and label popular Gems. The default list of hooks can be extended with custom configuration so that Gems used in your application do not have to be explicitly included and labeled in each appmap.yml. Set APPMAP_GEM_HOOKS_PATH to a folder with your custom hooks configuration yaml files that use the appmap.yml/functions syntax. See the AppMap default hooks configuration example (note: Gem names can be inferred from configuration file names if the gem property is missing).

APPMAP_RECORD_TESTS

When true, test case recording is always enabled. When false, it’s always disabled.

APPMAP_RECORD_REQUESTS

When true, requests recording is always enabled. When false, it’s always disabled.

APPMAP_RECORD_REMOTE

When true, remote recording is always enabled. When false, it’s always disabled.

APPMAP

When false, disable all recording.

GitHub repository

https://github.com/getappmap/appmap-ruby


Was this page helpful? thumb_up Yes thumb_down No
Thank you for your feedback!