appmap 0.92.1 → 0.93.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f527f92e73d7e71cf5a4e9db3b71c570b39443ad0788bc2c6f4b4bae4bb215ba
4
- data.tar.gz: 6604ce77790645d5c5a60d37a2bc1116952909fe29856ea9fb36bac29d992d81
3
+ metadata.gz: 065c32b4761f0e0c6ddbe31d008e6e015935b327a78a948ba546fea284e2e1c5
4
+ data.tar.gz: e960cde3aca40d4d103ffdbb1b9384e827555a0ae53f135142744fdbcd5adc99
5
5
  SHA512:
6
- metadata.gz: 841907ab84538b09c9f83509d3c65dcd6d3b0be92e452a3558194fd33a8c90336f4f8294be91ac94a8631546acc89bef91807a451a546c3c1d9408110268e237
7
- data.tar.gz: 715e6d1e540a89c660f0d3a9794b0c199356b1557fc5921226a422b78eff9c62f6b696cd1d231f0c25d06b82c417f5fef4b9e613b564146a50779b6e4d4d6910
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
- AppMap.tracing.record_event(SQLReturn.new(call.id, finished - started))
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 = trace? && with_disabled_hook { before_hook receiver, *args }
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
- with_disabled_hook { after_hook receiver, call_event, gettime - start_time, return_value, exception } \
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 = trace? && with_disabled_hook { before_hook receiver, *args, **kwargs }
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
- with_disabled_hook { after_hook receiver, call_event, gettime - start_time, return_value, exception } \
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
@@ -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
- @app.call(env).tap do |status, headers|
93
- if tracer
94
- AppMap.tracing.delete(tracer)
95
-
96
- events = tracer.events.map(&:to_h)
97
-
98
- appmap_name = "#{req.request_method} #{req.path} (#{status}) - #{start_time.strftime('%T.%L')}"
99
- appmap_file_name = AppMap::Util.scenario_filename([ start_time.to_f, req.url ].join('_'))
100
- output_dir = File.join(AppMap::DEFAULT_APPMAP_DIR, 'requests')
101
- appmap_file_path = File.join(output_dir, appmap_file_name)
102
-
103
- metadata = AppMap.detect_metadata
104
- metadata[:name] = appmap_name
105
- metadata[:timestamp] = start_time.to_f
106
- metadata[:recorder] = {
107
- name: 'rack',
108
- type: 'requests'
109
- }
110
-
111
- appmap = {
112
- version: AppMap::APPMAP_FORMAT_VERSION,
113
- classMap: AppMap.class_map(tracer.event_methods),
114
- metadata: metadata,
115
- events: events
116
- }
117
-
118
- FileUtils.mkdir_p(output_dir)
119
- File.write(appmap_file_path, JSON.generate(appmap))
120
-
121
- headers['AppMap-Name'] = File.expand_path(appmap_name)
122
- headers['AppMap-File-Name'] = File.expand_path(appmap_file_path)
123
- end
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
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.92.1'
6
+ VERSION = '0.93.1'
7
7
 
8
8
  APPMAP_FORMAT_VERSION = '1.9.0'
9
9
 
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.92.1
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-09-21 00:00:00.000000000 Z
11
+ date: 2022-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: method_source