appmap 0.99.1 → 0.99.4

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: ff070f04eeeb2d79a08d4f93462a84e277dfad345bffd1d6b87bd063a63b8f00
4
- data.tar.gz: 27e47f4e5a85fce938cbd259bf76cce952490851cfee98315fddf6d6b1c798d3
3
+ metadata.gz: eda0d23ff548aaa0e6e61eef0e7734a542660eeeb45e899f54a7287665baaf7e
4
+ data.tar.gz: 84dc9f162eeae76f58f0c6015ec47686509aabf9ac4cac727016957b69421daf
5
5
  SHA512:
6
- metadata.gz: 97db1ff69373241c3e2ca4633fcca4c800d72b60734bde3b32aae2d3765e86f484e490a4148b98c9e90c40b3fa68cc9b734e56ce9175d1e26ec9bfc767b9707e
7
- data.tar.gz: 074b036daecfc348fbe13510ab7c6cca46fe8d2d630bd81e28808ef4f05e15b47dedda31e9d3a2e423f426ba0980ea04e50439b3533b8b7690c9047be199a1a9
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(receiver.request)
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
- return_value = Thread.current[TEMPLATE_RENDER_VALUE]
115
- Thread.current[TEMPLATE_RENDER_VALUE] = nil
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
- return_value,
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
@@ -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
- signature = SIGNATURES[[ method.owner, method.name ]]
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
- SIGNATURES[[ hook_class, hook_method.name ]] = hook_method_parameters
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)
@@ -32,6 +32,8 @@ module AppMap
32
32
  AppMap::Handler::Rails::RequestHandler::RequestListener.method(:begin_request)
33
33
  )
34
34
  end
35
+
36
+ AppMap::Handler::Rails::RequestHandler::RackHook.new.activate
35
37
  end
36
38
  end
37
39
  end if AppMap.recording_enabled?
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 { |location| !Pathname.new(normalize_path(location.absolute_path)).absolute? }
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
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.99.1'
6
+ VERSION = '0.99.4'
7
7
 
8
8
  APPMAP_FORMAT_VERSION = '1.12.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.99.1
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-04-24 00:00:00.000000000 Z
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