appmap 0.99.1 → 0.99.4
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/lib/appmap/config.rb +5 -0
- data/lib/appmap/handler/rails/context.rb +42 -0
- data/lib/appmap/handler/rails/request_handler.rb +57 -7
- data/lib/appmap/hook/method.rb +15 -2
- data/lib/appmap/railtie.rb +2 -0
- data/lib/appmap/util.rb +3 -1
- data/lib/appmap/version.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: eda0d23ff548aaa0e6e61eef0e7734a542660eeeb45e899f54a7287665baaf7e
|
4
|
+
data.tar.gz: 84dc9f162eeae76f58f0c6015ec47686509aabf9ac4cac727016957b69421daf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af594c4d511623a7e9b37bc3bc45d5d2460de1410e9177f544e900c1d1cd5a555be04bef870c8c5e18a05f46fb242afd6dd9ce26646fc700f071c7a4d917da5a
|
7
|
+
data.tar.gz: 49a5e81dd6f504c1e5998f8dc2af53e1b657ddcef387005cd6f313f6c299eb46cc27b7a1c470356de8170435e459d3b1f5e06ef65a1d72f92cea5c0eed797607
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
## [0.99.4](https://github.com/getappmap/appmap-ruby/compare/v0.99.3...v0.99.4) (2023-05-15)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* More robust extraction of test failures ([3851f7c](https://github.com/getappmap/appmap-ruby/commit/3851f7cdf3daf27767f3eb161c69126b607b8a51))
|
7
|
+
* 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)
|
8
|
+
|
9
|
+
## [0.99.3](https://github.com/getappmap/appmap-ruby/compare/v0.99.2...v0.99.3) (2023-05-10)
|
10
|
+
|
11
|
+
|
12
|
+
### Bug Fixes
|
13
|
+
|
14
|
+
* 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)
|
15
|
+
|
16
|
+
## [0.99.2](https://github.com/getappmap/appmap-ruby/compare/v0.99.1...v0.99.2) (2023-05-10)
|
17
|
+
|
18
|
+
|
19
|
+
### Bug Fixes
|
20
|
+
|
21
|
+
* Ensure that signature hash key consists of strings ([acb5db9](https://github.com/getappmap/appmap-ruby/commit/acb5db9ecb0a6cdf40de83bf506f45f5283f641b))
|
22
|
+
|
1
23
|
## [0.99.1](https://github.com/getappmap/appmap-ruby/compare/v0.99.0...v0.99.1) (2023-04-24)
|
2
24
|
|
3
25
|
|
data/lib/appmap/config.rb
CHANGED
@@ -499,6 +499,11 @@ module AppMap
|
|
499
499
|
end
|
500
500
|
|
501
501
|
def never_hook?(cls, method)
|
502
|
+
unless method
|
503
|
+
HookLog.log "method is nil" if HookLog.enabled?
|
504
|
+
return true
|
505
|
+
end
|
506
|
+
|
502
507
|
_, separator, = ::AppMap::Hook.qualify_method_name(method)
|
503
508
|
if exclude.member?(cls.name) || exclude.member?([ cls.name, separator, method.name ].join)
|
504
509
|
HookLog.log "Hooking of #{method} disabled by configuration" if HookLog.enabled?
|
@@ -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/hook/method.rb
CHANGED
@@ -4,11 +4,23 @@ require 'appmap/util'
|
|
4
4
|
|
5
5
|
module AppMap
|
6
6
|
class Hook
|
7
|
+
class << self
|
8
|
+
def method_hash_key(cls, method)
|
9
|
+
[ cls, method.name ].hash
|
10
|
+
rescue TypeError => e
|
11
|
+
warn "Error building hash key for #{cls}, #{method}: #{e}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
7
16
|
SIGNATURES = {}
|
8
17
|
LOOKUP_SIGNATURE = lambda do |id|
|
9
18
|
method = super(id)
|
10
19
|
|
11
|
-
|
20
|
+
hash_key = Hook.method_hash_key(method.owner, method)
|
21
|
+
return method unless hash_key
|
22
|
+
|
23
|
+
signature = SIGNATURES[hash_key]
|
12
24
|
if signature
|
13
25
|
method.singleton_class.module_eval do
|
14
26
|
define_method(:parameters) do
|
@@ -48,7 +60,8 @@ module AppMap
|
|
48
60
|
end
|
49
61
|
|
50
62
|
hook_method_parameters = hook_method.parameters.dup.freeze
|
51
|
-
|
63
|
+
hash_key = Hook.method_hash_key(hook_class, hook_method)
|
64
|
+
SIGNATURES[hash_key] = hook_method_parameters if hash_key
|
52
65
|
|
53
66
|
# irb(main):001:0> Kernel.public_instance_method(:system)
|
54
67
|
# (irb):1:in `public_instance_method': method `system' for module `Kernel' is private (NameError)
|
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.99.
|
4
|
+
version: 0.99.4
|
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-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: method_source
|
@@ -403,6 +403,7 @@ files:
|
|
403
403
|
- lib/appmap/handler/marshal_load_handler.rb
|
404
404
|
- lib/appmap/handler/net_http_handler.rb
|
405
405
|
- lib/appmap/handler/open_ssl_handler.rb
|
406
|
+
- lib/appmap/handler/rails/context.rb
|
406
407
|
- lib/appmap/handler/rails/render_handler.rb
|
407
408
|
- lib/appmap/handler/rails/request_handler.rb
|
408
409
|
- lib/appmap/handler/rails/sql_handler.rb
|