appmap-ruby
is a Ruby Gem for recording
AppMaps of your code.
“AppMap” is a data format which records code structure (modules, classes, and methods), code execution events
(function calls and returns), and code metadata (repo name, repo URL, commit
SHA, labels, 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.
There are several ways to record AppMaps of your Ruby program using the appmap
gem:
APPMAP=true
. An AppMap will be generated for each spec.AppMap.record
block, which returns JSON containing the code execution trace.Once you have made a recording, there are two ways to view automatically generated diagrams of the AppMaps.
The first option is to load the diagrams directly in your IDE, using the AppMap extension for VSCode or the AppMap plugin for RubyMine.
The second option is to upload them to the AppMap server using the AppMap CLI.
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.
Add gem ‘appmap’
to the beginning of your Gemfile. It needs to be the first gem in the list to record other gems. 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 follows
gem 'rails', '6.1.0'
Install with bundle install
, as usual.
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/controllers
- path: app/models
# Exclude sub-paths within the package path
exclude:
- concerns/accessor
- path: app/jobs
- path: app/helpers
# Include the gems that you want to see in the dependency maps.
# These are just examples.
- gem: activerecord
- gem: devise
- gem: aws-sdk
- gem: will_paginate
# Global exclusion of a class name
exclude:
- MyClass
- MyClass#my_instance_method
- MyClass.my_class_method
# Application of labels
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
packages
Each entry in the packages
list is a YAML object which has the following keys:
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
list.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).
gem
or path
name needs to match the actual location of the method(s)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.
The AppMap data format provides for class and function labels
, which can be used to enhance the AppMap visualizations, and to programatically analyze the data.
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" ]
}
For non-Rails applications add the appmap/rspec
helper:
Require appmap/rspec
in your spec_helper.rb
before any other classes are loaded.
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
must be required before this:
require 'appmap/rspec'
require File.expand_path("../../config/environment", __FILE__)
Run the tests with the environment variable APPMAP=true
:
$ APPMAP=true 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
For non-Rails applications, add the appmap/minitest
helper:
Require appmap/minitest
in 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 with the environment variable APPMAP=true
. For example:
$ APPMAP=true bundle exec rake test
or
$ APPMAP=true bundle exec ruby -Ilib -Itest test/*_test.rb
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
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 with the environment variable APPMAP=true
:
$ APPMAP=true 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
appmap-ruby
supports the AppMap remote recording API.
This functionality is provided by a Rack middleware which is injected automatically into the middlewark stack of your Rails app.
To view the middleware stack of your app, and confirm that the AppMap middleware is configured and available, run:
APPMAP=true rake middleware
Note Without APPMAP=true
, the AppMap middleware will not be present, and remote recording is disabled. This is a safety feature
to prevent remote recording from being accidentally enabled.
To run your Rails application which remote recording enabled, start the server with APPMAP=true
. For example:
$ APPMAP=true bundle exec rails server
To make a remote recording, follow the Remote recording documentation.
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).
https://github.com/applandinc/appmap-ruby