appmap 0.78.0 → 0.80.1
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/.travis.yml +4 -16
- data/CHANGELOG.md +30 -0
- data/{spec/fixtures/rails5_users_app/Dockerfile.pg → Dockerfile.pg} +0 -0
- data/README.md +14 -44
- data/README_CI.md +0 -7
- data/Rakefile +12 -150
- data/appmap.gemspec +3 -2
- data/docker-compose.yml +10 -0
- data/lib/appmap/agent.rb +8 -0
- data/lib/appmap/event.rb +26 -12
- data/lib/appmap/gem_hooks/actionpack.yml +6 -0
- data/lib/appmap/handler/rails/render_handler.rb +29 -0
- data/lib/appmap/handler/rails/request_handler.rb +13 -11
- data/lib/appmap/handler/rails/sql_handler.rb +43 -1
- data/lib/appmap/handler.rb +3 -0
- data/lib/appmap/hook/method.rb +0 -1
- data/lib/appmap/version.rb +1 -1
- data/spec/config_spec.rb +1 -1
- data/spec/depends/api_spec.rb +13 -5
- data/spec/depends/spec_helper.rb +0 -9
- data/spec/fixtures/database.yml +11 -0
- data/spec/fixtures/rails5_users_app/config/database.yml +1 -0
- data/spec/fixtures/rails6_users_app/Gemfile +1 -25
- data/spec/fixtures/rails6_users_app/config/database.yml +1 -0
- data/spec/fixtures/rails7_users_app/Gemfile +1 -25
- data/spec/fixtures/rails7_users_app/config/database.yml +1 -0
- data/spec/handler/eval_spec.rb +1 -1
- data/spec/hook_spec.rb +6 -1
- data/spec/rails_recording_spec.rb +7 -21
- data/spec/rails_spec_helper.rb +76 -63
- data/spec/rails_test_spec.rb +7 -17
- data/spec/railtie_spec.rb +4 -18
- data/spec/record_sql_rails_pg_spec.rb +44 -75
- data/spec/remote_recording_spec.rb +18 -30
- data/spec/spec_helper.rb +1 -0
- data/spec/swagger/swagger_spec.rb +1 -16
- data/spec/util_spec.rb +1 -1
- data/test/expectations/openssl_test_key_sign2-3.1.json +2 -1
- metadata +22 -21
- data/Dockerfile.appmap +0 -5
- data/spec/fixtures/rack_users_app/Dockerfile +0 -32
- data/spec/fixtures/rack_users_app/docker-compose.yml +0 -9
- data/spec/fixtures/rails5_users_app/Dockerfile +0 -29
- data/spec/fixtures/rails5_users_app/config/database.yml +0 -18
- data/spec/fixtures/rails5_users_app/create_app +0 -33
- data/spec/fixtures/rails5_users_app/docker-compose.yml +0 -31
- data/spec/fixtures/rails6_users_app/.ruby-version +0 -1
- data/spec/fixtures/rails6_users_app/Dockerfile +0 -44
- data/spec/fixtures/rails6_users_app/Dockerfile.pg +0 -3
- data/spec/fixtures/rails6_users_app/config/database.yml +0 -18
- data/spec/fixtures/rails6_users_app/create_app +0 -33
- data/spec/fixtures/rails6_users_app/docker-compose.yml +0 -31
- data/spec/fixtures/rails7_users_app/.ruby-version +0 -1
- data/spec/fixtures/rails7_users_app/Dockerfile +0 -30
- data/spec/fixtures/rails7_users_app/Dockerfile.pg +0 -3
- data/spec/fixtures/rails7_users_app/config/database.yml +0 -86
- data/spec/fixtures/rails7_users_app/create_app +0 -31
- data/spec/fixtures/rails7_users_app/docker-compose.yml +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de1e5a636c3d6e807f11c54a29563e338804f3d101dc4779229596455cd60682
|
4
|
+
data.tar.gz: cf2aba805fdb427da2a642826f490b0002eb1892dd57a07693b119582a8cd7e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6056dda497e69c32a6e7550c76b4b4e40a5cd5405d7244748af4f5f34ed7d6790eafa206470ac8c9a98019fec802944b4cec0cc274451028cf2c5d6f405bd626
|
7
|
+
data.tar.gz: '039ef96a6ae64cf641e58fb364a2777a65dc507ded48027752ca766c65204659b844cb6408d17a077947968d1fbf006f7c9b29e2fdfe03fc392d90c4495361ff'
|
data/.travis.yml
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
language: ruby
|
2
2
|
dist: bionic
|
3
|
+
cache:
|
4
|
+
- bundle
|
5
|
+
- yarn
|
3
6
|
|
4
7
|
# NB: if you update the ruby versions here, you also need to update
|
5
8
|
# them in the Rakefile.
|
@@ -9,25 +12,10 @@ rbenv:
|
|
9
12
|
- 3.0
|
10
13
|
- 3.1
|
11
14
|
|
12
|
-
addons:
|
13
|
-
apt:
|
14
|
-
packages:
|
15
|
-
# https://docs.travis-ci.com/user/docker/#installing-a-newer-docker-version
|
16
|
-
- docker-ce
|
17
|
-
|
18
15
|
services:
|
19
|
-
-
|
16
|
+
- postgresql
|
20
17
|
|
21
18
|
before_install:
|
22
|
-
- sudo apt-get update && sudo apt-get install apt-transport-https ca-certificates -y && sudo update-ca-certificates
|
23
|
-
# see https://blog.travis-ci.com/docker-rate-limits
|
24
|
-
# and also https://www.docker.com/blog/what-you-need-to-know-about-upcoming-docker-hub-rate-limiting/
|
25
|
-
# if we do not use authorized account,
|
26
|
-
# the pulls-per-IP quota is shared with other Travis users
|
27
|
-
- >
|
28
|
-
if [ ! -z "$DOCKERHUB_PASSWORD" ] && [ ! -z "$DOCKERHUB_USERNAME" ]; then
|
29
|
-
echo "$DOCKERHUB_PASSWORD" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin ;
|
30
|
-
fi
|
31
19
|
- |
|
32
20
|
nvm install --lts \
|
33
21
|
&& nvm use --lts \
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
## [0.80.1](https://github.com/applandinc/appmap-ruby/compare/v0.80.0...v0.80.1) (2022-04-08)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Don't call #size on complex objects ([3f19d1e](https://github.com/applandinc/appmap-ruby/commit/3f19d1e67288379570dfa14d8758a0624d2c6c34))
|
7
|
+
|
8
|
+
# [0.80.0](https://github.com/applandinc/appmap-ruby/compare/v0.79.0...v0.80.0) (2022-04-08)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* Don't record SQL within an existing event ([ff37a69](https://github.com/applandinc/appmap-ruby/commit/ff37a69af1af02263df216e49aea0d0954b93925))
|
14
|
+
|
15
|
+
|
16
|
+
### Features
|
17
|
+
|
18
|
+
* Env var to EXPLAIN queries ([740be75](https://github.com/applandinc/appmap-ruby/commit/740be75c2bc59e343d67ecf86b7715e61cddadba))
|
19
|
+
* Optionally record parameter schema ([b7f41b1](https://github.com/applandinc/appmap-ruby/commit/b7f41b15a4556a0ce78650a6a77301d365632bb8))
|
20
|
+
* query_plan is available whether a current transaction exists or not ([6edf774](https://github.com/applandinc/appmap-ruby/commit/6edf774fea858d825c4b971be2c4c15db1652446))
|
21
|
+
* Record parameter and return value size ([6e69754](https://github.com/applandinc/appmap-ruby/commit/6e697543cb421378832492e286f972dc4cb1e1aa))
|
22
|
+
* Save render return value to a thread-local ([f9d1e3f](https://github.com/applandinc/appmap-ruby/commit/f9d1e3f0aa9972482ff77233d38220104515b1d6))
|
23
|
+
|
24
|
+
# [0.79.0](https://github.com/applandinc/appmap-ruby/compare/v0.78.0...v0.79.0) (2022-04-06)
|
25
|
+
|
26
|
+
|
27
|
+
### Features
|
28
|
+
|
29
|
+
* Use a more unique test database name ([0eed036](https://github.com/applandinc/appmap-ruby/commit/0eed036460f0384698ff91c1112a4a9c3214f7f4))
|
30
|
+
|
1
31
|
# [0.78.0](https://github.com/applandinc/appmap-ruby/compare/v0.77.4...v0.78.0) (2022-04-04)
|
2
32
|
|
3
33
|
|
File without changes
|
data/README.md
CHANGED
@@ -28,7 +28,7 @@ Visit the [AppMap for Ruby](https://appland.com/docs/reference/appmap-ruby.html)
|
|
28
28
|
|
29
29
|
**Configuration**
|
30
30
|
|
31
|
-
*appmap.yml* is loaded into an `AppMap::Config`.
|
31
|
+
*appmap.yml* is loaded into an `AppMap::Config`.
|
32
32
|
|
33
33
|
**Hooking**
|
34
34
|
|
@@ -39,7 +39,7 @@ method with calls that record the parameters and return value.
|
|
39
39
|
**Builtins**
|
40
40
|
|
41
41
|
`Hook` begins by iterating over builtin classes and modules defined in the `Config`. Builtins include code
|
42
|
-
like `openssl` and `net/http`. This code is not dependent on any external libraries being present, and
|
42
|
+
like `openssl` and `net/http`. This code is not dependent on any external libraries being present, and
|
43
43
|
`appmap` cannot guarantee that it will be loaded before builtins. Therefore, it's necessary to require it and
|
44
44
|
hook it by looking up the classes and modules as constants in the `Object` namespace.
|
45
45
|
|
@@ -81,50 +81,20 @@ The fixture apps in `test/fixtures` are plain Ruby projects that exercise the ba
|
|
81
81
|
|
82
82
|
The fixture apps in `spec/fixtures` are simple Rack, Rails5, and Rails6 apps.
|
83
83
|
You can use them to interactively develop and test the recording features of the `appmap` gem.
|
84
|
-
These fixture apps are more sophisticated than `test/fixtures`, because they include additional
|
85
|
-
resources such as a PostgreSQL database.
|
84
|
+
These fixture apps are more sophisticated than `test/fixtures`, because they include additional
|
85
|
+
resources such as a PostgreSQL database. Still, you can simply enter the fixture directory and `bundle`.
|
86
86
|
|
87
|
-
|
87
|
+
If you don't have PostgreSQL on the local (default) socket, you can export `DATABASE_URL` to
|
88
|
+
point to the database server you want to use.
|
88
89
|
|
89
|
-
|
90
|
-
$ bundle exec rake build:fixtures:all
|
91
|
-
```
|
92
|
-
|
93
|
-
This will build the `appmap.gem`, along with a Docker image for each fixture app.
|
94
|
-
|
95
|
-
Then move to the directory of the fixture you want to use, and provision the environment.
|
96
|
-
In this example, we use Ruby 2.6.
|
97
|
-
|
98
|
-
```sh-session
|
99
|
-
$ export RUBY_VERSION=2.6
|
100
|
-
$ docker-compose up -d pg
|
101
|
-
$ sleep 10s # Or some reasonable amount of time
|
102
|
-
$ docker-compose run --rm app ./create_app
|
103
|
-
```
|
90
|
+
You can launch a database like this:
|
104
91
|
|
105
|
-
Now you can start a development container.
|
106
|
-
|
107
|
-
```sh-session
|
108
|
-
$ docker-compose run --rm -v $PWD:/app -v $PWD/../../..:/src/appmap-ruby app bash
|
109
|
-
Starting rails_users_app_pg_1 ... done
|
110
|
-
root@6fab5f89125f:/app# cd /src/appmap-ruby
|
111
|
-
root@6fab5f89125f:/src/appmap-ruby# rm ext/appmap/*.so ext/appmap/*.o
|
112
|
-
root@6fab5f89125f:/src/appmap-ruby# bundle
|
113
|
-
root@6fab5f89125f:/src/appmap-ruby# bundle exec rake compile
|
114
|
-
root@6fab5f89125f:/src/appmap-ruby# cd /src/app
|
115
|
-
root@6fab5f89125f:/src/app# bundle config local.appmap /src/appmap-ruby
|
116
|
-
root@6fab5f89125f:/src/app# bundle
|
117
92
|
```
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
Configuring AppMap from path appmap.yml
|
126
|
-
....
|
127
|
-
|
128
|
-
Finished in 0.07357 seconds (files took 2.1 seconds to load)
|
129
|
-
4 examples, 0 failures
|
93
|
+
➜ docker-compose -p appmap-ruby up -d
|
94
|
+
... stuff
|
95
|
+
➜ docker-compose ps pg
|
96
|
+
Name Command State Ports
|
97
|
+
-----------------------------------------------------------------------------------------
|
98
|
+
appmap-ruby_pg_1 docker-entrypoint.sh postgres Up (healthy) 0.0.0.0:59134->5432/tcp
|
99
|
+
➜ export DATABASE_URL=postgres://postgres@localhost:59134
|
130
100
|
```
|
data/README_CI.md
CHANGED
@@ -4,13 +4,6 @@
|
|
4
4
|
* `GEM_HOST_API_KEY`: rubygems API key
|
5
5
|
* `GEM_ALTERNATIVE_NAME` (optional): used for testing of CI flows,
|
6
6
|
to avoid publication of test releases under official package name
|
7
|
-
* `DOCKERHUB\_USERNAME`, `DOCKERHUB_PASSWORD`: optional dockerhub credentials,
|
8
|
-
to avoid throttling of dockerhub anonymous pulls
|
9
|
-
|
10
|
-
Note: for security reasons, it's better to use dedicated (not personal)
|
11
|
-
Dockerhub account,
|
12
|
-
and also use [access tokens](https://docs.docker.com/docker-hub/access-tokens/)
|
13
|
-
instead of primary password
|
14
7
|
|
15
8
|
# Release command
|
16
9
|
|
data/Rakefile
CHANGED
@@ -1,26 +1,8 @@
|
|
1
1
|
$: << File.join(__dir__, 'lib')
|
2
|
-
require 'appmap/version'
|
3
|
-
GEM_VERSION = AppMap::VERSION
|
4
|
-
|
5
|
-
# Make sure the local version is not behind the one on
|
6
|
-
# rubygems.org (it's ok if they're the same).
|
7
|
-
#
|
8
|
-
# If it is behind, the fixture images won't get updated with the gem
|
9
|
-
# built from the local source, so you'll wind up testing the rubygems
|
10
|
-
# version instead.
|
11
|
-
unless ENV['SKIP_VERSION_CHECK']
|
12
|
-
require 'json'
|
13
|
-
require 'net/http'
|
14
|
-
rubygems_version = JSON.parse(Net::HTTP.get(URI.parse('https://rubygems.org/api/v1/gems/appmap.json')))['version']
|
15
|
-
if Gem::Version.new(GEM_VERSION) < Gem::Version.new(rubygems_version)
|
16
|
-
raise "#{GEM_VERSION} < #{rubygems_version}. Rebase to avoid build issues."
|
17
|
-
end
|
18
|
-
end
|
19
2
|
|
3
|
+
require 'rspec/core/rake_task'
|
20
4
|
require 'rake/testtask'
|
21
5
|
require 'rdoc/task'
|
22
|
-
|
23
|
-
require 'open3'
|
24
6
|
require 'rake/extensiontask'
|
25
7
|
|
26
8
|
desc 'build the native extension'
|
@@ -28,138 +10,20 @@ Rake::ExtensionTask.new("appmap") do |ext|
|
|
28
10
|
ext.lib_dir = "lib/appmap"
|
29
11
|
end
|
30
12
|
|
31
|
-
RUBY_VERSIONS=%w[2.6 2.7 3.0 3.1].select do |version|
|
32
|
-
travis_ruby_version = ENV['TRAVIS_RUBY_VERSION']
|
33
|
-
next true unless travis_ruby_version
|
34
|
-
|
35
|
-
if travis_ruby_version.index(version) == 0
|
36
|
-
warn "Testing Ruby version #{version}, since it matches TRAVIS_RUBY_VERSION=#{travis_ruby_version}"
|
37
|
-
next true
|
38
|
-
end
|
39
|
-
|
40
|
-
false
|
41
|
-
end
|
42
|
-
FIXTURE_APPS=[:rack_users_app, :rails6_users_app, :rails5_users_app, :rails7_users_app => {:ruby_version => '>= 2.7'}]
|
43
|
-
|
44
|
-
def run_cmd(*cmd)
|
45
|
-
$stderr.puts "Running: #{cmd}"
|
46
|
-
out, s = Open3.capture2e(*cmd)
|
47
|
-
unless s.success?
|
48
|
-
$stderr.puts <<-END
|
49
|
-
Command failed:
|
50
|
-
<<< Output:
|
51
|
-
#{out}
|
52
|
-
>>> End of output
|
53
|
-
END
|
54
|
-
raise 'Docker build failed'
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def build_base_image(ruby_version)
|
59
|
-
run_cmd "docker build" \
|
60
|
-
" --build-arg RUBY_VERSION=#{ruby_version}" \
|
61
|
-
" --build-arg GEM_VERSION=#{GEM_VERSION}" \
|
62
|
-
" -t appmap:#{GEM_VERSION} -f Dockerfile.appmap ."
|
63
|
-
end
|
64
|
-
|
65
|
-
def build_app_image(app, ruby_version)
|
66
|
-
Dir.chdir "spec/fixtures/#{app}" do
|
67
|
-
env = {"RUBY_VERSION" => ruby_version, "GEM_VERSION" => GEM_VERSION}
|
68
|
-
run_cmd(env,
|
69
|
-
"docker-compose build" \
|
70
|
-
" --build-arg RUBY_VERSION=#{ruby_version}" \
|
71
|
-
" --build-arg GEM_VERSION=#{GEM_VERSION}" )
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
13
|
desc 'Install non-Ruby dependencies'
|
76
14
|
task :install do
|
77
15
|
system 'yarn install' or raise 'yarn install failed'
|
78
16
|
end
|
79
17
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
build_base_image(ruby_version)
|
90
|
-
end.tap do |t|
|
91
|
-
desc "Build all images"
|
92
|
-
task all: t
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
namespace :fixtures do
|
98
|
-
RUBY_VERSIONS.each do |ruby_version|
|
99
|
-
namespace ruby_version do
|
100
|
-
desc "build:fixtures:#{ruby_version}"
|
101
|
-
FIXTURE_APPS.each do |app_spec|
|
102
|
-
app = if app_spec.instance_of?(Hash)
|
103
|
-
app_spec = app_spec.flatten
|
104
|
-
version_rqt = Gem::Requirement.create(app_spec[1][:ruby_version])
|
105
|
-
next unless version_rqt =~ (Gem::Version.new(ruby_version))
|
106
|
-
app = app_spec[0]
|
107
|
-
else
|
108
|
-
app = app_spec
|
109
|
-
end.to_s
|
110
|
-
|
111
|
-
|
112
|
-
desc app
|
113
|
-
task app => ["base:#{ruby_version}"] do
|
114
|
-
build_app_image(app, ruby_version)
|
115
|
-
end.tap do |t|
|
116
|
-
desc "Build all fixture images for #{ruby_version}"
|
117
|
-
task all: t
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
desc "Build all fixture images"
|
123
|
-
task all: ["#{ruby_version}:all"]
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
task all: ["fixtures:all"]
|
128
|
-
end
|
129
|
-
|
130
|
-
def run_specs(ruby_version, task_args)
|
131
|
-
require 'rspec/core/rake_task'
|
132
|
-
require 'climate_control'
|
133
|
-
# Define an rspec rake task for the specified Ruby version. It's hidden (i.e. doesn't have a
|
134
|
-
# description), because it's not intended to be invoked directly
|
135
|
-
RSpec::Core::RakeTask.new("rspec_#{ruby_version}", [:specs]) do |task, args|
|
136
|
-
task.exclude_pattern = 'spec/fixtures/**/*_spec.rb'
|
137
|
-
task.rspec_opts = '-f doc'
|
138
|
-
if args.count > 0
|
139
|
-
# There doesn't appear to be a value for +pattern+ that will
|
140
|
-
# cause it to be ignored. Setting it to '' or +nil+ causes an
|
141
|
-
# empty argument to get passed to rspec, which confuses it.
|
142
|
-
task.pattern = 'never match this'
|
143
|
-
task.rspec_opts += " " + args.to_a.join(' ')
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
# Set up the environment, then execute the rspec task we
|
148
|
-
# created above.
|
149
|
-
ClimateControl.modify(RUBY_VERSION: ruby_version) do
|
150
|
-
Rake::Task["rspec_#{ruby_version}"].execute(task_args)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
namespace :spec do
|
155
|
-
RUBY_VERSIONS.each do |ruby_version|
|
156
|
-
desc ruby_version
|
157
|
-
task ruby_version, [:specs] => ["install", "compile", "build:fixtures:#{ruby_version}:all"] do |_, task_args|
|
158
|
-
run_specs(ruby_version, task_args)
|
159
|
-
end.tap do |t|
|
160
|
-
desc "Run all specs"
|
161
|
-
task :all, [:specs] => t
|
162
|
-
end
|
18
|
+
RSpec::Core::RakeTask.new spec: %i[compile install] do |task, args|
|
19
|
+
task.exclude_pattern = 'spec/fixtures/**/*_spec.rb'
|
20
|
+
task.rspec_opts = '-f doc'
|
21
|
+
if args.count > 0
|
22
|
+
# There doesn't appear to be a value for +pattern+ that will
|
23
|
+
# cause it to be ignored. Setting it to '' or +nil+ causes an
|
24
|
+
# empty argument to get passed to rspec, which confuses it.
|
25
|
+
task.pattern = 'never match this'
|
26
|
+
task.rspec_opts += [nil, *args].join(' ')
|
163
27
|
end
|
164
28
|
end
|
165
29
|
|
@@ -169,14 +33,12 @@ Rake::RDocTask.new do |rd|
|
|
169
33
|
rd.title = 'AppMap'
|
170
34
|
end
|
171
35
|
|
172
|
-
Rake::TestTask.new(minitest:
|
36
|
+
Rake::TestTask.new(minitest: :compile) do |t|
|
173
37
|
t.libs << 'test'
|
174
38
|
t.libs << 'lib'
|
175
39
|
t.test_files = FileList['test/*_test.rb']
|
176
40
|
end
|
177
41
|
|
178
|
-
task
|
179
|
-
|
180
|
-
task test: %i[spec:all minitest]
|
42
|
+
task test: %i[spec minitest]
|
181
43
|
|
182
44
|
task default: :test
|
data/appmap.gemspec
CHANGED
@@ -5,7 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
require 'appmap/version'
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
|
-
# ability to parameterize gem name is added intentionally,
|
8
|
+
# ability to parameterize gem name is added intentionally,
|
9
9
|
# to support the possibility of unofficial releases, e.g. during CI tests
|
10
10
|
spec.name = (ENV['GEM_ALTERNATIVE_NAME'].to_s.empty? ? 'appmap' : ENV["GEM_ALTERNATIVE_NAME"] )
|
11
11
|
spec.version = AppMap::VERSION
|
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_dependency 'reverse_markdown'
|
33
33
|
|
34
34
|
spec.add_development_dependency 'bundler', '>= 1.16'
|
35
|
-
spec.add_development_dependency 'minitest', '~> 5.
|
35
|
+
spec.add_development_dependency 'minitest', '~> 5.15'
|
36
36
|
spec.add_development_dependency 'pry-byebug'
|
37
37
|
spec.add_development_dependency 'rake', '>= 12.3.3'
|
38
38
|
spec.add_development_dependency 'rdoc'
|
@@ -48,5 +48,6 @@ Gem::Specification.new do |spec|
|
|
48
48
|
spec.add_development_dependency 'webdrivers', '~> 4.0'
|
49
49
|
spec.add_development_dependency 'timecop'
|
50
50
|
spec.add_development_dependency 'hashie'
|
51
|
+
spec.add_development_dependency 'random-port', '~> 0.5.1'
|
51
52
|
spec.add_development_dependency 'webrick'
|
52
53
|
end
|
data/docker-compose.yml
ADDED
data/lib/appmap/agent.rb
CHANGED
@@ -100,5 +100,13 @@ module AppMap
|
|
100
100
|
@metadata ||= Metadata.detect.freeze
|
101
101
|
Util.deep_dup(@metadata)
|
102
102
|
end
|
103
|
+
|
104
|
+
def parameter_schema?
|
105
|
+
ENV['APPMAP_PARAMETER_SCHEMA'] == 'true'
|
106
|
+
end
|
107
|
+
|
108
|
+
def explain_queries?
|
109
|
+
ENV['APPMAP_EXPLAIN_QUERIES'] == 'true'
|
110
|
+
end
|
103
111
|
end
|
104
112
|
end
|
data/lib/appmap/event.rb
CHANGED
@@ -60,16 +60,25 @@ module AppMap
|
|
60
60
|
final ? value_string : encode_display_string(value_string)
|
61
61
|
end
|
62
62
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
def add_size(param, value)
|
64
|
+
# Don't risk calling #size on things like data-access objects, which can and will issue queries for this information.
|
65
|
+
if value.is_a?(Array) || value.is_a?(Hash)
|
66
|
+
param[:size] = value.size
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_schema(param, value)
|
71
|
+
begin
|
72
|
+
if value.respond_to?(:keys)
|
73
|
+
param[:properties] = value.keys.map { |key| { name: key, class: best_class_name(value[key]) } }
|
74
|
+
elsif value.respond_to?(:first) && value.first
|
75
|
+
if value.first != value
|
76
|
+
add_schema param, value.first
|
77
|
+
end
|
78
|
+
end
|
79
|
+
rescue
|
80
|
+
warn "Error in add_schema(#{value.class})", $!
|
70
81
|
end
|
71
|
-
rescue
|
72
|
-
nil
|
73
82
|
end
|
74
83
|
|
75
84
|
# Heuristic for dynamically defined class whose name can be nil
|
@@ -221,7 +230,9 @@ module AppMap
|
|
221
230
|
object_id: value.__id__,
|
222
231
|
value: display_string(value),
|
223
232
|
kind: param_type
|
224
|
-
}
|
233
|
+
}.tap do |param|
|
234
|
+
add_size param, value
|
235
|
+
end
|
225
236
|
end
|
226
237
|
event.receiver = {
|
227
238
|
class: best_class_name(receiver),
|
@@ -276,7 +287,7 @@ module AppMap
|
|
276
287
|
attr_accessor :return_value, :exceptions
|
277
288
|
|
278
289
|
class << self
|
279
|
-
def build_from_invocation(parent_id, return_value, exception, elapsed: nil, event: MethodReturn.new)
|
290
|
+
def build_from_invocation(parent_id, return_value, exception, elapsed: nil, event: MethodReturn.new, parameter_schema: false)
|
280
291
|
event ||= MethodReturn.new
|
281
292
|
event.tap do |_|
|
282
293
|
if return_value
|
@@ -284,7 +295,10 @@ module AppMap
|
|
284
295
|
class: best_class_name(return_value),
|
285
296
|
value: display_string(return_value),
|
286
297
|
object_id: return_value.__id__
|
287
|
-
}
|
298
|
+
}.tap do |param|
|
299
|
+
add_size param, return_value
|
300
|
+
add_schema param, return_value if parameter_schema && !exception
|
301
|
+
end
|
288
302
|
end
|
289
303
|
if exception
|
290
304
|
next_exception = exception
|
@@ -43,3 +43,9 @@
|
|
43
43
|
- ActionController::Instrumentation#redirect_to
|
44
44
|
label: mvc.controller
|
45
45
|
require_name: action_controller
|
46
|
+
- methods:
|
47
|
+
- AbstractController::Rendering#render_to_body
|
48
|
+
- ActionController::Renderers#render_to_body
|
49
|
+
label: mvc.render
|
50
|
+
handler_class: AppMap::Handler::Rails::RenderHandler
|
51
|
+
require_name: action_controller
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'appmap/handler/function'
|
2
|
+
|
3
|
+
module AppMap
|
4
|
+
module Handler
|
5
|
+
module Rails
|
6
|
+
class RenderHandler < AppMap::Handler::Function
|
7
|
+
def handle_call(receiver, args)
|
8
|
+
options, _ = args
|
9
|
+
# TODO: :file, :xml
|
10
|
+
# https://guides.rubyonrails.org/v5.1/layouts_and_rendering.html
|
11
|
+
if options[:json]
|
12
|
+
Thread.current[TEMPLATE_RENDER_FORMAT] = :json
|
13
|
+
end
|
14
|
+
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def handle_return(call_event_id, elapsed, return_value, exception)
|
19
|
+
if Thread.current[TEMPLATE_RENDER_FORMAT] == :json
|
20
|
+
Thread.current[TEMPLATE_RENDER_VALUE] = JSON.parse(return_value) rescue nil
|
21
|
+
end
|
22
|
+
Thread.current[TEMPLATE_RENDER_FORMAT] = nil
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -7,6 +7,7 @@ require 'appmap/util'
|
|
7
7
|
module AppMap
|
8
8
|
module Handler
|
9
9
|
module Rails
|
10
|
+
|
10
11
|
module RequestHandler
|
11
12
|
class HTTPServerRequest < AppMap::Event::MethodEvent
|
12
13
|
attr_accessor :normalized_path_info, :request_method, :path_info, :params, :headers
|
@@ -46,8 +47,7 @@ module AppMap
|
|
46
47
|
value: self.class.display_string(val),
|
47
48
|
object_id: val.__id__,
|
48
49
|
}.tap do |message|
|
49
|
-
|
50
|
-
message[:properties] = properties if properties
|
50
|
+
AppMap::Event::MethodEvent.add_schema message, val
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -67,16 +67,16 @@ module AppMap
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
class HTTPServerResponse < AppMap::Event::
|
70
|
+
class HTTPServerResponse < AppMap::Event::MethodReturn
|
71
71
|
attr_accessor :status, :headers
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
73
|
+
class << self
|
74
|
+
def build_from_invocation(parent_id, return_value, elapsed, response, event: HTTPServerResponse.new)
|
75
|
+
event ||= HTTPServerResponse.new
|
76
|
+
event.status = response.status
|
77
|
+
event.headers = response.headers.dup
|
78
|
+
AppMap::Event::MethodReturn.build_from_invocation parent_id, return_value, nil, elapsed: elapsed, event: event, parameter_schema: true
|
79
|
+
end
|
80
80
|
end
|
81
81
|
|
82
82
|
def to_h
|
@@ -108,7 +108,9 @@ module AppMap
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def after_hook(receiver, call_event, elapsed, *)
|
111
|
-
|
111
|
+
return_value = Thread.current[TEMPLATE_RENDER_VALUE]
|
112
|
+
Thread.current[TEMPLATE_RENDER_VALUE] = nil
|
113
|
+
return_event = HTTPServerResponse.build_from_invocation call_event.id, return_value, elapsed, receiver.response
|
112
114
|
AppMap.tracing.record_event return_event
|
113
115
|
end
|
114
116
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'appmap/event'
|
4
|
+
require 'appmap/hook/method'
|
4
5
|
|
5
6
|
module AppMap
|
6
7
|
module Handler
|
@@ -21,6 +22,7 @@ module AppMap
|
|
21
22
|
sql: payload[:sql],
|
22
23
|
database_type: payload[:database_type]
|
23
24
|
}.tap do |sql_query|
|
25
|
+
sql_query[:query_plan] = payload[:query_plan] if payload[:query_plan]
|
24
26
|
%i[server_version].each do |attribute|
|
25
27
|
sql_query[attribute] = payload[attribute] if payload[attribute]
|
26
28
|
end
|
@@ -43,6 +45,36 @@ module AppMap
|
|
43
45
|
def examine(payload, sql:)
|
44
46
|
return unless (examiner = build_examiner)
|
45
47
|
|
48
|
+
in_transaction = examiner.in_transaction?
|
49
|
+
|
50
|
+
if AppMap.explain_queries? && examiner.database_type == :postgres
|
51
|
+
if sql =~ /\A(SELECT|INSERT|UPDATE|DELETE|WITH)/i
|
52
|
+
savepoint_established = \
|
53
|
+
begin
|
54
|
+
tx_query = in_transaction ? 'SAVEPOINT appmap_sql_examiner' : 'BEGIN TRANSACTION'
|
55
|
+
examiner.execute_query tx_query
|
56
|
+
true
|
57
|
+
rescue
|
58
|
+
# Probably: Sequel::DatabaseError: PG::InFailedSqlTransaction
|
59
|
+
warn $!
|
60
|
+
false
|
61
|
+
end
|
62
|
+
|
63
|
+
if savepoint_established
|
64
|
+
plan = nil
|
65
|
+
begin
|
66
|
+
plan = examiner.execute_query(%(EXPLAIN #{sql}))
|
67
|
+
payload[:query_plan] = plan.map { |line| line[:'QUERY PLAN'] }.join("\n")
|
68
|
+
rescue
|
69
|
+
warn "(appmap) Error explaining query: #{$!}"
|
70
|
+
ensure
|
71
|
+
tx_query = in_transaction ? 'ROLLBACK TO SAVEPOINT appmap_sql_examiner' : 'ROLLBACK'
|
72
|
+
examiner.execute_query tx_query
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
46
78
|
payload[:server_version] = examiner.server_version
|
47
79
|
payload[:database_type] = examiner.database_type.to_s
|
48
80
|
end
|
@@ -67,6 +99,10 @@ module AppMap
|
|
67
99
|
Sequel::Model.db.database_type.to_sym
|
68
100
|
end
|
69
101
|
|
102
|
+
def in_transaction?
|
103
|
+
Sequel::Model.db.in_transaction?
|
104
|
+
end
|
105
|
+
|
70
106
|
def execute_query(sql)
|
71
107
|
Sequel::Model.db[sql].all
|
72
108
|
end
|
@@ -93,8 +129,12 @@ module AppMap
|
|
93
129
|
type
|
94
130
|
end
|
95
131
|
|
132
|
+
def in_transaction?
|
133
|
+
ActiveRecord::Base.connection.open_transactions > 0
|
134
|
+
end
|
135
|
+
|
96
136
|
def execute_query(sql)
|
97
|
-
ActiveRecord::Base.connection.execute(sql).
|
137
|
+
ActiveRecord::Base.connection.execute(sql).to_a
|
98
138
|
end
|
99
139
|
end
|
100
140
|
end
|
@@ -102,6 +142,8 @@ module AppMap
|
|
102
142
|
def call(_, started, finished, _, payload) # (name, started, finished, unique_id, payload)
|
103
143
|
return if AppMap.tracing.empty?
|
104
144
|
|
145
|
+
return if Thread.current[AppMap::Hook::Method::HOOK_DISABLE_KEY] == true
|
146
|
+
|
105
147
|
reentry_key = "#{self.class.name}#call"
|
106
148
|
return if Thread.current[reentry_key] == true
|
107
149
|
|