appmap 0.99.2 → 0.100.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 +22 -0
- data/exe/appmap-agent-config +31 -0
- data/lib/appmap/command/agent_setup/config.rb +29 -0
- data/lib/appmap/handler/rails/context.rb +42 -0
- data/lib/appmap/handler/rails/request_handler.rb +57 -7
- data/lib/appmap/railtie.rb +2 -0
- data/lib/appmap/util.rb +3 -1
- data/lib/appmap/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f52fa6b2d5e7414553f16fbcf018b4d32214bdfcda4afd2567d9bef11203979
|
4
|
+
data.tar.gz: 0d456c7e95dc01ae731bbe9def89ba1585651412d6d8a35eaa2f9c4f83580d2f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a07b7341627a96c8ef21930d21a095e9cf849cf06b882d980ec670ce07003dc28e68d2a30e930fb203bf4315ca7aba1f60d43e5d7053d2e56e598ed7d6709302
|
7
|
+
data.tar.gz: 691c992e647a2760de0ae0ec75c00efb6824b008abd85f6506c31d34175e52d178d5e3297ab3414eb918fc3e083bba208b0b04c9c74f763330bc4333eb078ce1
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
# [0.100.0](https://github.com/getappmap/appmap-ruby/compare/v0.99.4...v0.100.0) (2023-07-11)
|
2
|
+
|
3
|
+
|
4
|
+
### Features
|
5
|
+
|
6
|
+
* Add `appmap-agent-config` command to bootstrap appmap.yml ([568acc3](https://github.com/getappmap/appmap-ruby/commit/568acc3948871f736ba72244a04fd54d8be67615))
|
7
|
+
|
8
|
+
## [0.99.4](https://github.com/getappmap/appmap-ruby/compare/v0.99.3...v0.99.4) (2023-05-15)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* More robust extraction of test failures ([3851f7c](https://github.com/getappmap/appmap-ruby/commit/3851f7cdf3daf27767f3eb161c69126b607b8a51))
|
14
|
+
* Use a copy of rack environment to probe the route ([7ec89a8](https://github.com/getappmap/appmap-ruby/commit/7ec89a8412dc8b9209322ebe9e246f22531b68ab)), closes [#329](https://github.com/getappmap/appmap-ruby/issues/329)
|
15
|
+
|
16
|
+
## [0.99.3](https://github.com/getappmap/appmap-ruby/compare/v0.99.2...v0.99.3) (2023-05-10)
|
17
|
+
|
18
|
+
|
19
|
+
### Bug Fixes
|
20
|
+
|
21
|
+
* Capture HTTP requests in Rails on Rack level if possible ([e50a280](https://github.com/getappmap/appmap-ruby/commit/e50a280829cd102b8eecbb83a4e2a76247ee6270)), closes [#323](https://github.com/getappmap/appmap-ruby/issues/323)
|
22
|
+
|
1
23
|
## [0.99.2](https://github.com/getappmap/appmap-ruby/compare/v0.99.1...v0.99.2) (2023-05-10)
|
2
24
|
|
3
25
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
require 'appmap'
|
6
|
+
require 'appmap/command/agent_setup/config'
|
7
|
+
|
8
|
+
@options = { config_file: AppMap::DEFAULT_CONFIG_FILE_PATH, force: false }
|
9
|
+
|
10
|
+
OptionParser.new do |parser|
|
11
|
+
parser.banner = 'Usage: appmap-agent-config [options]'
|
12
|
+
|
13
|
+
description = "AppMap configuration file path (default: #{AppMap::DEFAULT_CONFIG_FILE_PATH})"
|
14
|
+
parser.on('-c', '--config=FILEPATH', description) do |filepath|
|
15
|
+
@options[:config_file] = filepath
|
16
|
+
end
|
17
|
+
|
18
|
+
parser.on('-f', '--force', 'Overwrite existing configuration file') do
|
19
|
+
@options[:force] = true
|
20
|
+
end
|
21
|
+
end.parse!
|
22
|
+
|
23
|
+
begin
|
24
|
+
AppMap::Command::AgentSetup::Config.new(@options[:config_file], @options[:force]).perform
|
25
|
+
|
26
|
+
puts "AppMap configuration file created at #{@options[:config_file]}"
|
27
|
+
rescue AppMap::Command::AgentSetup::Config::FileExistsError
|
28
|
+
puts "AppMap configuration file already exists at #{@options[:config_file]}"
|
29
|
+
puts 'Use the --force option to overwrite.'
|
30
|
+
exit 1
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'appmap/service/guesser'
|
5
|
+
|
6
|
+
module AppMap
|
7
|
+
module Command
|
8
|
+
module AgentSetup
|
9
|
+
ConfigStruct = Struct.new(:config_file, :overwrite)
|
10
|
+
|
11
|
+
class Config < ConfigStruct
|
12
|
+
class FileExistsError < StandardError; end
|
13
|
+
|
14
|
+
def perform
|
15
|
+
raise FileExistsError unless overwrite || !File.exist?(config_file)
|
16
|
+
|
17
|
+
config = {
|
18
|
+
'name' => Service::Guesser.guess_name,
|
19
|
+
'packages' => Service::Guesser.guess_paths.map { |path| { 'path' => path } },
|
20
|
+
'language' => 'ruby',
|
21
|
+
'appmap_dir' => 'tmp/appmap'
|
22
|
+
}
|
23
|
+
|
24
|
+
File.write(config_file, YAML.dump(config))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'appmap/handler'
|
4
|
+
|
5
|
+
module AppMap
|
6
|
+
module Handler
|
7
|
+
module Rails
|
8
|
+
# Context of a rails request tracking.
|
9
|
+
# Mostly a utility class to clean up and deduplicate request handler code.
|
10
|
+
class Context
|
11
|
+
def initialize(environment = nil)
|
12
|
+
environment[REQUEST_CONTEXT] = self if environment
|
13
|
+
@thread = Thread.current
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.from(environment)
|
17
|
+
environment[REQUEST_CONTEXT]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.create(environment)
|
21
|
+
return if from environment
|
22
|
+
|
23
|
+
new environment
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.remove(env)
|
27
|
+
env[REQUEST_CONTEXT] = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_template_render_value
|
31
|
+
@thread[TEMPLATE_RENDER_VALUE].tap do
|
32
|
+
@thread[TEMPLATE_RENDER_VALUE] = nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# context is set on the rack environment to make sure a single request is only recorded once
|
37
|
+
# even if ActionDispatch::Executor is entered more than once (as can happen with engines)
|
38
|
+
REQUEST_CONTEXT = 'appmap.handler.request.context'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'appmap/event'
|
4
4
|
require 'appmap/hook'
|
5
5
|
require 'appmap/util'
|
6
|
+
require 'appmap/handler/rails/context'
|
6
7
|
|
7
8
|
module AppMap
|
8
9
|
module Handler
|
@@ -57,6 +58,8 @@ module AppMap
|
|
57
58
|
private
|
58
59
|
|
59
60
|
def normalized_path(request, router = ::Rails.application.routes.router)
|
61
|
+
# use a cloned environment because the router can modify it
|
62
|
+
request = ActionDispatch::Request.new request.env.clone
|
60
63
|
router.recognize request do |route, _|
|
61
64
|
app = route.app
|
62
65
|
next unless app.matches? request
|
@@ -101,8 +104,11 @@ module AppMap
|
|
101
104
|
protected
|
102
105
|
|
103
106
|
def before_hook(receiver, *)
|
107
|
+
req = receiver.request
|
108
|
+
return unless Context.create req.env
|
109
|
+
|
104
110
|
before_hook_start_time = AppMap::Util.gettime()
|
105
|
-
call_event = HTTPServerRequest.new(
|
111
|
+
call_event = HTTPServerRequest.new(req)
|
106
112
|
call_event.call_elapsed_instrumentation = (AppMap::Util.gettime() - before_hook_start_time)
|
107
113
|
# http_server_request events are i/o and do not require a package name.
|
108
114
|
AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
|
@@ -111,13 +117,56 @@ module AppMap
|
|
111
117
|
|
112
118
|
def after_hook(receiver, call_event, elapsed, *)
|
113
119
|
after_hook_start_time = AppMap::Util.gettime()
|
114
|
-
|
115
|
-
|
116
|
-
return_event = HTTPServerResponse.build_from_invocation call_event.id, return_value, elapsed, receiver.response
|
120
|
+
return_event = HTTPServerResponse.build_from_invocation \
|
121
|
+
call_event.id, Context.new.find_template_render_value, elapsed, receiver.response
|
117
122
|
return_event.elapsed_instrumentation = (AppMap::Util.gettime() - after_hook_start_time) + call_event.call_elapsed_instrumentation
|
118
123
|
call_event.call_elapsed_instrumentation = nil # to stay consistent with elapsed_instrumentation only being stored in return
|
119
124
|
AppMap.tracing.record_event return_event
|
125
|
+
Context.remove receiver.request.env
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Additional hook for the Rack stack in Rails applications.
|
130
|
+
#
|
131
|
+
# Hooking just in ActionController can be inaccurate if there's a middleware that
|
132
|
+
# intercepts the response and modifies it, or catches an exception
|
133
|
+
# or an object and does some other processing.
|
134
|
+
# For example, Devise catches a throw from warden on authentication error, then runs
|
135
|
+
# ActionController stack AGAIN to render a login page, which it then modifies to change
|
136
|
+
# the HTTP status code.
|
137
|
+
# ActionDispatch::Executor seems a good place to hook as the central entry point
|
138
|
+
# in a Rails application; there are a couple middleware that sometimes sit on top of it
|
139
|
+
# but they're usually inconsequential. One issue is that the executor can be entered several
|
140
|
+
# times in the stack (especially if Rails engines are used). To handle that, we set
|
141
|
+
# a context in the request environment the first time we enter it.
|
142
|
+
class RackHook < AppMap::Hook::Method
|
143
|
+
def initialize
|
144
|
+
super(nil, ActionDispatch::Executor, ActionDispatch::Executor.instance_method(:call))
|
145
|
+
end
|
146
|
+
|
147
|
+
protected
|
148
|
+
|
149
|
+
def before_hook(_receiver, env)
|
150
|
+
return unless Context.create env
|
151
|
+
|
152
|
+
before_hook_start_time = AppMap::Util.gettime
|
153
|
+
call_event = HTTPServerRequest.new ActionDispatch::Request.new(env)
|
154
|
+
# http_server_request events are i/o and do not require a package name.
|
155
|
+
AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
|
156
|
+
[call_event, (AppMap::Util.gettime - before_hook_start_time)]
|
157
|
+
end
|
158
|
+
|
159
|
+
# NOTE: this method smells of :reek:LongParameterList and :reek:UtilityFunction
|
160
|
+
# because of the interface it implements.
|
161
|
+
# rubocop:disable Metrics/ParameterLists
|
162
|
+
def after_hook(_receiver, call_event, elapsed_before, elapsed, after_hook_start_time, rack_return, _exception)
|
163
|
+
# TODO: handle exceptions
|
164
|
+
return_event = HTTPServerResponse.build_from_invocation \
|
165
|
+
call_event.id, Context.new.find_template_render_value, elapsed, ActionDispatch::Response.new(*rack_return)
|
166
|
+
return_event.elapsed_instrumentation = (AppMap::Util.gettime - after_hook_start_time) + elapsed_before
|
167
|
+
AppMap.tracing.record_event return_event
|
120
168
|
end
|
169
|
+
# rubocop:enable Metrics/ParameterLists
|
121
170
|
end
|
122
171
|
|
123
172
|
# RequestListener listens to the 'start_processing.action_controller' notification as a
|
@@ -125,6 +174,8 @@ module AppMap
|
|
125
174
|
# Rails >= 7 due to the hooked methods visibility dropping to private.
|
126
175
|
class RequestListener
|
127
176
|
def self.begin_request(_name, _started, _finished, _unique_id, payload)
|
177
|
+
return unless Context.create payload[:request].env
|
178
|
+
|
128
179
|
RequestListener.new(payload)
|
129
180
|
end
|
130
181
|
|
@@ -149,17 +200,16 @@ module AppMap
|
|
149
200
|
return unless @request_id == payload[:request].request_id
|
150
201
|
|
151
202
|
after_hook_start_time = AppMap::Util.gettime()
|
152
|
-
return_value = Thread.current[TEMPLATE_RENDER_VALUE]
|
153
|
-
Thread.current[TEMPLATE_RENDER_VALUE] = nil
|
154
203
|
return_event = HTTPServerResponse.build_from_invocation(
|
155
204
|
@call_event.id,
|
156
|
-
|
205
|
+
Context.new.find_template_render_value,
|
157
206
|
finished - started,
|
158
207
|
payload[:response] || payload
|
159
208
|
)
|
160
209
|
return_event.elapsed_instrumentation = (AppMap::Util.gettime() - after_hook_start_time) + @call_event.call_elapsed_instrumentation
|
161
210
|
|
162
211
|
AppMap.tracing.record_event return_event
|
212
|
+
Context.remove payload[:request].env
|
163
213
|
ActiveSupport::Notifications.unsubscribe(@subscriber)
|
164
214
|
end
|
165
215
|
end
|
data/lib/appmap/railtie.rb
CHANGED
data/lib/appmap/util.rb
CHANGED
@@ -161,7 +161,9 @@ module AppMap
|
|
161
161
|
return unless exception
|
162
162
|
|
163
163
|
{ message: exception.message }.tap do |test_failure|
|
164
|
-
first_location = exception.backtrace_locations&.find
|
164
|
+
first_location = exception.backtrace_locations&.find do |location|
|
165
|
+
!Pathname.new(normalize_path(location.absolute_path || location.path)).absolute?
|
166
|
+
end
|
165
167
|
test_failure[:location] = [ normalize_path(first_location.path), first_location.lineno ].join(':') if first_location
|
166
168
|
end
|
167
169
|
end
|
data/lib/appmap/version.rb
CHANGED
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.100.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: 2023-
|
11
|
+
date: 2023-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: method_source
|
@@ -308,6 +308,7 @@ description:
|
|
308
308
|
email:
|
309
309
|
- kgilpin@gmail.com
|
310
310
|
executables:
|
311
|
+
- appmap-agent-config
|
311
312
|
- appmap-agent-init
|
312
313
|
- appmap-agent-status
|
313
314
|
- appmap-agent-validate
|
@@ -347,6 +348,7 @@ files:
|
|
347
348
|
- examples/mock_webapp/lib/mock_webapp/controller.rb
|
348
349
|
- examples/mock_webapp/lib/mock_webapp/request.rb
|
349
350
|
- examples/mock_webapp/lib/mock_webapp/user.rb
|
351
|
+
- exe/appmap-agent-config
|
350
352
|
- exe/appmap-agent-init
|
351
353
|
- exe/appmap-agent-status
|
352
354
|
- exe/appmap-agent-validate
|
@@ -365,6 +367,7 @@ files:
|
|
365
367
|
- lib/appmap/builtin_hooks/psych.yml
|
366
368
|
- lib/appmap/builtin_hooks/ruby.yml
|
367
369
|
- lib/appmap/class_map.rb
|
370
|
+
- lib/appmap/command/agent_setup/config.rb
|
368
371
|
- lib/appmap/command/agent_setup/init.rb
|
369
372
|
- lib/appmap/command/agent_setup/status.rb
|
370
373
|
- lib/appmap/command/agent_setup/validate.rb
|
@@ -403,6 +406,7 @@ files:
|
|
403
406
|
- lib/appmap/handler/marshal_load_handler.rb
|
404
407
|
- lib/appmap/handler/net_http_handler.rb
|
405
408
|
- lib/appmap/handler/open_ssl_handler.rb
|
409
|
+
- lib/appmap/handler/rails/context.rb
|
406
410
|
- lib/appmap/handler/rails/render_handler.rb
|
407
411
|
- lib/appmap/handler/rails/request_handler.rb
|
408
412
|
- lib/appmap/handler/rails/sql_handler.rb
|