appmap 0.44.0 → 0.47.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/.releaserc.yml +11 -0
- data/.travis.yml +28 -12
- data/CHANGELOG.md +42 -0
- data/README.md +39 -11
- data/README_CI.md +29 -0
- data/Rakefile +4 -2
- data/appmap.gemspec +5 -1
- data/lib/appmap/class_map.rb +7 -15
- data/lib/appmap/config.rb +203 -95
- data/lib/appmap/event.rb +29 -28
- data/lib/appmap/handler/function.rb +19 -0
- data/lib/appmap/handler/net_http.rb +107 -0
- data/lib/appmap/handler/rails/request_handler.rb +124 -0
- data/lib/appmap/handler/rails/sql_handler.rb +152 -0
- data/lib/appmap/handler/rails/template.rb +155 -0
- data/lib/appmap/hook.rb +109 -71
- data/lib/appmap/hook/method.rb +6 -8
- data/lib/appmap/railtie.rb +5 -5
- data/lib/appmap/trace.rb +47 -6
- data/lib/appmap/util.rb +41 -2
- data/lib/appmap/version.rb +2 -2
- data/package-lock.json +3 -3
- data/release.sh +17 -0
- data/spec/abstract_controller_base_spec.rb +74 -11
- data/spec/class_map_spec.rb +3 -3
- data/spec/config_spec.rb +3 -1
- data/spec/hook_spec.rb +12 -66
- data/spec/record_net_http_spec.rb +160 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/util_spec.rb +18 -1
- metadata +16 -10
- data/lib/appmap/rails/request_handler.rb +0 -140
- data/lib/appmap/rails/sql_handler.rb +0 -150
- data/patch +0 -1447
data/lib/appmap/event.rb
CHANGED
@@ -21,10 +21,10 @@ module AppMap
|
|
21
21
|
LIMIT = 100
|
22
22
|
|
23
23
|
class << self
|
24
|
-
def build_from_invocation(
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
def build_from_invocation(event_type, event:)
|
25
|
+
event.id = AppMap::Event.next_id_counter
|
26
|
+
event.event = event_type
|
27
|
+
event.thread_id = Thread.current.object_id
|
28
28
|
end
|
29
29
|
|
30
30
|
# Gets a display string for a value. This is not meant to be a machine deserializable value.
|
@@ -48,8 +48,6 @@ module AppMap
|
|
48
48
|
nil
|
49
49
|
end
|
50
50
|
|
51
|
-
protected
|
52
|
-
|
53
51
|
# Heuristic for dynamically defined class whose name can be nil
|
54
52
|
def best_class_name(value)
|
55
53
|
value_cls = value.class
|
@@ -103,19 +101,20 @@ module AppMap
|
|
103
101
|
attr_accessor :defined_class, :method_id, :path, :lineno, :parameters, :receiver, :static
|
104
102
|
|
105
103
|
class << self
|
106
|
-
def build_from_invocation(
|
104
|
+
def build_from_invocation(defined_class, method, receiver, arguments, event: MethodCall.new)
|
105
|
+
event ||= MethodCall.new
|
107
106
|
defined_class ||= 'Class'
|
108
|
-
|
107
|
+
event.tap do
|
109
108
|
static = receiver.is_a?(Module)
|
110
|
-
|
111
|
-
|
109
|
+
event.defined_class = defined_class
|
110
|
+
event.method_id = method.name.to_s
|
112
111
|
if method.source_location
|
113
112
|
path = method.source_location[0]
|
114
113
|
path = path[Dir.pwd.length + 1..-1] if path.index(Dir.pwd) == 0
|
115
|
-
|
116
|
-
|
114
|
+
event.path = path
|
115
|
+
event.lineno = method.source_location[1]
|
117
116
|
else
|
118
|
-
|
117
|
+
event.path = [ defined_class, static ? '.' : '#', method.name ].join
|
119
118
|
end
|
120
119
|
|
121
120
|
# Check if the method has key parameters. If there are any they'll always be last.
|
@@ -123,7 +122,7 @@ module AppMap
|
|
123
122
|
has_key = [[:dummy], *method.parameters].last.first.to_s.start_with?('key') && arguments[-1].is_a?(Hash)
|
124
123
|
kwargs = has_key && arguments[-1].dup || {}
|
125
124
|
|
126
|
-
|
125
|
+
event.parameters = method.parameters.map.with_index do |method_param, idx|
|
127
126
|
param_type, param_name = method_param
|
128
127
|
param_name ||= 'arg'
|
129
128
|
value = case param_type
|
@@ -144,13 +143,13 @@ module AppMap
|
|
144
143
|
kind: param_type
|
145
144
|
}
|
146
145
|
end
|
147
|
-
|
146
|
+
event.receiver = {
|
148
147
|
class: best_class_name(receiver),
|
149
148
|
object_id: receiver.__id__,
|
150
149
|
value: display_string(receiver)
|
151
150
|
}
|
152
|
-
|
153
|
-
MethodEvent.build_from_invocation(
|
151
|
+
event.static = static
|
152
|
+
MethodEvent.build_from_invocation(:call, event: event)
|
154
153
|
end
|
155
154
|
end
|
156
155
|
end
|
@@ -175,11 +174,12 @@ module AppMap
|
|
175
174
|
attr_accessor :parent_id, :elapsed
|
176
175
|
|
177
176
|
class << self
|
178
|
-
def build_from_invocation(
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
177
|
+
def build_from_invocation(parent_id, elapsed: nil, event: MethodReturnIgnoreValue.new)
|
178
|
+
event ||= MethodReturnIgnoreValue.new
|
179
|
+
event.tap do |_|
|
180
|
+
event.parent_id = parent_id
|
181
|
+
event.elapsed = elapsed
|
182
|
+
MethodEvent.build_from_invocation(:return, event: event)
|
183
183
|
end
|
184
184
|
end
|
185
185
|
end
|
@@ -187,7 +187,7 @@ module AppMap
|
|
187
187
|
def to_h
|
188
188
|
super.tap do |h|
|
189
189
|
h[:parent_id] = parent_id
|
190
|
-
h[:elapsed] = elapsed
|
190
|
+
h[:elapsed] = elapsed if elapsed
|
191
191
|
end
|
192
192
|
end
|
193
193
|
end
|
@@ -196,10 +196,11 @@ module AppMap
|
|
196
196
|
attr_accessor :return_value, :exceptions
|
197
197
|
|
198
198
|
class << self
|
199
|
-
def build_from_invocation(
|
200
|
-
|
199
|
+
def build_from_invocation(parent_id, return_value, exception, elapsed: nil, event: MethodReturn.new)
|
200
|
+
event ||= MethodReturn.new
|
201
|
+
event.tap do |_|
|
201
202
|
if return_value
|
202
|
-
|
203
|
+
event.return_value = {
|
203
204
|
class: best_class_name(return_value),
|
204
205
|
value: display_string(return_value),
|
205
206
|
object_id: return_value.__id__
|
@@ -220,9 +221,9 @@ module AppMap
|
|
220
221
|
next_exception = next_exception.cause
|
221
222
|
end
|
222
223
|
|
223
|
-
|
224
|
+
event.exceptions = exceptions
|
224
225
|
end
|
225
|
-
MethodReturnIgnoreValue.build_from_invocation(
|
226
|
+
MethodReturnIgnoreValue.build_from_invocation(parent_id, elapsed: elapsed, event: event)
|
226
227
|
end
|
227
228
|
end
|
228
229
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'appmap/event'
|
4
|
+
|
5
|
+
module AppMap
|
6
|
+
module Handler
|
7
|
+
module Function
|
8
|
+
class << self
|
9
|
+
def handle_call(defined_class, hook_method, receiver, args)
|
10
|
+
AppMap::Event::MethodCall.build_from_invocation(defined_class, hook_method, receiver, args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def handle_return(call_event_id, elapsed, return_value, exception)
|
14
|
+
AppMap::Event::MethodReturn.build_from_invocation(call_event_id, return_value, exception, elapsed: elapsed)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'appmap/event'
|
4
|
+
|
5
|
+
module AppMap
|
6
|
+
module Handler
|
7
|
+
class HTTPClientRequest < AppMap::Event::MethodEvent
|
8
|
+
attr_accessor :request_method, :url, :params, :headers
|
9
|
+
|
10
|
+
def initialize(http, request)
|
11
|
+
super AppMap::Event.next_id_counter, :call, Thread.current.object_id
|
12
|
+
|
13
|
+
path, query = request.path.split('?')
|
14
|
+
query ||= ''
|
15
|
+
|
16
|
+
protocol = http.use_ssl? ? 'https' : 'http'
|
17
|
+
port = if http.use_ssl? && http.port == 443
|
18
|
+
nil
|
19
|
+
elsif !http.use_ssl? && http.port == 80
|
20
|
+
nil
|
21
|
+
else
|
22
|
+
":#{http.port}"
|
23
|
+
end
|
24
|
+
|
25
|
+
url = [ protocol, '://', http.address, port, path ].compact.join
|
26
|
+
|
27
|
+
self.request_method = request.method
|
28
|
+
self.url = url
|
29
|
+
self.headers = AppMap::Util.select_headers(NetHTTP.request_headers(request))
|
30
|
+
self.params = Rack::Utils.parse_nested_query(query)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_h
|
34
|
+
super.tap do |h|
|
35
|
+
h[:http_client_request] = {
|
36
|
+
request_method: request_method,
|
37
|
+
url: url,
|
38
|
+
headers: headers
|
39
|
+
}.compact
|
40
|
+
|
41
|
+
unless params.blank?
|
42
|
+
h[:message] = params.keys.map do |key|
|
43
|
+
val = params[key]
|
44
|
+
{
|
45
|
+
name: key,
|
46
|
+
class: val.class.name,
|
47
|
+
value: self.class.display_string(val),
|
48
|
+
object_id: val.__id__,
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class HTTPClientResponse < AppMap::Event::MethodReturnIgnoreValue
|
57
|
+
attr_accessor :status, :mime_type, :headers
|
58
|
+
|
59
|
+
def initialize(response, parent_id, elapsed)
|
60
|
+
super AppMap::Event.next_id_counter, :return, Thread.current.object_id
|
61
|
+
|
62
|
+
self.status = response.code.to_i
|
63
|
+
self.parent_id = parent_id
|
64
|
+
self.elapsed = elapsed
|
65
|
+
self.headers = AppMap::Util.select_headers(NetHTTP.response_headers(response))
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_h
|
69
|
+
super.tap do |h|
|
70
|
+
h[:http_client_response] = {
|
71
|
+
status_code: status,
|
72
|
+
mime_type: mime_type,
|
73
|
+
headers: headers
|
74
|
+
}.compact
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class NetHTTP
|
80
|
+
class << self
|
81
|
+
def request_headers(request)
|
82
|
+
{}.tap do |headers|
|
83
|
+
request.each_header do |k,v|
|
84
|
+
key = [ 'HTTP', k.underscore.upcase ].join('_')
|
85
|
+
headers[key] = v
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
alias response_headers request_headers
|
91
|
+
|
92
|
+
def handle_call(defined_class, hook_method, receiver, args)
|
93
|
+
# request will call itself again in a start block if it's not already started.
|
94
|
+
return unless receiver.started?
|
95
|
+
|
96
|
+
http = receiver
|
97
|
+
request = args.first
|
98
|
+
HTTPClientRequest.new(http, request)
|
99
|
+
end
|
100
|
+
|
101
|
+
def handle_return(call_event_id, elapsed, return_value, exception)
|
102
|
+
HTTPClientResponse.new(return_value, call_event_id, elapsed)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'appmap/event'
|
4
|
+
require 'appmap/hook'
|
5
|
+
|
6
|
+
module AppMap
|
7
|
+
module Handler
|
8
|
+
module Rails
|
9
|
+
module RequestHandler
|
10
|
+
class HTTPServerRequest < AppMap::Event::MethodEvent
|
11
|
+
attr_accessor :normalized_path_info, :request_method, :path_info, :params, :mime_type, :headers, :authorization
|
12
|
+
|
13
|
+
def initialize(request)
|
14
|
+
super AppMap::Event.next_id_counter, :call, Thread.current.object_id
|
15
|
+
|
16
|
+
self.request_method = request.request_method
|
17
|
+
self.normalized_path_info = normalized_path(request)
|
18
|
+
self.mime_type = request.headers['Content-Type']
|
19
|
+
self.headers = AppMap::Util.select_headers(request.env)
|
20
|
+
self.authorization = request.headers['Authorization']
|
21
|
+
self.path_info = request.path_info.split('?')[0]
|
22
|
+
# ActionDispatch::Http::ParameterFilter is deprecated
|
23
|
+
parameter_filter_cls = \
|
24
|
+
if defined?(ActiveSupport::ParameterFilter)
|
25
|
+
ActiveSupport::ParameterFilter
|
26
|
+
else
|
27
|
+
ActionDispatch::Http::ParameterFilter
|
28
|
+
end
|
29
|
+
self.params = parameter_filter_cls.new(::Rails.application.config.filter_parameters).filter(request.params)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_h
|
33
|
+
super.tap do |h|
|
34
|
+
h[:http_server_request] = {
|
35
|
+
request_method: request_method,
|
36
|
+
path_info: path_info,
|
37
|
+
mime_type: mime_type,
|
38
|
+
normalized_path_info: normalized_path_info,
|
39
|
+
authorization: authorization,
|
40
|
+
headers: headers,
|
41
|
+
}.compact
|
42
|
+
|
43
|
+
unless params.blank?
|
44
|
+
h[:message] = params.keys.map do |key|
|
45
|
+
val = params[key]
|
46
|
+
{
|
47
|
+
name: key,
|
48
|
+
class: val.class.name,
|
49
|
+
value: self.class.display_string(val),
|
50
|
+
object_id: val.__id__,
|
51
|
+
}.tap do |message|
|
52
|
+
properties = object_properties(val)
|
53
|
+
message[:properties] = properties if properties
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def normalized_path(request, router = ::Rails.application.routes.router)
|
63
|
+
router.recognize request do |route, _|
|
64
|
+
app = route.app
|
65
|
+
next unless app.matches? request
|
66
|
+
return normalized_path request, app.rack_app.routes.router if app.engine?
|
67
|
+
|
68
|
+
return AppMap::Util.swaggerize_path(route.path.spec.to_s)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class HTTPServerResponse < AppMap::Event::MethodReturnIgnoreValue
|
74
|
+
attr_accessor :status, :mime_type, :headers
|
75
|
+
|
76
|
+
def initialize(response, parent_id, elapsed)
|
77
|
+
super AppMap::Event.next_id_counter, :return, Thread.current.object_id
|
78
|
+
|
79
|
+
self.status = response.status
|
80
|
+
self.mime_type = response.headers['Content-Type']
|
81
|
+
self.parent_id = parent_id
|
82
|
+
self.elapsed = elapsed
|
83
|
+
self.headers = AppMap::Util.select_headers(response.headers)
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_h
|
87
|
+
super.tap do |h|
|
88
|
+
h[:http_server_response] = {
|
89
|
+
status_code: status,
|
90
|
+
mime_type: mime_type,
|
91
|
+
headers: headers
|
92
|
+
}.compact
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class HookMethod < AppMap::Hook::Method
|
98
|
+
def initialize
|
99
|
+
# ActionController::Instrumentation has issued start_processing.action_controller and
|
100
|
+
# process_action.action_controller since Rails 3. Therefore it's a stable place to hook
|
101
|
+
# the request. Rails controller notifications can't be used directly because they don't
|
102
|
+
# provide response headers, and we want the Content-Type.
|
103
|
+
super(nil, ActionController::Instrumentation, ActionController::Instrumentation.instance_method(:process_action))
|
104
|
+
end
|
105
|
+
|
106
|
+
protected
|
107
|
+
|
108
|
+
def before_hook(receiver, defined_class, _) # args
|
109
|
+
call_event = HTTPServerRequest.new(receiver.request)
|
110
|
+
# http_server_request events are i/o and do not require a package name.
|
111
|
+
AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
|
112
|
+
[ call_event, TIME_NOW.call ]
|
113
|
+
end
|
114
|
+
|
115
|
+
def after_hook(receiver, call_event, start_time, _, _) # return_value, exception
|
116
|
+
elapsed = TIME_NOW.call - start_time
|
117
|
+
return_event = HTTPServerResponse.new receiver.response, call_event.id, elapsed
|
118
|
+
AppMap.tracing.record_event return_event
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'appmap/event'
|
4
|
+
|
5
|
+
module AppMap
|
6
|
+
module Handler
|
7
|
+
module Rails
|
8
|
+
class SQLHandler
|
9
|
+
class SQLCall < AppMap::Event::MethodCall
|
10
|
+
attr_accessor :payload
|
11
|
+
|
12
|
+
def initialize(payload)
|
13
|
+
super AppMap::Event.next_id_counter, :call, Thread.current.object_id
|
14
|
+
|
15
|
+
self.payload = payload
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
super.tap do |h|
|
20
|
+
h[:sql_query] = {
|
21
|
+
sql: payload[:sql],
|
22
|
+
database_type: payload[:database_type]
|
23
|
+
}.tap do |sql_query|
|
24
|
+
%i[server_version].each do |attribute|
|
25
|
+
sql_query[attribute] = payload[attribute] if payload[attribute]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class SQLReturn < AppMap::Event::MethodReturnIgnoreValue
|
33
|
+
def initialize(parent_id, elapsed)
|
34
|
+
super AppMap::Event.next_id_counter, :return, Thread.current.object_id
|
35
|
+
|
36
|
+
self.parent_id = parent_id
|
37
|
+
self.elapsed = elapsed
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module SQLExaminer
|
42
|
+
class << self
|
43
|
+
def examine(payload, sql:)
|
44
|
+
return unless (examiner = build_examiner)
|
45
|
+
|
46
|
+
payload[:server_version] = examiner.server_version
|
47
|
+
payload[:database_type] = examiner.database_type.to_s
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def build_examiner
|
53
|
+
if defined?(Sequel)
|
54
|
+
SequelExaminer.new
|
55
|
+
elsif defined?(ActiveRecord)
|
56
|
+
ActiveRecordExaminer.new
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class SequelExaminer
|
62
|
+
def server_version
|
63
|
+
Sequel::Model.db.server_version
|
64
|
+
end
|
65
|
+
|
66
|
+
def database_type
|
67
|
+
Sequel::Model.db.database_type.to_sym
|
68
|
+
end
|
69
|
+
|
70
|
+
def execute_query(sql)
|
71
|
+
Sequel::Model.db[sql].all
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class ActiveRecordExaminer
|
76
|
+
@@db_version_warning_issued = {}
|
77
|
+
|
78
|
+
def issue_warning
|
79
|
+
db_type = database_type
|
80
|
+
return if @@db_version_warning_issued[db_type]
|
81
|
+
warn("AppMap: Unable to determine database version for #{db_type.inspect}")
|
82
|
+
@@db_version_warning_issued[db_type] = true
|
83
|
+
end
|
84
|
+
|
85
|
+
def server_version
|
86
|
+
ActiveRecord::Base.connection.try(:database_version) || issue_warning
|
87
|
+
end
|
88
|
+
|
89
|
+
def database_type
|
90
|
+
type = ActiveRecord::Base.connection.adapter_name.downcase.to_sym
|
91
|
+
type = :postgres if type == :postgresql
|
92
|
+
|
93
|
+
type
|
94
|
+
end
|
95
|
+
|
96
|
+
def execute_query(sql)
|
97
|
+
ActiveRecord::Base.connection.execute(sql).inject([]) { |memo, r| memo << r; memo }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def call(_, started, finished, _, payload) # (name, started, finished, unique_id, payload)
|
103
|
+
return if AppMap.tracing.empty?
|
104
|
+
|
105
|
+
reentry_key = "#{self.class.name}#call"
|
106
|
+
return if Thread.current[reentry_key] == true
|
107
|
+
|
108
|
+
Thread.current[reentry_key] = true
|
109
|
+
begin
|
110
|
+
sql = payload[:sql].strip
|
111
|
+
|
112
|
+
# Detect whether a function call within a specified filename is present in the call stack.
|
113
|
+
find_in_backtrace = lambda do |file_name, function_name = nil|
|
114
|
+
Thread.current.backtrace.find do |line|
|
115
|
+
tokens = line.split(':')
|
116
|
+
matches_file = tokens.find { |t| t.rindex(file_name) == (t.length - file_name.length) }
|
117
|
+
matches_function = function_name.nil? || tokens.find { |t| t == "in `#{function_name}'" }
|
118
|
+
matches_file && matches_function
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Ignore SQL calls which are made while establishing a new connection.
|
123
|
+
#
|
124
|
+
# Example:
|
125
|
+
# /path/to/ruby/2.6.0/gems/sequel-5.20.0/lib/sequel/connection_pool.rb:122:in `make_new'
|
126
|
+
return if find_in_backtrace.call('lib/sequel/connection_pool.rb', 'make_new')
|
127
|
+
# lib/active_record/connection_adapters/abstract/connection_pool.rb:811:in `new_connection'
|
128
|
+
return if find_in_backtrace.call('lib/active_record/connection_adapters/abstract/connection_pool.rb', 'new_connection')
|
129
|
+
|
130
|
+
# Ignore SQL calls which are made while inspecting the DB schema.
|
131
|
+
#
|
132
|
+
# Example:
|
133
|
+
# /path/to/ruby/2.6.0/gems/sequel-5.20.0/lib/sequel/model/base.rb:812:in `get_db_schema'
|
134
|
+
return if find_in_backtrace.call('lib/sequel/model/base.rb', 'get_db_schema')
|
135
|
+
# /usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/model_schema.rb:466:in `load_schema!'
|
136
|
+
return if find_in_backtrace.call('lib/active_record/model_schema.rb', 'load_schema!')
|
137
|
+
return if find_in_backtrace.call('lib/active_model/attribute_methods.rb', 'define_attribute_methods')
|
138
|
+
return if find_in_backtrace.call('lib/active_record/connection_adapters/schema_cache.rb')
|
139
|
+
|
140
|
+
SQLExaminer.examine payload, sql: sql
|
141
|
+
|
142
|
+
call = SQLCall.new(payload)
|
143
|
+
AppMap.tracing.record_event(call)
|
144
|
+
AppMap.tracing.record_event(SQLReturn.new(call.id, finished - started))
|
145
|
+
ensure
|
146
|
+
Thread.current[reentry_key] = nil
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|