appmap 0.40.0 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +25 -6
- data/lib/appmap/config.rb +35 -19
- data/lib/appmap/hook.rb +7 -0
- data/lib/appmap/version.rb +1 -1
- data/spec/abstract_controller_base_spec.rb +2 -2
- data/spec/config_spec.rb +1 -0
- data/spec/fixtures/hook/exclude.rb +15 -0
- data/spec/fixtures/rails5_users_app/appmap.yml +4 -1
- data/spec/fixtures/rails6_users_app/appmap.yml +4 -1
- data/spec/hook_spec.rb +11 -1
- data/spec/record_sql_rails_pg_spec.rb +1 -1
- data/spec/rspec_feature_metadata_spec.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d747c0e65dc1efb45769ae9118a7b97015b0378d79e20eec855e74c20b622af2
|
4
|
+
data.tar.gz: dcc3b91f32246aaefd02fbcf47672bfdf13acf6cadb715fbc5ff0040dbcfbf72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f82a6a0cf8075537b0835e783779c827c5afed81e83e242bc97cbfd5b6ef0320fe352235436f57009a63db321223b9f7f82e8bcdced558d9b935a2ce4e4cbf3
|
7
|
+
data.tar.gz: aaa4676d24b6bb1bcbd34fb7f0e3a0244d005c36dd89782b774b342b7e30e1c9637795d0d492522b2a5e525de413e3adb9da54bd5ace2699373ffdfadf3a8157
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -53,15 +53,23 @@ Support for new versions is added frequently, please check back regularly for up
|
|
53
53
|
<a href="https://www.loom.com/share/78ab32a312ff4b85aa8827a37f1cb655"> <p>Quick and easy setup of the AppMap gem for Rails - Watch Video</p> <img style="max-width:300px;" src="https://cdn.loom.com/sessions/thumbnails/78ab32a312ff4b85aa8827a37f1cb655-with-play.gif"> </a>
|
54
54
|
|
55
55
|
|
56
|
-
Add `gem 'appmap'` to your Gemfile
|
56
|
+
Add `gem 'appmap'` to **beginning** of your Gemfile. We recommend that you add the `appmap` gem to the `:development, :test` group. Your Gemfile should look something like this:
|
57
57
|
|
58
58
|
```
|
59
|
+
source 'https://rubygems.org'
|
60
|
+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
61
|
+
|
62
|
+
# Optional rubRuby version
|
63
|
+
# ruby '2.7.2'
|
64
|
+
|
59
65
|
group :development, :test do
|
60
66
|
gem 'appmap'
|
61
67
|
end
|
62
68
|
```
|
63
69
|
|
64
|
-
|
70
|
+
Install with `bundle install`, as usual.
|
71
|
+
|
72
|
+
It's important to add the `appmap` gem before any other gems that you may want to instrument. There is more about this in the section on adding gems to the *appmap.yml*.
|
65
73
|
|
66
74
|
**Railtie**
|
67
75
|
|
@@ -79,7 +87,8 @@ When you run your program, the `appmap` gem reads configuration settings from `a
|
|
79
87
|
file for a typical Rails project:
|
80
88
|
|
81
89
|
```yaml
|
82
|
-
name
|
90
|
+
# 'name' should generally be the same as the code repo name.
|
91
|
+
name: my_project
|
83
92
|
packages:
|
84
93
|
- path: app/controllers
|
85
94
|
- path: app/models
|
@@ -91,10 +100,15 @@ packages:
|
|
91
100
|
- gem: devise
|
92
101
|
- gem: aws-sdk
|
93
102
|
- gem: will_paginate
|
103
|
+
exclude:
|
104
|
+
- MyClass
|
105
|
+
- MyClass#my_instance_method
|
106
|
+
- MyClass.my_class_method
|
94
107
|
```
|
95
108
|
|
96
109
|
* **name** Provides the project name (required)
|
97
|
-
* **packages** A list of source code directories which should be
|
110
|
+
* **packages** A list of source code directories which should be recorded.
|
111
|
+
* **exclude** A list of classes and/or methods to definitively exclude from recording.
|
98
112
|
|
99
113
|
**packages**
|
100
114
|
|
@@ -102,13 +116,18 @@ Each entry in the `packages` list is a YAML object which has the following keys:
|
|
102
116
|
|
103
117
|
* **path** The path to the source code directory. The path may be relative to the current working directory, or it may
|
104
118
|
be an absolute path.
|
105
|
-
* **gem** As an alternative to specifying the path, specify the name of a dependency gem. When using `gem`, don't specify `path`.
|
119
|
+
* **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*.
|
106
120
|
* **exclude** A list of files and directories which will be ignored. By default, all modules, classes and public
|
107
|
-
functions are inspected.
|
121
|
+
functions are inspected. See also: global `exclude` list.
|
108
122
|
* **shallow** When set to `true`, only the first function call entry into a package will be recorded. Subsequent function calls within
|
109
123
|
the same package are not recorded unless code execution leaves the package and re-enters it. Default: `true` when using `gem`,
|
110
124
|
`false` when using `path`.
|
111
125
|
|
126
|
+
**exclude**
|
127
|
+
|
128
|
+
Optional list of fully qualified class and method names. Separate class and method names with period (`.`) for class methods and hash (`#`) for instance methods.
|
129
|
+
|
130
|
+
|
112
131
|
# Labels
|
113
132
|
|
114
133
|
The [AppMap data format](https://github.com/applandinc/appmap) provides for class and function `labels`, which can be used to enhance the AppMap visualizations, and to programatically analyze the data.
|
data/lib/appmap/config.rb
CHANGED
@@ -57,32 +57,32 @@ module AppMap
|
|
57
57
|
# Methods that should always be hooked, with their containing
|
58
58
|
# package and labels that should be applied to them.
|
59
59
|
HOOKED_METHODS = {
|
60
|
-
'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.build_from_path('active_support', package_name: 'active_support', labels: %w[
|
61
|
-
'ActionView::Renderer' => Hook.new(:render, Package.build_from_path('action_view', package_name: 'action_view', labels: %w[view]))
|
60
|
+
'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.build_from_path('active_support', package_name: 'active_support', labels: %w[provider.secure_compare])),
|
61
|
+
'ActionView::Renderer' => Hook.new(:render, Package.build_from_path('action_view', package_name: 'action_view', labels: %w[mvc.view]))
|
62
62
|
}.freeze
|
63
63
|
|
64
64
|
BUILTIN_METHODS = {
|
65
65
|
'OpenSSL::PKey::PKey' => Hook.new(:sign, OPENSSL_PACKAGES),
|
66
|
-
'Digest::Instance' => Hook.new(:digest, OPENSSL_PACKAGES),
|
67
66
|
'OpenSSL::X509::Request' => Hook.new(%i[sign verify], OPENSSL_PACKAGES),
|
68
67
|
'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGES),
|
69
68
|
'OpenSSL::Cipher' => Hook.new(%i[encrypt decrypt final], OPENSSL_PACKAGES),
|
70
69
|
'OpenSSL::X509::Certificate' => Hook.new(:sign, OPENSSL_PACKAGES),
|
71
|
-
'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[http io])),
|
72
|
-
'Net::SMTP' => Hook.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[smtp email io])),
|
73
|
-
'Net::POP3' => Hook.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[pop
|
74
|
-
'Net::IMAP' => Hook.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[imap email io])),
|
75
|
-
'Marshal' => Hook.new(%i[dump load], Package.build_from_path('marshal', labels: %w[serialization marshal])),
|
76
|
-
'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[serialization
|
77
|
-
'JSON::Ext::Parser' => Hook.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[serialization
|
78
|
-
'JSON::Ext::Generator::State' => Hook.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[serialization
|
70
|
+
'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[protocol.http io])),
|
71
|
+
'Net::SMTP' => Hook.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[protocol.smtp protocol.email io])),
|
72
|
+
'Net::POP3' => Hook.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[protocol.pop protocol.email io])),
|
73
|
+
'Net::IMAP' => Hook.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[protocol.imap protocol.email io])),
|
74
|
+
'Marshal' => Hook.new(%i[dump load], Package.build_from_path('marshal', labels: %w[format.marshal provider.serialization marshal])),
|
75
|
+
'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml provider.serialization])),
|
76
|
+
'JSON::Ext::Parser' => Hook.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json provider.serialization])),
|
77
|
+
'JSON::Ext::Generator::State' => Hook.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json provider.serialization])),
|
79
78
|
}.freeze
|
80
79
|
|
81
|
-
attr_reader :name, :packages
|
80
|
+
attr_reader :name, :packages, :exclude
|
82
81
|
|
83
|
-
def initialize(name, packages = [])
|
82
|
+
def initialize(name, packages = [], exclude = [])
|
84
83
|
@name = name
|
85
84
|
@packages = packages
|
85
|
+
@exclude = exclude
|
86
86
|
end
|
87
87
|
|
88
88
|
class << self
|
@@ -108,22 +108,30 @@ module AppMap
|
|
108
108
|
[ Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow']) ]
|
109
109
|
end
|
110
110
|
end.flatten
|
111
|
-
Config.new config_data['name'], packages
|
111
|
+
Config.new config_data['name'], packages, config_data['exclude'] || []
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
115
115
|
def to_h
|
116
116
|
{
|
117
117
|
name: name,
|
118
|
-
packages: packages.map(&:to_h)
|
118
|
+
packages: packages.map(&:to_h),
|
119
|
+
exclude: exclude
|
119
120
|
}
|
120
121
|
end
|
121
122
|
|
123
|
+
# package_for_method finds the Package, if any, which configures the hook
|
124
|
+
# for a method.
|
122
125
|
def package_for_method(method)
|
126
|
+
package_hooked_by_class(method) || package_hooked_by_source_location(method)
|
127
|
+
end
|
128
|
+
|
129
|
+
def package_hooked_by_class(method)
|
123
130
|
defined_class, _, method_name = ::AppMap::Hook.qualify_method_name(method)
|
124
|
-
|
125
|
-
|
131
|
+
return find_package(defined_class, method_name)
|
132
|
+
end
|
126
133
|
|
134
|
+
def package_hooked_by_source_location(method)
|
127
135
|
location = method.source_location
|
128
136
|
location_file, = location
|
129
137
|
return unless location_file
|
@@ -135,14 +143,22 @@ module AppMap
|
|
135
143
|
end
|
136
144
|
end
|
137
145
|
|
138
|
-
def
|
139
|
-
|
146
|
+
def never_hook?(method)
|
147
|
+
defined_class, separator, method_name = ::AppMap::Hook.qualify_method_name(method)
|
148
|
+
return true if exclude.member?(defined_class) || exclude.member?([ defined_class, separator, method_name ].join)
|
140
149
|
end
|
141
150
|
|
151
|
+
# always_hook? indicates a method that should always be hooked.
|
142
152
|
def always_hook?(defined_class, method_name)
|
143
153
|
!!find_package(defined_class, method_name)
|
144
154
|
end
|
145
155
|
|
156
|
+
# included_by_location? indicates a method whose source location matches a method definition that has been
|
157
|
+
# configured for inclusion.
|
158
|
+
def included_by_location?(method)
|
159
|
+
!!package_for_method(method)
|
160
|
+
end
|
161
|
+
|
146
162
|
def find_package(defined_class, method_name)
|
147
163
|
hook = find_hook(defined_class)
|
148
164
|
return nil unless hook
|
data/lib/appmap/hook.rb
CHANGED
@@ -63,6 +63,8 @@ module AppMap
|
|
63
63
|
# Skip methods that have no instruction sequence, as they are obviously trivial.
|
64
64
|
next unless disasm
|
65
65
|
|
66
|
+
next if config.never_hook?(method)
|
67
|
+
|
66
68
|
next unless \
|
67
69
|
config.always_hook?(hook_cls, method.name) ||
|
68
70
|
config.included_by_location?(method)
|
@@ -84,6 +86,8 @@ module AppMap
|
|
84
86
|
tp.enable(&block)
|
85
87
|
end
|
86
88
|
|
89
|
+
# hook_builtins builds hooks for code that is built in to the Ruby standard library.
|
90
|
+
# No TracePoint events are emitted for builtins, so a separate hooking mechanism is needed.
|
87
91
|
def hook_builtins
|
88
92
|
return unless self.class.lock_builtins
|
89
93
|
|
@@ -97,6 +101,7 @@ module AppMap
|
|
97
101
|
require hook.package.package_name if hook.package.package_name
|
98
102
|
Array(hook.method_names).each do |method_name|
|
99
103
|
method_name = method_name.to_sym
|
104
|
+
|
100
105
|
cls = class_from_string.(class_name)
|
101
106
|
method = \
|
102
107
|
begin
|
@@ -105,6 +110,8 @@ module AppMap
|
|
105
110
|
cls.method(method_name) rescue nil
|
106
111
|
end
|
107
112
|
|
113
|
+
next if config.never_hook?(method)
|
114
|
+
|
108
115
|
if method
|
109
116
|
Hook::Method.new(hook.package, cls, method).activate
|
110
117
|
else
|
data/lib/appmap/version.rb
CHANGED
@@ -8,7 +8,7 @@ describe 'AbstractControllerBase' do
|
|
8
8
|
FileUtils.rm_rf tmpdir
|
9
9
|
FileUtils.mkdir_p tmpdir
|
10
10
|
cmd = <<~CMD.gsub "\n", ' '
|
11
|
-
docker-compose run --rm -e APPMAP=true
|
11
|
+
docker-compose run --rm -e RAILS_ENV=test -e APPMAP=true
|
12
12
|
-v #{File.absolute_path tmpdir}:/app/tmp app ./bin/rspec #{spec_name}
|
13
13
|
CMD
|
14
14
|
run_cmd cmd, chdir: fixture_dir
|
@@ -137,7 +137,7 @@ describe 'AbstractControllerBase' do
|
|
137
137
|
'name' => 'Renderer',
|
138
138
|
'children' => include(hash_including(
|
139
139
|
'name' => 'render',
|
140
|
-
'labels' => ['view']
|
140
|
+
'labels' => ['mvc.view']
|
141
141
|
))
|
142
142
|
))
|
143
143
|
))
|
data/spec/config_spec.rb
CHANGED
data/spec/hook_spec.rb
CHANGED
@@ -61,6 +61,16 @@ describe 'AppMap class Hooking', docker: false do
|
|
61
61
|
AppMap.configuration = nil
|
62
62
|
end
|
63
63
|
|
64
|
+
it 'excludes named classes and methods' do
|
65
|
+
load 'spec/fixtures/hook/exclude.rb'
|
66
|
+
package = AppMap::Config::Package.build_from_path('spec/fixtures/hook/exclude.rb')
|
67
|
+
config = AppMap::Config.new('hook_spec', [ package ], %w[ExcludeTest])
|
68
|
+
AppMap.configuration = config
|
69
|
+
|
70
|
+
expect(config.never_hook?(ExcludeTest.new.method(:instance_method))).to be_truthy
|
71
|
+
expect(config.never_hook?(ExcludeTest.method(:cls_method))).to be_truthy
|
72
|
+
end
|
73
|
+
|
64
74
|
it 'parses labels from comments' do
|
65
75
|
_, tracer = invoke_test_file 'spec/fixtures/hook/labels.rb' do
|
66
76
|
ClassWithLabel.new.fn_with_label
|
@@ -827,7 +837,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
827
837
|
entry = cm[1][:children][0][:children][0][:children][0]
|
828
838
|
# Sanity check, make sure we got the right one
|
829
839
|
expect(entry[:name]).to eq('secure_compare')
|
830
|
-
expect(entry[:labels]).to eq(%w[
|
840
|
+
expect(entry[:labels]).to eq(%w[provider.secure_compare])
|
831
841
|
end
|
832
842
|
end
|
833
843
|
|
@@ -5,7 +5,7 @@ describe 'SQL events' do
|
|
5
5
|
around(:each) do |example|
|
6
6
|
FileUtils.rm_rf tmpdir
|
7
7
|
FileUtils.mkdir_p tmpdir
|
8
|
-
cmd = "docker-compose run --rm -e ORM_MODULE=#{orm_module} -e APPMAP=true -v #{File.absolute_path tmpdir}:/app/tmp app ./bin/rspec spec/controllers/users_controller_api_spec.rb:#{test_line_number}"
|
8
|
+
cmd = "docker-compose run --rm -e ORM_MODULE=#{orm_module} -e RAILS_ENV=test -e APPMAP=true -v #{File.absolute_path tmpdir}:/app/tmp app ./bin/rspec spec/controllers/users_controller_api_spec.rb:#{test_line_number}"
|
9
9
|
run_cmd cmd, chdir: fixture_dir
|
10
10
|
|
11
11
|
example.run
|
@@ -7,7 +7,7 @@ describe 'RSpec feature and feature group metadata' do
|
|
7
7
|
around(:each) do |example|
|
8
8
|
FileUtils.rm_rf tmpdir
|
9
9
|
FileUtils.mkdir_p tmpdir
|
10
|
-
cmd = "docker-compose run --rm -e APPMAP=true -v #{File.absolute_path(tmpdir).shellescape}:/app/tmp app ./bin/rspec spec/models/user_spec.rb"
|
10
|
+
cmd = "docker-compose run --rm -e RAILS_ENV=test -e APPMAP=true -v #{File.absolute_path(tmpdir).shellescape}:/app/tmp app ./bin/rspec spec/models/user_spec.rb"
|
11
11
|
run_cmd cmd, chdir: fixture_dir
|
12
12
|
|
13
13
|
example.run
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appmap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.41.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Gilpin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -386,6 +386,7 @@ files:
|
|
386
386
|
- spec/fixtures/hook/compare.rb
|
387
387
|
- spec/fixtures/hook/constructor.rb
|
388
388
|
- spec/fixtures/hook/exception_method.rb
|
389
|
+
- spec/fixtures/hook/exclude.rb
|
389
390
|
- spec/fixtures/hook/instance_method.rb
|
390
391
|
- spec/fixtures/hook/labels.rb
|
391
392
|
- spec/fixtures/hook/singleton_method.rb
|