appmap 0.45.0 → 0.48.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +10 -0
- data/CHANGELOG.md +36 -0
- data/README.md +39 -27
- data/lib/appmap.rb +1 -2
- data/lib/appmap/class_map.rb +7 -15
- data/lib/appmap/config.rb +194 -96
- data/lib/appmap/event.rb +29 -28
- data/lib/appmap/handler/function.rb +1 -1
- 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 +1 -1
- data/lib/appmap/railtie.rb +6 -28
- data/lib/appmap/trace.rb +46 -6
- data/lib/appmap/util.rb +18 -0
- data/lib/appmap/version.rb +2 -2
- data/package-lock.json +3 -3
- data/spec/abstract_controller_base_spec.rb +68 -9
- data/spec/class_map_spec.rb +3 -3
- data/spec/fixtures/rails5_users_app/config/application.rb +0 -2
- data/spec/fixtures/rails6_users_app/config/application.rb +0 -2
- data/spec/hook_spec.rb +11 -58
- data/spec/railtie_spec.rb +7 -11
- data/spec/util_spec.rb +18 -1
- metadata +5 -4
- data/lib/appmap/rails/request_handler.rb +0 -122
- data/lib/appmap/rails/sql_handler.rb +0 -150
data/spec/class_map_spec.rb
CHANGED
@@ -5,7 +5,7 @@ require 'spec_helper'
|
|
5
5
|
describe 'AppMap::ClassMap' do
|
6
6
|
describe '.build_from_methods' do
|
7
7
|
it 'includes method comment' do
|
8
|
-
map = AppMap.class_map([
|
8
|
+
map = AppMap.class_map([ruby_method((method :test_method))])
|
9
9
|
function = dig_map(map, 5)[0]
|
10
10
|
expect(function).to include(:comment)
|
11
11
|
end
|
@@ -15,8 +15,8 @@ describe 'AppMap::ClassMap' do
|
|
15
15
|
'test method body'
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
AppMap::Trace::
|
18
|
+
def ruby_method(method)
|
19
|
+
AppMap::Trace::RubyMethod.new AppMap::Config::Package.new, method.receiver.class.name, method, false
|
20
20
|
end
|
21
21
|
|
22
22
|
def dig_map(map, depth)
|
@@ -38,8 +38,6 @@ module UsersApp
|
|
38
38
|
# Initialize configuration defaults for originally generated Rails version.
|
39
39
|
config.load_defaults 5.2
|
40
40
|
|
41
|
-
config.appmap.enabled = true if ENV['APPMAP'] == 'true' && !Rails.env.test?
|
42
|
-
|
43
41
|
# Settings in config/environments/* take precedence over those specified here.
|
44
42
|
# Application configuration can go into files in config/initializers
|
45
43
|
# -- all .rb files in that directory are automatically loaded after loading
|
@@ -38,8 +38,6 @@ module UsersApp
|
|
38
38
|
# Initialize configuration defaults for originally generated Rails version.
|
39
39
|
config.load_defaults 5.2
|
40
40
|
|
41
|
-
config.appmap.enabled = true if ENV['APPMAP'] == 'true' && !Rails.env.test?
|
42
|
-
|
43
41
|
# Settings in config/environments/* take precedence over those specified here.
|
44
42
|
# Application configuration can go into files in config/initializers
|
45
43
|
# -- all .rb files in that directory are automatically loaded after loading
|
data/spec/hook_spec.rb
CHANGED
@@ -60,69 +60,18 @@ describe 'AppMap class Hooking', docker: false do
|
|
60
60
|
config = AppMap::Config.new('hook_spec', [ package ], exclude: %w[ExcludeTest])
|
61
61
|
AppMap.configuration = config
|
62
62
|
|
63
|
-
expect(config.never_hook?(ExcludeTest.new.method(:instance_method))).to be_truthy
|
64
|
-
expect(config.never_hook?(ExcludeTest.method(:cls_method))).to be_truthy
|
63
|
+
expect(config.never_hook?(ExcludeTest, ExcludeTest.new.method(:instance_method))).to be_truthy
|
64
|
+
expect(config.never_hook?(ExcludeTest, ExcludeTest.method(:cls_method))).to be_truthy
|
65
65
|
end
|
66
66
|
|
67
|
-
it "
|
67
|
+
it "an instance method named 'call' will be ignored" do
|
68
68
|
events_yaml = <<~YAML
|
69
|
-
---
|
70
|
-
- :id: 1
|
71
|
-
:event: :call
|
72
|
-
:defined_class: MethodNamedCall
|
73
|
-
:method_id: call
|
74
|
-
:path: spec/fixtures/hook/method_named_call.rb
|
75
|
-
:lineno: 8
|
76
|
-
:static: false
|
77
|
-
:parameters:
|
78
|
-
- :name: :a
|
79
|
-
:class: Integer
|
80
|
-
:value: '1'
|
81
|
-
:kind: :req
|
82
|
-
- :name: :b
|
83
|
-
:class: Integer
|
84
|
-
:value: '2'
|
85
|
-
:kind: :req
|
86
|
-
- :name: :c
|
87
|
-
:class: Integer
|
88
|
-
:value: '3'
|
89
|
-
:kind: :req
|
90
|
-
- :name: :d
|
91
|
-
:class: Integer
|
92
|
-
:value: '4'
|
93
|
-
:kind: :req
|
94
|
-
- :name: :e
|
95
|
-
:class: Integer
|
96
|
-
:value: '5'
|
97
|
-
:kind: :req
|
98
|
-
:receiver:
|
99
|
-
:class: MethodNamedCall
|
100
|
-
:value: MethodNamedCall
|
101
|
-
- :id: 2
|
102
|
-
:event: :return
|
103
|
-
:parent_id: 1
|
104
|
-
:return_value:
|
105
|
-
:class: String
|
106
|
-
:value: 1 2 3 4 5
|
69
|
+
--- []
|
107
70
|
YAML
|
108
71
|
|
109
72
|
_, tracer = test_hook_behavior 'spec/fixtures/hook/method_named_call.rb', events_yaml do
|
110
73
|
expect(MethodNamedCall.new.call(1, 2, 3, 4, 5)).to eq('1 2 3 4 5')
|
111
74
|
end
|
112
|
-
class_map = AppMap.class_map(tracer.event_methods)
|
113
|
-
expect(Diffy::Diff.new(<<~CLASSMAP, YAML.dump(class_map)).to_s).to eq('')
|
114
|
-
---
|
115
|
-
- :name: spec/fixtures/hook/method_named_call.rb
|
116
|
-
:type: package
|
117
|
-
:children:
|
118
|
-
- :name: MethodNamedCall
|
119
|
-
:type: class
|
120
|
-
:children:
|
121
|
-
- :name: call
|
122
|
-
:type: function
|
123
|
-
:location: spec/fixtures/hook/method_named_call.rb:8
|
124
|
-
:static: false
|
125
|
-
CLASSMAP
|
126
75
|
end
|
127
76
|
|
128
77
|
it 'can custom hook and label a function' do
|
@@ -163,7 +112,9 @@ describe 'AppMap class Hooking', docker: false do
|
|
163
112
|
method = hook_cls.instance_method(:say_default)
|
164
113
|
|
165
114
|
require 'appmap/hook/method'
|
166
|
-
|
115
|
+
package = config.lookup_package(hook_cls, method)
|
116
|
+
expect(package).to be
|
117
|
+
hook_method = AppMap::Hook::Method.new(package, hook_cls, method)
|
167
118
|
hook_method.activate
|
168
119
|
|
169
120
|
tracer = AppMap.tracing.trace
|
@@ -248,8 +199,8 @@ describe 'AppMap class Hooking', docker: false do
|
|
248
199
|
_, tracer = invoke_test_file 'spec/fixtures/hook/instance_method.rb' do
|
249
200
|
InstanceMethod.new.say_default
|
250
201
|
end
|
251
|
-
expect(tracer.event_methods.to_a.map(&:
|
252
|
-
expect(tracer.event_methods.to_a.map(&:
|
202
|
+
expect(tracer.event_methods.to_a.map(&:class_name)).to eq([ 'InstanceMethod' ])
|
203
|
+
expect(tracer.event_methods.to_a.map(&:name)).to eq([ InstanceMethod.public_instance_method(:say_default).name ])
|
253
204
|
end
|
254
205
|
|
255
206
|
it 'builds a class map of invoked methods' do
|
@@ -861,7 +812,9 @@ describe 'AppMap class Hooking', docker: false do
|
|
861
812
|
_, _, events = test_hook_behavior 'spec/fixtures/hook/compare.rb', nil do
|
862
813
|
expect(Compare.compare('string', 'string')).to be_truthy
|
863
814
|
end
|
815
|
+
|
864
816
|
secure_compare_event = YAML.load(events).find { |evt| evt[:defined_class] == 'ActiveSupport::SecurityUtils' }
|
817
|
+
expect(secure_compare_event).to be_truthy
|
865
818
|
secure_compare_event.delete(:lineno)
|
866
819
|
secure_compare_event.delete(:path)
|
867
820
|
|
data/spec/railtie_spec.rb
CHANGED
@@ -4,7 +4,7 @@ describe 'AppMap tracer via Railtie' do
|
|
4
4
|
include_context 'Rails app pg database', 'spec/fixtures/rails5_users_app' do
|
5
5
|
let(:env) { {} }
|
6
6
|
|
7
|
-
let(:cmd) { %(docker-compose run --rm -e RAILS_ENV -e APPMAP app ./bin/rails r "puts
|
7
|
+
let(:cmd) { %(docker-compose run --rm -e RAILS_ENV=development -e APPMAP app ./bin/rails r "puts AppMap.instance_variable_get('@configuration').nil?") }
|
8
8
|
let(:command_capture2) do
|
9
9
|
require 'open3'
|
10
10
|
Open3.capture3(env, cmd, chdir: fixture_dir).tap do |result|
|
@@ -23,20 +23,16 @@ describe 'AppMap tracer via Railtie' do
|
|
23
23
|
let(:command_output) { command_capture2[0].strip }
|
24
24
|
let(:command_result) { command_capture2[2] }
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
describe 'with APPMAP=false' do
|
27
|
+
let(:env) { { 'APPMAP' => 'false' } }
|
28
|
+
it 'is disabled' do
|
29
|
+
expect(command_output).to eq('true')
|
30
|
+
end
|
28
31
|
end
|
29
|
-
|
30
32
|
describe 'with APPMAP=true' do
|
31
33
|
let(:env) { { 'APPMAP' => 'true' } }
|
32
34
|
it 'is enabled' do
|
33
|
-
expect(command_output
|
34
|
-
end
|
35
|
-
context 'and RAILS_ENV=test' do
|
36
|
-
let(:env) { { 'APPMAP' => 'true', 'RAILS_ENV' => 'test' } }
|
37
|
-
it 'is disabled' do
|
38
|
-
expect(command_output).to eq('nil')
|
39
|
-
end
|
35
|
+
expect(command_output).to eq('false')
|
40
36
|
end
|
41
37
|
end
|
42
38
|
end
|
data/spec/util_spec.rb
CHANGED
@@ -4,8 +4,8 @@ require 'spec_helper'
|
|
4
4
|
require 'appmap/util'
|
5
5
|
|
6
6
|
describe AppMap::Util, docker: false do
|
7
|
-
let(:subject) { AppMap::Util.method(:scenario_filename) }
|
8
7
|
describe 'scenario_filename' do
|
8
|
+
let(:subject) { AppMap::Util.method(:scenario_filename) }
|
9
9
|
it 'leaves short names alone' do
|
10
10
|
expect(subject.call('foobar')).to eq('foobar.appmap.json')
|
11
11
|
end
|
@@ -18,4 +18,21 @@ describe AppMap::Util, docker: false do
|
|
18
18
|
expect(subject.call(fname, max_length: 50)).to eq('abcdefghijklmno-RAd_SFbH1sUZ_OXfwPsfzw.appmap.json')
|
19
19
|
end
|
20
20
|
end
|
21
|
+
describe 'swaggerize path' do
|
22
|
+
it 'replaces rails-style parameters' do
|
23
|
+
expect(AppMap::Util.swaggerize_path('/org/:org_id(.:format)')).to eq('/org/{org_id}')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'strips the format specifier' do
|
27
|
+
expect(AppMap::Util.swaggerize_path('/org(.:format)')).to eq('/org')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'ignores malformed parameter specs' do
|
31
|
+
expect(AppMap::Util.swaggerize_path('/org/o:rg_id')).to eq('/org/o:rg_id')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'ignores already swaggerized paths' do
|
35
|
+
expect(AppMap::Util.swaggerize_path('/org/{org_id}')).to eq('/org/{org_id}')
|
36
|
+
end
|
37
|
+
end
|
21
38
|
end
|
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.48.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Gilpin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05-
|
11
|
+
date: 2021-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -348,14 +348,15 @@ files:
|
|
348
348
|
- lib/appmap/event.rb
|
349
349
|
- lib/appmap/handler/function.rb
|
350
350
|
- lib/appmap/handler/net_http.rb
|
351
|
+
- lib/appmap/handler/rails/request_handler.rb
|
352
|
+
- lib/appmap/handler/rails/sql_handler.rb
|
353
|
+
- lib/appmap/handler/rails/template.rb
|
351
354
|
- lib/appmap/hook.rb
|
352
355
|
- lib/appmap/hook/method.rb
|
353
356
|
- lib/appmap/metadata.rb
|
354
357
|
- lib/appmap/middleware/remote_recording.rb
|
355
358
|
- lib/appmap/minitest.rb
|
356
359
|
- lib/appmap/open.rb
|
357
|
-
- lib/appmap/rails/request_handler.rb
|
358
|
-
- lib/appmap/rails/sql_handler.rb
|
359
360
|
- lib/appmap/railtie.rb
|
360
361
|
- lib/appmap/record.rb
|
361
362
|
- lib/appmap/rspec.rb
|
@@ -1,122 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'appmap/event'
|
4
|
-
require 'appmap/hook'
|
5
|
-
|
6
|
-
module AppMap
|
7
|
-
module Rails
|
8
|
-
module RequestHandler
|
9
|
-
class HTTPServerRequest < AppMap::Event::MethodEvent
|
10
|
-
attr_accessor :normalized_path_info, :request_method, :path_info, :params, :mime_type, :headers, :authorization
|
11
|
-
|
12
|
-
def initialize(request)
|
13
|
-
super AppMap::Event.next_id_counter, :call, Thread.current.object_id
|
14
|
-
|
15
|
-
self.request_method = request.request_method
|
16
|
-
self.normalized_path_info = normalized_path(request)
|
17
|
-
self.mime_type = request.headers['Content-Type']
|
18
|
-
self.headers = AppMap::Util.select_headers(request.env)
|
19
|
-
self.authorization = request.headers['Authorization']
|
20
|
-
self.path_info = request.path_info.split('?')[0]
|
21
|
-
# ActionDispatch::Http::ParameterFilter is deprecated
|
22
|
-
parameter_filter_cls = \
|
23
|
-
if defined?(ActiveSupport::ParameterFilter)
|
24
|
-
ActiveSupport::ParameterFilter
|
25
|
-
else
|
26
|
-
ActionDispatch::Http::ParameterFilter
|
27
|
-
end
|
28
|
-
self.params = parameter_filter_cls.new(::Rails.application.config.filter_parameters).filter(request.params)
|
29
|
-
end
|
30
|
-
|
31
|
-
def to_h
|
32
|
-
super.tap do |h|
|
33
|
-
h[:http_server_request] = {
|
34
|
-
request_method: request_method,
|
35
|
-
path_info: path_info,
|
36
|
-
mime_type: mime_type,
|
37
|
-
normalized_path_info: normalized_path_info,
|
38
|
-
authorization: authorization,
|
39
|
-
headers: headers,
|
40
|
-
}.compact
|
41
|
-
|
42
|
-
unless params.blank?
|
43
|
-
h[:message] = params.keys.map do |key|
|
44
|
-
val = params[key]
|
45
|
-
{
|
46
|
-
name: key,
|
47
|
-
class: val.class.name,
|
48
|
-
value: self.class.display_string(val),
|
49
|
-
object_id: val.__id__,
|
50
|
-
}.tap do |message|
|
51
|
-
properties = object_properties(val)
|
52
|
-
message[:properties] = properties if properties
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def normalized_path(request, router = ::Rails.application.routes.router)
|
62
|
-
router.recognize request do |route, _|
|
63
|
-
app = route.app
|
64
|
-
next unless app.matches? request
|
65
|
-
return normalized_path request, app.rack_app.routes.router if app.engine?
|
66
|
-
|
67
|
-
return route.path.spec.to_s
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
class HTTPServerResponse < AppMap::Event::MethodReturnIgnoreValue
|
73
|
-
attr_accessor :status, :mime_type, :headers
|
74
|
-
|
75
|
-
def initialize(response, parent_id, elapsed)
|
76
|
-
super AppMap::Event.next_id_counter, :return, Thread.current.object_id
|
77
|
-
|
78
|
-
self.status = response.status
|
79
|
-
self.mime_type = response.headers['Content-Type']
|
80
|
-
self.parent_id = parent_id
|
81
|
-
self.elapsed = elapsed
|
82
|
-
self.headers = AppMap::Util.select_headers(response.headers)
|
83
|
-
end
|
84
|
-
|
85
|
-
def to_h
|
86
|
-
super.tap do |h|
|
87
|
-
h[:http_server_response] = {
|
88
|
-
status_code: status,
|
89
|
-
mime_type: mime_type,
|
90
|
-
headers: headers
|
91
|
-
}.compact
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
class HookMethod < AppMap::Hook::Method
|
97
|
-
def initialize
|
98
|
-
# ActionController::Instrumentation has issued start_processing.action_controller and
|
99
|
-
# process_action.action_controller since Rails 3. Therefore it's a stable place to hook
|
100
|
-
# the request. Rails controller notifications can't be used directly because they don't
|
101
|
-
# provide response headers, and we want the Content-Type.
|
102
|
-
super(nil, ActionController::Instrumentation, ActionController::Instrumentation.instance_method(:process_action))
|
103
|
-
end
|
104
|
-
|
105
|
-
protected
|
106
|
-
|
107
|
-
def before_hook(receiver, defined_class, _) # args
|
108
|
-
call_event = HTTPServerRequest.new(receiver.request)
|
109
|
-
# http_server_request events are i/o and do not require a package name.
|
110
|
-
AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
|
111
|
-
[ call_event, TIME_NOW.call ]
|
112
|
-
end
|
113
|
-
|
114
|
-
def after_hook(receiver, call_event, start_time, _, _) # return_value, exception
|
115
|
-
elapsed = TIME_NOW.call - start_time
|
116
|
-
return_event = HTTPServerResponse.new receiver.response, call_event.id, elapsed
|
117
|
-
AppMap.tracing.record_event return_event
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
@@ -1,150 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'appmap/event'
|
4
|
-
|
5
|
-
module AppMap
|
6
|
-
module Rails
|
7
|
-
class SQLHandler
|
8
|
-
class SQLCall < AppMap::Event::MethodCall
|
9
|
-
attr_accessor :payload
|
10
|
-
|
11
|
-
def initialize(payload)
|
12
|
-
super AppMap::Event.next_id_counter, :call, Thread.current.object_id
|
13
|
-
|
14
|
-
self.payload = payload
|
15
|
-
end
|
16
|
-
|
17
|
-
def to_h
|
18
|
-
super.tap do |h|
|
19
|
-
h[:sql_query] = {
|
20
|
-
sql: payload[:sql],
|
21
|
-
database_type: payload[:database_type]
|
22
|
-
}.tap do |sql_query|
|
23
|
-
%i[server_version].each do |attribute|
|
24
|
-
sql_query[attribute] = payload[attribute] if payload[attribute]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class SQLReturn < AppMap::Event::MethodReturnIgnoreValue
|
32
|
-
def initialize(parent_id, elapsed)
|
33
|
-
super AppMap::Event.next_id_counter, :return, Thread.current.object_id
|
34
|
-
|
35
|
-
self.parent_id = parent_id
|
36
|
-
self.elapsed = elapsed
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
module SQLExaminer
|
41
|
-
class << self
|
42
|
-
def examine(payload, sql:)
|
43
|
-
return unless (examiner = build_examiner)
|
44
|
-
|
45
|
-
payload[:server_version] = examiner.server_version
|
46
|
-
payload[:database_type] = examiner.database_type.to_s
|
47
|
-
end
|
48
|
-
|
49
|
-
protected
|
50
|
-
|
51
|
-
def build_examiner
|
52
|
-
if defined?(Sequel)
|
53
|
-
SequelExaminer.new
|
54
|
-
elsif defined?(ActiveRecord)
|
55
|
-
ActiveRecordExaminer.new
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class SequelExaminer
|
61
|
-
def server_version
|
62
|
-
Sequel::Model.db.server_version
|
63
|
-
end
|
64
|
-
|
65
|
-
def database_type
|
66
|
-
Sequel::Model.db.database_type.to_sym
|
67
|
-
end
|
68
|
-
|
69
|
-
def execute_query(sql)
|
70
|
-
Sequel::Model.db[sql].all
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
class ActiveRecordExaminer
|
75
|
-
@@db_version_warning_issued = {}
|
76
|
-
|
77
|
-
def issue_warning
|
78
|
-
db_type = database_type
|
79
|
-
return if @@db_version_warning_issued[db_type]
|
80
|
-
warn("AppMap: Unable to determine database version for #{db_type.inspect}")
|
81
|
-
@@db_version_warning_issued[db_type] = true
|
82
|
-
end
|
83
|
-
|
84
|
-
def server_version
|
85
|
-
ActiveRecord::Base.connection.try(:database_version) || issue_warning
|
86
|
-
end
|
87
|
-
|
88
|
-
def database_type
|
89
|
-
type = ActiveRecord::Base.connection.adapter_name.downcase.to_sym
|
90
|
-
type = :postgres if type == :postgresql
|
91
|
-
|
92
|
-
type
|
93
|
-
end
|
94
|
-
|
95
|
-
def execute_query(sql)
|
96
|
-
ActiveRecord::Base.connection.execute(sql).inject([]) { |memo, r| memo << r; memo }
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def call(_, started, finished, _, payload) # (name, started, finished, unique_id, payload)
|
102
|
-
return if AppMap.tracing.empty?
|
103
|
-
|
104
|
-
reentry_key = "#{self.class.name}#call"
|
105
|
-
return if Thread.current[reentry_key] == true
|
106
|
-
|
107
|
-
Thread.current[reentry_key] = true
|
108
|
-
begin
|
109
|
-
sql = payload[:sql].strip
|
110
|
-
|
111
|
-
# Detect whether a function call within a specified filename is present in the call stack.
|
112
|
-
find_in_backtrace = lambda do |file_name, function_name = nil|
|
113
|
-
Thread.current.backtrace.find do |line|
|
114
|
-
tokens = line.split(':')
|
115
|
-
matches_file = tokens.find { |t| t.rindex(file_name) == (t.length - file_name.length) }
|
116
|
-
matches_function = function_name.nil? || tokens.find { |t| t == "in `#{function_name}'" }
|
117
|
-
matches_file && matches_function
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# Ignore SQL calls which are made while establishing a new connection.
|
122
|
-
#
|
123
|
-
# Example:
|
124
|
-
# /path/to/ruby/2.6.0/gems/sequel-5.20.0/lib/sequel/connection_pool.rb:122:in `make_new'
|
125
|
-
return if find_in_backtrace.call('lib/sequel/connection_pool.rb', 'make_new')
|
126
|
-
# lib/active_record/connection_adapters/abstract/connection_pool.rb:811:in `new_connection'
|
127
|
-
return if find_in_backtrace.call('lib/active_record/connection_adapters/abstract/connection_pool.rb', 'new_connection')
|
128
|
-
|
129
|
-
# Ignore SQL calls which are made while inspecting the DB schema.
|
130
|
-
#
|
131
|
-
# Example:
|
132
|
-
# /path/to/ruby/2.6.0/gems/sequel-5.20.0/lib/sequel/model/base.rb:812:in `get_db_schema'
|
133
|
-
return if find_in_backtrace.call('lib/sequel/model/base.rb', 'get_db_schema')
|
134
|
-
# /usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/model_schema.rb:466:in `load_schema!'
|
135
|
-
return if find_in_backtrace.call('lib/active_record/model_schema.rb', 'load_schema!')
|
136
|
-
return if find_in_backtrace.call('lib/active_model/attribute_methods.rb', 'define_attribute_methods')
|
137
|
-
return if find_in_backtrace.call('lib/active_record/connection_adapters/schema_cache.rb')
|
138
|
-
|
139
|
-
SQLExaminer.examine payload, sql: sql
|
140
|
-
|
141
|
-
call = SQLCall.new(payload)
|
142
|
-
AppMap.tracing.record_event(call)
|
143
|
-
AppMap.tracing.record_event(SQLReturn.new(call.id, finished - started))
|
144
|
-
ensure
|
145
|
-
Thread.current[reentry_key] = nil
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|