appmap 0.92.1 → 0.93.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/CHANGELOG.md +17 -0
- data/lib/appmap/event.rb +2 -1
- data/lib/appmap/handler/rails/request_handler.rb +10 -1
- data/lib/appmap/handler/rails/sql_handler.rb +6 -1
- data/lib/appmap/hook/method/ruby2.rb +13 -6
- data/lib/appmap/hook/method/ruby3.rb +13 -6
- data/lib/appmap/hook/method.rb +4 -5
- data/lib/appmap/middleware/remote_recording.rb +39 -32
- data/lib/appmap/util.rb +5 -0
- data/lib/appmap/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 065c32b4761f0e0c6ddbe31d008e6e015935b327a78a948ba546fea284e2e1c5
|
4
|
+
data.tar.gz: e960cde3aca40d4d103ffdbb1b9384e827555a0ae53f135142744fdbcd5adc99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2efd8e697df46ae1f65630b49567283e437586b9a4bb6177d08d481a27a13167d2bae303ca27a9b17bcb57d396ed51f4683a1fa617804eb61222deb0ae0fb95
|
7
|
+
data.tar.gz: 2cc2eeba86b6b35452dd2b2b3f76b0adeffdcfe0fb54e6a03c85a445de3004abec26dea56b07c117a8e90ce3e9e52115398b993cdc5e322801cf248accbcbbd6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## [0.93.1](https://github.com/getappmap/appmap-ruby/compare/v0.93.0...v0.93.1) (2022-10-04)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Unyield static asset requests ([4041e4b](https://github.com/getappmap/appmap-ruby/commit/4041e4bbfb44612c5ade2cba334e1ab36a7929e5))
|
7
|
+
|
8
|
+
# [0.93.0](https://github.com/getappmap/appmap-ruby/compare/v0.92.1...v0.93.0) (2022-09-22)
|
9
|
+
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
* Record the time spent instrumentating a function, as opposed to only the time spend executing a function ([0941b4d](https://github.com/getappmap/appmap-ruby/commit/0941b4d343b1ea2964268d3a47d8f762de00deea))
|
14
|
+
* save elapsed_instrumentation for HTTP requests ([a32fb52](https://github.com/getappmap/appmap-ruby/commit/a32fb526a6f7b47ddc859e86a68b53b50b052061))
|
15
|
+
* save elapsed_instrumentation for RequestListener ([5a081cc](https://github.com/getappmap/appmap-ruby/commit/5a081ccf558a4591035acc81b1717b2ccef880ee))
|
16
|
+
* Save elapsed_instrumentation for sql_query ([40a851e](https://github.com/getappmap/appmap-ruby/commit/40a851e5d40cc5ada36ea52e32d5222da4c67d10))
|
17
|
+
|
1
18
|
## [0.92.1](https://github.com/getappmap/appmap-ruby/compare/v0.92.0...v0.92.1) (2022-09-21)
|
2
19
|
|
3
20
|
|
data/lib/appmap/event.rb
CHANGED
@@ -262,7 +262,7 @@ module AppMap
|
|
262
262
|
end
|
263
263
|
|
264
264
|
class MethodReturnIgnoreValue < MethodEvent
|
265
|
-
attr_accessor :parent_id, :elapsed
|
265
|
+
attr_accessor :parent_id, :elapsed, :elapsed_instrumentation
|
266
266
|
|
267
267
|
class << self
|
268
268
|
def build_from_invocation(parent_id, elapsed: nil, event: MethodReturnIgnoreValue.new)
|
@@ -279,6 +279,7 @@ module AppMap
|
|
279
279
|
super.tap do |h|
|
280
280
|
h[:parent_id] = parent_id
|
281
281
|
h[:elapsed] = elapsed if elapsed
|
282
|
+
h[:elapsed_instrumentation] = elapsed_instrumentation if elapsed_instrumentation
|
282
283
|
end
|
283
284
|
end
|
284
285
|
end
|
@@ -10,7 +10,7 @@ module AppMap
|
|
10
10
|
|
11
11
|
module RequestHandler
|
12
12
|
class HTTPServerRequest < AppMap::Event::MethodEvent
|
13
|
-
attr_accessor :normalized_path_info, :request_method, :path_info, :params, :headers
|
13
|
+
attr_accessor :normalized_path_info, :request_method, :path_info, :params, :headers, :call_elapsed_instrumentation
|
14
14
|
|
15
15
|
def initialize(request)
|
16
16
|
super AppMap::Event.next_id_counter, :call, Thread.current.object_id
|
@@ -101,16 +101,21 @@ module AppMap
|
|
101
101
|
protected
|
102
102
|
|
103
103
|
def before_hook(receiver, *)
|
104
|
+
before_hook_start_time = AppMap::Util.gettime()
|
104
105
|
call_event = HTTPServerRequest.new(receiver.request)
|
106
|
+
call_event.call_elapsed_instrumentation = (AppMap::Util.gettime() - before_hook_start_time)
|
105
107
|
# http_server_request events are i/o and do not require a package name.
|
106
108
|
AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
|
107
109
|
call_event
|
108
110
|
end
|
109
111
|
|
110
112
|
def after_hook(receiver, call_event, elapsed, *)
|
113
|
+
after_hook_start_time = AppMap::Util.gettime()
|
111
114
|
return_value = Thread.current[TEMPLATE_RENDER_VALUE]
|
112
115
|
Thread.current[TEMPLATE_RENDER_VALUE] = nil
|
113
116
|
return_event = HTTPServerResponse.build_from_invocation call_event.id, return_value, elapsed, receiver.response
|
117
|
+
return_event.elapsed_instrumentation = (AppMap::Util.gettime() - after_hook_start_time) + call_event.call_elapsed_instrumentation
|
118
|
+
call_event.call_elapsed_instrumentation = nil # to stay consistent with elapsed_instrumentation only being stored in return
|
114
119
|
AppMap.tracing.record_event return_event
|
115
120
|
end
|
116
121
|
end
|
@@ -134,13 +139,16 @@ module AppMap
|
|
134
139
|
end
|
135
140
|
|
136
141
|
def before_hook(payload)
|
142
|
+
before_hook_start_time = AppMap::Util.gettime()
|
137
143
|
@call_event = HTTPServerRequest.new payload[:request]
|
144
|
+
@call_event.call_elapsed_instrumentation = (AppMap::Util.gettime() - before_hook_start_time)
|
138
145
|
AppMap.tracing.record_event @call_event
|
139
146
|
end
|
140
147
|
|
141
148
|
def after_hook(_name, started, finished, _unique_id, payload)
|
142
149
|
return unless @request_id == payload[:request].request_id
|
143
150
|
|
151
|
+
after_hook_start_time = AppMap::Util.gettime()
|
144
152
|
return_value = Thread.current[TEMPLATE_RENDER_VALUE]
|
145
153
|
Thread.current[TEMPLATE_RENDER_VALUE] = nil
|
146
154
|
return_event = HTTPServerResponse.build_from_invocation(
|
@@ -149,6 +157,7 @@ module AppMap
|
|
149
157
|
finished - started,
|
150
158
|
payload[:response] || payload
|
151
159
|
)
|
160
|
+
return_event.elapsed_instrumentation = (AppMap::Util.gettime() - after_hook_start_time) + @call_event.call_elapsed_instrumentation
|
152
161
|
|
153
162
|
AppMap.tracing.record_event return_event
|
154
163
|
ActiveSupport::Notifications.unsubscribe(@subscriber)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'appmap/event'
|
4
4
|
require 'appmap/hook/method'
|
5
|
+
require 'appmap/util'
|
5
6
|
|
6
7
|
module AppMap
|
7
8
|
module Handler
|
@@ -148,6 +149,8 @@ module AppMap
|
|
148
149
|
reentry_key = "#{self.class.name}#call"
|
149
150
|
return if Thread.current[reentry_key] == true
|
150
151
|
|
152
|
+
after_start_time = AppMap::Util.gettime()
|
153
|
+
|
151
154
|
Thread.current[reentry_key] = true
|
152
155
|
begin
|
153
156
|
sql = payload[:sql].strip
|
@@ -156,7 +159,9 @@ module AppMap
|
|
156
159
|
|
157
160
|
call = SQLCall.new(payload)
|
158
161
|
AppMap.tracing.record_event(call)
|
159
|
-
|
162
|
+
sql_return_event = SQLReturn.new(call.id, finished - started)
|
163
|
+
sql_return_event.elapsed_instrumentation = AppMap::Util.gettime() - after_start_time
|
164
|
+
AppMap.tracing.record_event(sql_return_event)
|
160
165
|
ensure
|
161
166
|
Thread.current[reentry_key] = nil
|
162
167
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'appmap/util'
|
4
|
+
|
3
5
|
def ruby2_keywords(*); end unless respond_to?(:ruby2_keywords, true)
|
4
6
|
|
5
7
|
module AppMap
|
@@ -8,15 +10,19 @@ module AppMap
|
|
8
10
|
# cf. https://eregon.me/blog/2019/11/10/the-delegation-challenge-of-ruby27.html
|
9
11
|
class Method
|
10
12
|
ruby2_keywords def call(receiver, *args, &block)
|
11
|
-
call_event =
|
13
|
+
call_event = false
|
14
|
+
if trace?
|
15
|
+
call_event, elapsed_before = with_disabled_hook { before_hook receiver, *args }
|
16
|
+
end
|
12
17
|
# note we can't short-circuit directly to do_call because then the call stack
|
13
18
|
# depth changes and eval handler doesn't work correctly
|
14
|
-
trace_call call_event, receiver, *args, &block
|
19
|
+
trace_call call_event, elapsed_before, receiver, *args, &block
|
15
20
|
end
|
16
21
|
|
17
22
|
protected
|
18
23
|
|
19
24
|
def before_hook(receiver, *args)
|
25
|
+
before_hook_start_time = AppMap::Util.gettime()
|
20
26
|
call_event = handle_call(receiver, args)
|
21
27
|
if call_event
|
22
28
|
AppMap.tracing.record_event \
|
@@ -25,7 +31,7 @@ module AppMap
|
|
25
31
|
defined_class: defined_class,
|
26
32
|
method: hook_method
|
27
33
|
end
|
28
|
-
call_event
|
34
|
+
[call_event, AppMap::Util.gettime() - before_hook_start_time]
|
29
35
|
end
|
30
36
|
|
31
37
|
ruby2_keywords def do_call(receiver, *args, &block)
|
@@ -33,17 +39,18 @@ module AppMap
|
|
33
39
|
end
|
34
40
|
|
35
41
|
# rubocop:disable Metrics/MethodLength
|
36
|
-
ruby2_keywords def trace_call(call_event, receiver, *args, &block)
|
42
|
+
ruby2_keywords def trace_call(call_event, elapsed_before, receiver, *args, &block)
|
37
43
|
return do_call(receiver, *args, &block) unless call_event
|
38
44
|
|
39
|
-
start_time = gettime
|
45
|
+
start_time = AppMap::Util.gettime()
|
40
46
|
begin
|
41
47
|
return_value = do_call(receiver, *args, &block)
|
42
48
|
rescue # rubocop:disable Style/RescueStandardError
|
43
49
|
exception = $ERROR_INFO
|
44
50
|
raise
|
45
51
|
ensure
|
46
|
-
|
52
|
+
after_start_time = AppMap::Util.gettime()
|
53
|
+
with_disabled_hook { after_hook receiver, call_event, elapsed_before, after_start_time - start_time, after_start_time, return_value, exception } \
|
47
54
|
if call_event
|
48
55
|
end
|
49
56
|
end
|
@@ -1,19 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'appmap/util'
|
4
|
+
|
3
5
|
module AppMap
|
4
6
|
class Hook
|
5
7
|
# Delegation methods for Ruby 3.
|
6
8
|
class Method
|
7
9
|
def call(receiver, *args, **kwargs, &block)
|
8
|
-
call_event =
|
10
|
+
call_event = false
|
11
|
+
if trace?
|
12
|
+
call_event, elapsed_before = with_disabled_hook { before_hook receiver, *args, **kwargs }
|
13
|
+
end
|
9
14
|
# note we can't short-circuit directly to do_call because then the call stack
|
10
15
|
# depth changes and eval handler doesn't work correctly
|
11
|
-
trace_call call_event, receiver, *args, **kwargs, &block
|
16
|
+
trace_call call_event, elapsed_before, receiver, *args, **kwargs, &block
|
12
17
|
end
|
13
18
|
|
14
19
|
protected
|
15
20
|
|
16
21
|
def before_hook(receiver, *args, **kwargs)
|
22
|
+
before_hook_start_time = AppMap::Util.gettime()
|
17
23
|
args = [*args, kwargs] if !kwargs.empty? || keyrest?
|
18
24
|
call_event = handle_call(receiver, args)
|
19
25
|
if call_event
|
@@ -23,7 +29,7 @@ module AppMap
|
|
23
29
|
defined_class: defined_class,
|
24
30
|
method: hook_method
|
25
31
|
end
|
26
|
-
call_event
|
32
|
+
[call_event, AppMap::Util.gettime() - before_hook_start_time]
|
27
33
|
end
|
28
34
|
|
29
35
|
def keyrest?
|
@@ -35,17 +41,18 @@ module AppMap
|
|
35
41
|
end
|
36
42
|
|
37
43
|
# rubocop:disable Metrics/MethodLength
|
38
|
-
def trace_call(call_event, receiver, *args, **kwargs, &block)
|
44
|
+
def trace_call(call_event, elapsed_before, receiver, *args, **kwargs, &block)
|
39
45
|
return do_call(receiver, *args, **kwargs, &block) unless call_event
|
40
46
|
|
41
|
-
start_time = gettime
|
47
|
+
start_time = AppMap::Util.gettime()
|
42
48
|
begin
|
43
49
|
return_value = do_call(receiver, *args, **kwargs, &block)
|
44
50
|
rescue # rubocop:disable Style/RescueStandardError
|
45
51
|
exception = $ERROR_INFO
|
46
52
|
raise
|
47
53
|
ensure
|
48
|
-
|
54
|
+
after_start_time = AppMap::Util.gettime()
|
55
|
+
with_disabled_hook { after_hook receiver, call_event, elapsed_before, after_start_time - start_time, after_start_time, return_value, exception } \
|
49
56
|
if call_event
|
50
57
|
end
|
51
58
|
end
|
data/lib/appmap/hook/method.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'appmap/util'
|
4
|
+
|
3
5
|
module AppMap
|
4
6
|
class Hook
|
5
7
|
SIGNATURES = {}
|
@@ -78,10 +80,6 @@ module AppMap
|
|
78
80
|
warn "#{hook_method.name} not found on #{hook_class}" if Hook::LOG
|
79
81
|
end
|
80
82
|
|
81
|
-
def gettime
|
82
|
-
Process.clock_gettime Process::CLOCK_MONOTONIC
|
83
|
-
end
|
84
|
-
|
85
83
|
def trace?
|
86
84
|
return false unless AppMap.tracing_enabled?
|
87
85
|
return false if Thread.current[HOOK_DISABLE_KEY]
|
@@ -102,8 +100,9 @@ module AppMap
|
|
102
100
|
@defined_class ||= Hook.qualify_method_name(hook_method)&.first
|
103
101
|
end
|
104
102
|
|
105
|
-
def after_hook(_receiver, call_event, elapsed, return_value, exception)
|
103
|
+
def after_hook(_receiver, call_event, elapsed_before, elapsed, after_start_time, return_value, exception)
|
106
104
|
return_event = handle_return(call_event.id, elapsed, return_value, exception)
|
105
|
+
return_event.elapsed_instrumentation = elapsed_before + (AppMap::Util.gettime() - after_start_time)
|
107
106
|
AppMap.tracing.record_event(return_event) if return_event
|
108
107
|
end
|
109
108
|
|
@@ -89,39 +89,46 @@ module AppMap
|
|
89
89
|
# into a separate Tracer.
|
90
90
|
tracer = AppMap.tracing.trace(thread: Thread.current) if record_all_requests?
|
91
91
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
92
|
+
record_request = lambda do |args|
|
93
|
+
return unless tracer
|
94
|
+
|
95
|
+
AppMap.tracing.delete(tracer)
|
96
|
+
|
97
|
+
status, headers = args
|
98
|
+
events = tracer.events.map(&:to_h)
|
99
|
+
|
100
|
+
event_fields = events.map(&:keys).flatten.map(&:to_sym).uniq.sort
|
101
|
+
return unless %i[http_server_request http_server_response].all? { |field| event_fields.include?(field) }
|
102
|
+
|
103
|
+
path = req.path.gsub(/\/{2,}/, '/') # Double slashes have been observed
|
104
|
+
appmap_name = "#{req.request_method} #{path} (#{status}) - #{start_time.strftime('%T.%L')}"
|
105
|
+
appmap_file_name = AppMap::Util.scenario_filename([ start_time.to_f, req.url ].join('_'))
|
106
|
+
output_dir = File.join(AppMap::DEFAULT_APPMAP_DIR, 'requests')
|
107
|
+
appmap_file_path = File.join(output_dir, appmap_file_name)
|
108
|
+
|
109
|
+
metadata = AppMap.detect_metadata
|
110
|
+
metadata[:name] = appmap_name
|
111
|
+
metadata[:timestamp] = start_time.to_f
|
112
|
+
metadata[:recorder] = {
|
113
|
+
name: 'rack',
|
114
|
+
type: 'requests'
|
115
|
+
}
|
116
|
+
|
117
|
+
appmap = {
|
118
|
+
version: AppMap::APPMAP_FORMAT_VERSION,
|
119
|
+
classMap: AppMap.class_map(tracer.event_methods),
|
120
|
+
metadata: metadata,
|
121
|
+
events: events
|
122
|
+
}
|
123
|
+
|
124
|
+
FileUtils.mkdir_p(output_dir)
|
125
|
+
File.write(appmap_file_path, JSON.generate(appmap))
|
126
|
+
|
127
|
+
headers['AppMap-Name'] = File.expand_path(appmap_name)
|
128
|
+
headers['AppMap-File-Name'] = File.expand_path(appmap_file_path)
|
124
129
|
end
|
130
|
+
|
131
|
+
@app.call(env).tap(&record_request)
|
125
132
|
end
|
126
133
|
|
127
134
|
def recording_state
|
data/lib/appmap/util.rb
CHANGED
@@ -86,6 +86,7 @@ module AppMap
|
|
86
86
|
def sanitize_event(event, &block)
|
87
87
|
event.delete(:thread_id)
|
88
88
|
event.delete(:elapsed)
|
89
|
+
event.delete(:elapsed_instrumentation)
|
89
90
|
delete_object_id = ->(obj) { (obj || {}).delete(:object_id) }
|
90
91
|
delete_object_id.call(event[:receiver])
|
91
92
|
delete_object_id.call(event[:return_value])
|
@@ -231,6 +232,10 @@ module AppMap
|
|
231
232
|
def ruby_minor_version
|
232
233
|
@ruby_minor_version ||= RUBY_VERSION.split('.')[0..1].join('.').to_f
|
233
234
|
end
|
235
|
+
|
236
|
+
def gettime
|
237
|
+
Process.clock_gettime Process::CLOCK_MONOTONIC
|
238
|
+
end
|
234
239
|
end
|
235
240
|
end
|
236
241
|
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.93.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Gilpin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: method_source
|