scout_apm 3.0.0.pre28 → 4.0.0
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 +5 -5
- data/.gitignore +1 -1
- data/.rubocop.yml +3 -4
- data/.travis.yml +17 -14
- data/CHANGELOG.markdown +124 -4
- data/Gemfile +1 -7
- data/README.markdown +13 -4
- data/Rakefile +1 -1
- data/ext/allocations/allocations.c +2 -0
- data/gems/README.md +28 -0
- data/gems/octoshark.gemfile +4 -0
- data/gems/rails3.gemfile +5 -0
- data/gems/rails4.gemfile +4 -0
- data/gems/rails5.gemfile +4 -0
- data/gems/rails6.gemfile +4 -0
- data/lib/scout_apm.rb +37 -9
- data/lib/scout_apm/agent.rb +29 -10
- data/lib/scout_apm/agent/exit_handler.rb +0 -1
- data/lib/scout_apm/agent_context.rb +22 -3
- data/lib/scout_apm/app_server_load.rb +7 -2
- data/lib/scout_apm/attribute_arranger.rb +0 -2
- data/lib/scout_apm/auto_instrument.rb +5 -0
- data/lib/scout_apm/auto_instrument/instruction_sequence.rb +31 -0
- data/lib/scout_apm/auto_instrument/layer.rb +23 -0
- data/lib/scout_apm/auto_instrument/parser.rb +27 -0
- data/lib/scout_apm/auto_instrument/rails.rb +175 -0
- data/lib/scout_apm/background_job_integrations/legacy_sneakers.rb +55 -0
- data/lib/scout_apm/background_job_integrations/que.rb +134 -0
- data/lib/scout_apm/background_job_integrations/resque.rb +6 -2
- data/lib/scout_apm/background_job_integrations/shoryuken.rb +124 -0
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +5 -19
- data/lib/scout_apm/background_job_integrations/sneakers.rb +87 -0
- data/lib/scout_apm/config.rb +45 -8
- data/lib/scout_apm/detailed_trace.rb +217 -0
- data/lib/scout_apm/environment.rb +3 -0
- data/lib/scout_apm/error.rb +27 -0
- data/lib/scout_apm/error_service.rb +32 -0
- data/lib/scout_apm/error_service/error_buffer.rb +39 -0
- data/lib/scout_apm/error_service/error_record.rb +211 -0
- data/lib/scout_apm/error_service/ignored_exceptions.rb +66 -0
- data/lib/scout_apm/error_service/middleware.rb +32 -0
- data/lib/scout_apm/error_service/notifier.rb +33 -0
- data/lib/scout_apm/error_service/payload.rb +47 -0
- data/lib/scout_apm/error_service/periodic_work.rb +17 -0
- data/lib/scout_apm/error_service/railtie.rb +11 -0
- data/lib/scout_apm/error_service/sidekiq.rb +80 -0
- data/lib/scout_apm/extensions/transaction_callback_payload.rb +1 -1
- data/lib/scout_apm/fake_store.rb +3 -0
- data/lib/scout_apm/framework_integrations/rails_2.rb +2 -1
- data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +3 -1
- data/lib/scout_apm/git_revision.rb +6 -3
- data/lib/scout_apm/instant/middleware.rb +2 -1
- data/lib/scout_apm/instrument_manager.rb +8 -7
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +3 -1
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +56 -55
- data/lib/scout_apm/instruments/action_view.rb +114 -26
- data/lib/scout_apm/instruments/active_record.rb +62 -18
- data/lib/scout_apm/instruments/http.rb +48 -0
- data/lib/scout_apm/instruments/memcached.rb +43 -0
- data/lib/scout_apm/instruments/mongoid.rb +9 -4
- data/lib/scout_apm/instruments/net_http.rb +8 -1
- data/lib/scout_apm/job_record.rb +4 -2
- data/lib/scout_apm/layaway_file.rb +4 -0
- data/lib/scout_apm/layer.rb +5 -56
- data/lib/scout_apm/layer_children_set.rb +9 -8
- data/lib/scout_apm/layer_converters/converter_base.rb +15 -30
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +12 -2
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +14 -4
- data/lib/scout_apm/layer_converters/trace_converter.rb +184 -0
- data/lib/scout_apm/limited_layer.rb +0 -7
- data/lib/scout_apm/metric_stats.rb +0 -8
- data/lib/scout_apm/middleware.rb +1 -1
- data/lib/scout_apm/periodic_work.rb +19 -0
- data/lib/scout_apm/remote/message.rb +4 -0
- data/lib/scout_apm/reporter.rb +8 -3
- data/lib/scout_apm/reporting.rb +2 -1
- data/lib/scout_apm/request_histograms.rb +8 -0
- data/lib/scout_apm/serializers/app_server_load_serializer.rb +4 -0
- data/lib/scout_apm/serializers/directive_serializer.rb +4 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +2 -2
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +30 -15
- data/lib/scout_apm/slow_job_record.rb +5 -1
- data/lib/scout_apm/slow_policy/age_policy.rb +33 -0
- data/lib/scout_apm/slow_policy/percent_policy.rb +22 -0
- data/lib/scout_apm/slow_policy/percentile_policy.rb +24 -0
- data/lib/scout_apm/slow_policy/policy.rb +21 -0
- data/lib/scout_apm/slow_policy/speed_policy.rb +16 -0
- data/lib/scout_apm/slow_request_policy.rb +18 -77
- data/lib/scout_apm/slow_transaction.rb +3 -1
- data/lib/scout_apm/store.rb +0 -1
- data/lib/scout_apm/tracked_request.rb +39 -30
- data/lib/scout_apm/utils/backtrace_parser.rb +3 -0
- data/lib/scout_apm/utils/marshal_logging.rb +90 -0
- data/lib/scout_apm/utils/sql_sanitizer.rb +10 -1
- data/lib/scout_apm/utils/sql_sanitizer_regex.rb +7 -0
- data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +6 -0
- data/lib/scout_apm/utils/unique_id.rb +27 -0
- data/lib/scout_apm/version.rb +1 -1
- data/scout_apm.gemspec +13 -7
- data/test/test_helper.rb +2 -2
- data/test/unit/agent_context_test.rb +29 -0
- data/test/unit/auto_instrument/assignments-instrumented.rb +31 -0
- data/test/unit/auto_instrument/assignments.rb +31 -0
- data/test/unit/auto_instrument/controller-ast.txt +57 -0
- data/test/unit/auto_instrument/controller-instrumented.rb +49 -0
- data/test/unit/auto_instrument/controller.rb +49 -0
- data/test/unit/auto_instrument/rescue_from-instrumented.rb +13 -0
- data/test/unit/auto_instrument/rescue_from.rb +13 -0
- data/test/unit/auto_instrument_test.rb +54 -0
- data/test/unit/error_service/error_buffer_test.rb +25 -0
- data/test/unit/error_service/ignored_exceptions_test.rb +49 -0
- data/test/unit/instruments/active_record_test.rb +40 -0
- data/test/unit/layer_children_set_test.rb +9 -0
- data/test/unit/request_histograms_test.rb +17 -0
- data/test/unit/serializers/payload_serializer_test.rb +39 -5
- data/test/unit/slow_request_policy_test.rb +41 -13
- data/test/unit/sql_sanitizer_test.rb +47 -0
- metadata +99 -19
- data/ext/stacks/extconf.rb +0 -38
- data/ext/stacks/scout_atomics.h +0 -86
- data/ext/stacks/stacks.c +0 -814
- data/lib/scout_apm/slow_job_policy.rb +0 -111
- data/lib/scout_apm/trace_compactor.rb +0 -312
- data/lib/scout_apm/utils/fake_stacks.rb +0 -88
- data/test/unit/instruments/active_record_instruments_test.rb +0 -5
- data/test/unit/slow_job_policy_test.rb +0 -6
- data/tester.rb +0 -53
data/lib/scout_apm/agent.rb
CHANGED
|
@@ -37,10 +37,13 @@ module ScoutApm
|
|
|
37
37
|
|
|
38
38
|
logger.info "Scout Agent [#{ScoutApm::VERSION}] Initialized"
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
if should_load_instruments? || force
|
|
41
|
+
instrument_manager.install!
|
|
42
|
+
install_background_job_integrations
|
|
43
|
+
install_app_server_integration
|
|
44
|
+
else
|
|
45
|
+
logger.info "Not Loading Instruments"
|
|
46
|
+
end
|
|
44
47
|
|
|
45
48
|
logger.info "Scout Agent [#{ScoutApm::VERSION}] Installed"
|
|
46
49
|
|
|
@@ -63,6 +66,7 @@ module ScoutApm
|
|
|
63
66
|
|
|
64
67
|
if context.started?
|
|
65
68
|
start_background_worker unless background_worker_running?
|
|
69
|
+
start_error_service_background_worker unless error_service_background_worker_running?
|
|
66
70
|
return
|
|
67
71
|
end
|
|
68
72
|
|
|
@@ -78,6 +82,7 @@ module ScoutApm
|
|
|
78
82
|
@app_server_load ||= AppServerLoad.new(context).run
|
|
79
83
|
|
|
80
84
|
start_background_worker
|
|
85
|
+
start_error_service_background_worker
|
|
81
86
|
end
|
|
82
87
|
|
|
83
88
|
def instrument_manager
|
|
@@ -165,12 +170,6 @@ module ScoutApm
|
|
|
165
170
|
|
|
166
171
|
ScoutApm::Agent::ExitHandler.new(context).install
|
|
167
172
|
|
|
168
|
-
if context.config.value('profile')
|
|
169
|
-
# After we fork, setup the handlers here.
|
|
170
|
-
ScoutApm::Instruments::Stacks.install
|
|
171
|
-
ScoutApm::Instruments::Stacks.start
|
|
172
|
-
end
|
|
173
|
-
|
|
174
173
|
periodic_work = ScoutApm::PeriodicWork.new(context)
|
|
175
174
|
|
|
176
175
|
@background_worker = ScoutApm::BackgroundWorker.new(context)
|
|
@@ -201,5 +200,25 @@ module ScoutApm
|
|
|
201
200
|
@background_worker &&
|
|
202
201
|
@background_worker.running?
|
|
203
202
|
end
|
|
203
|
+
|
|
204
|
+
# seconds to batch error reports
|
|
205
|
+
ERROR_SEND_FREQUENCY = 5
|
|
206
|
+
def start_error_service_background_worker
|
|
207
|
+
periodic_work = ScoutApm::ErrorService::PeriodicWork.new(context)
|
|
208
|
+
|
|
209
|
+
@error_service_background_worker = ScoutApm::BackgroundWorker.new(context, ERROR_SEND_FREQUENCY)
|
|
210
|
+
@error_service_background_worker_thread = Thread.new do
|
|
211
|
+
@error_service_background_worker.start {
|
|
212
|
+
periodic_work.run
|
|
213
|
+
}
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def error_service_background_worker_running?
|
|
218
|
+
@error_service_background_worker_thread &&
|
|
219
|
+
@error_service_background_worker_thread.alive? &&
|
|
220
|
+
@error_service_background_worker &&
|
|
221
|
+
@error_service_background_worker.running?
|
|
222
|
+
end
|
|
204
223
|
end
|
|
205
224
|
end
|
|
@@ -96,11 +96,18 @@ module ScoutApm
|
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def slow_request_policy
|
|
99
|
-
@slow_request_policy ||= ScoutApm::SlowRequestPolicy.new(self)
|
|
99
|
+
@slow_request_policy ||= ScoutApm::SlowRequestPolicy.new(self).tap{|p| p.add_default_policies }
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
def slow_job_policy
|
|
103
|
-
@slow_job_policy ||= ScoutApm::
|
|
103
|
+
@slow_job_policy ||= ScoutApm::SlowRequestPolicy.new(self).tap{|p| p.add_default_policies }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Maintains a Histogram of insignificant/significant autoinstrument layers.
|
|
107
|
+
# significant = 1
|
|
108
|
+
# insignificant = 0
|
|
109
|
+
def auto_instruments_layer_histograms
|
|
110
|
+
@auto_instruments_layer_histograms ||= ScoutApm::RequestHistograms.new
|
|
104
111
|
end
|
|
105
112
|
|
|
106
113
|
# Histogram of the cumulative requests since the start of the process
|
|
@@ -135,6 +142,18 @@ module ScoutApm
|
|
|
135
142
|
config.value('dev_trace') && environment.env == "development"
|
|
136
143
|
end
|
|
137
144
|
|
|
145
|
+
###################
|
|
146
|
+
# Error Service #
|
|
147
|
+
###################
|
|
148
|
+
|
|
149
|
+
def error_buffer
|
|
150
|
+
@error_buffer ||= ScoutApm::ErrorService::ErrorBuffer.new(self)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def ignored_exceptions
|
|
154
|
+
@ignored_exceptions ||= ScoutApm::ErrorService::IgnoredExceptions.new(self, config.value('errors_ignored_exceptions'))
|
|
155
|
+
end
|
|
156
|
+
|
|
138
157
|
#############
|
|
139
158
|
# Setters #
|
|
140
159
|
#############
|
|
@@ -205,7 +224,7 @@ module ScoutApm
|
|
|
205
224
|
if !@config.any_keys_found?
|
|
206
225
|
logger.info("No configuration file loaded, and no configuration found in ENV. " +
|
|
207
226
|
"For assistance configuring Scout, visit " +
|
|
208
|
-
"
|
|
227
|
+
"https://docs.scoutapm.com/#ruby-configuration-options")
|
|
209
228
|
end
|
|
210
229
|
end
|
|
211
230
|
end
|
|
@@ -29,12 +29,17 @@ module ScoutApm
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def data
|
|
32
|
-
{
|
|
32
|
+
{
|
|
33
|
+
:language => 'ruby',
|
|
34
|
+
:language_version => RUBY_VERSION,
|
|
35
|
+
:ruby_version => RUBY_VERSION, # Deprecated.
|
|
36
|
+
|
|
33
37
|
:framework => to_s_safe(environment.framework_integration.human_name),
|
|
34
38
|
:framework_version => to_s_safe(environment.framework_integration.version),
|
|
39
|
+
|
|
40
|
+
:server_time => to_s_safe(Time.now),
|
|
35
41
|
:environment => to_s_safe(environment.framework_integration.env),
|
|
36
42
|
:app_server => to_s_safe(environment.app_server),
|
|
37
|
-
:ruby_version => RUBY_VERSION,
|
|
38
43
|
:hostname => to_s_safe(environment.hostname),
|
|
39
44
|
:database_engine => to_s_safe(environment.database_engine), # Detected
|
|
40
45
|
:database_adapter => to_s_safe(environment.raw_database_adapter), # Raw
|
|
@@ -5,8 +5,6 @@ module ScoutApm
|
|
|
5
5
|
def self.call(subject, attributes_list)
|
|
6
6
|
attributes_list.inject({}) do |attribute_hash, attribute|
|
|
7
7
|
case attribute
|
|
8
|
-
when :traces
|
|
9
|
-
attribute_hash[attribute] = subject.traces.to_a
|
|
10
8
|
when Array
|
|
11
9
|
attribute_hash[attribute[0]] = subject.send(attribute[1])
|
|
12
10
|
when :bucket
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
require 'scout_apm/auto_instrument/rails'
|
|
3
|
+
|
|
4
|
+
module ScoutApm
|
|
5
|
+
module AutoInstrument
|
|
6
|
+
module InstructionSequence
|
|
7
|
+
def load_iseq(path)
|
|
8
|
+
if Rails.controller_path?(path) & !Rails.ignore?(path)
|
|
9
|
+
begin
|
|
10
|
+
new_code = Rails.rewrite(path)
|
|
11
|
+
return self.compile(new_code, path, path)
|
|
12
|
+
rescue
|
|
13
|
+
warn "Failed to apply auto-instrumentation to #{path}: #{$!}"
|
|
14
|
+
end
|
|
15
|
+
elsif Rails.ignore?(path)
|
|
16
|
+
warn "AutoInstruments are ignored for path=#{path}."
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
return self.compile_file(path)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# This should work (https://bugs.ruby-lang.org/issues/15572), but it doesn't.
|
|
24
|
+
# RubyVM::InstructionSequence.extend(InstructionSequence)
|
|
25
|
+
|
|
26
|
+
# So we do this instead:
|
|
27
|
+
class << ::RubyVM::InstructionSequence
|
|
28
|
+
prepend InstructionSequence
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
module ScoutApm
|
|
3
|
+
def self.AutoInstrument(name, backtrace)
|
|
4
|
+
request = ScoutApm::RequestManager.lookup
|
|
5
|
+
|
|
6
|
+
file_name, _ = backtrace.first.split(":", 2)
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
layer = ScoutApm::Layer.new('AutoInstrument', name)
|
|
10
|
+
layer.backtrace = backtrace
|
|
11
|
+
layer.file_name = file_name
|
|
12
|
+
|
|
13
|
+
request.start_layer(layer)
|
|
14
|
+
started_layer = true
|
|
15
|
+
|
|
16
|
+
result = yield
|
|
17
|
+
ensure
|
|
18
|
+
request.stop_layer if started_layer
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
return result
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
require 'parser/current'
|
|
3
|
+
raise LoadError, "Parser::TreeRewriter was not defined" unless defined?(Parser::TreeRewriter)
|
|
4
|
+
|
|
5
|
+
module ScoutApm
|
|
6
|
+
module AutoInstrument
|
|
7
|
+
class Cache
|
|
8
|
+
def initialize
|
|
9
|
+
@local_assignments = {}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def local_assignments?(node)
|
|
13
|
+
unless @local_assignments.key?(node)
|
|
14
|
+
if node.type == :lvasgn
|
|
15
|
+
@local_assignments[node] = true
|
|
16
|
+
elsif node.children.find{|child| child.is_a?(Parser::AST::Node) && self.local_assignments?(child)}
|
|
17
|
+
@local_assignments[node] = true
|
|
18
|
+
else
|
|
19
|
+
@local_assignments[node] = false
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
return @local_assignments[node]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
|
|
2
|
+
require 'scout_apm/auto_instrument/layer'
|
|
3
|
+
require 'scout_apm/auto_instrument/parser'
|
|
4
|
+
|
|
5
|
+
module ScoutApm
|
|
6
|
+
module AutoInstrument
|
|
7
|
+
module Rails
|
|
8
|
+
# A general pattern to match Rails controller files:
|
|
9
|
+
CONTROLLER_FILE = /\/app\/controllers\/*\/.*_controller.rb$/.freeze
|
|
10
|
+
|
|
11
|
+
# Some gems (Devise) provide controllers that match CONTROLLER_FILE pattern.
|
|
12
|
+
# Try a simple match to see if it's a Gemfile
|
|
13
|
+
GEM_FILE = /\/gems?\//.freeze
|
|
14
|
+
|
|
15
|
+
# Whether the given path is likely to be a Rails controller and not provided by a Gem.
|
|
16
|
+
def self.controller_path? path
|
|
17
|
+
CONTROLLER_FILE.match(path) && !GEM_FILE.match(path)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Autoinstruments increases overhead when applied to many code expressions that perform little work.
|
|
21
|
+
# You can exclude files from autoinstruments via the `auto_instruments_ignore` option.
|
|
22
|
+
def self.ignore?(path)
|
|
23
|
+
res = false
|
|
24
|
+
ScoutApm::Agent.instance.context.config.value('auto_instruments_ignore').each do |ignored_file_name|
|
|
25
|
+
if path.include?(ignored_file_name)
|
|
26
|
+
res = true
|
|
27
|
+
break
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
res
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.rewrite(path, code = nil)
|
|
34
|
+
code ||= File.read(path)
|
|
35
|
+
|
|
36
|
+
ast = ::Parser::CurrentRuby.parse(code)
|
|
37
|
+
|
|
38
|
+
# pp ast
|
|
39
|
+
|
|
40
|
+
buffer = ::Parser::Source::Buffer.new(path)
|
|
41
|
+
buffer.source = code
|
|
42
|
+
|
|
43
|
+
rewriter = Rewriter.new
|
|
44
|
+
|
|
45
|
+
# Rewrite the AST, returns a String with the new form.
|
|
46
|
+
rewriter.rewrite(buffer, ast)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class Rewriter < ::Parser::TreeRewriter
|
|
50
|
+
def initialize
|
|
51
|
+
super
|
|
52
|
+
|
|
53
|
+
# Keeps track of the parent - child relationship between nodes:
|
|
54
|
+
@nesting = []
|
|
55
|
+
|
|
56
|
+
# The stack of method nodes (type :def):
|
|
57
|
+
@method = []
|
|
58
|
+
|
|
59
|
+
# The stack of class nodes:
|
|
60
|
+
@scope = []
|
|
61
|
+
|
|
62
|
+
@cache = Cache.new
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def instrument(source, file_name, line)
|
|
66
|
+
# Don't log huge chunks of code... just the first line:
|
|
67
|
+
if lines = source.lines and lines.count > 1
|
|
68
|
+
source = lines.first.chomp + "..."
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
method_name = @method.last.children[0]
|
|
72
|
+
class_name = @scope.last.children[1]
|
|
73
|
+
bt = ["#{file_name}:#{line}:in `#{method_name}'"]
|
|
74
|
+
|
|
75
|
+
return [
|
|
76
|
+
"::ScoutApm::AutoInstrument("+ source.dump + ",#{bt}){",
|
|
77
|
+
"}"
|
|
78
|
+
]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Look up 1 or more nodes to check if the parent exists and matches the given type.
|
|
82
|
+
# @param type [Symbol] the symbol type to match.
|
|
83
|
+
# @param up [Integer] how far up to look.
|
|
84
|
+
def parent_type?(type, up = 1)
|
|
85
|
+
parent = @nesting[@nesting.size - up - 1] and parent.type == type
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def on_block(node)
|
|
89
|
+
# If we are not in a method, don't do any instrumentation:
|
|
90
|
+
return if @method.empty?
|
|
91
|
+
|
|
92
|
+
line = node.location.line || 'line?'
|
|
93
|
+
column = node.location.column || 'column?' # not used
|
|
94
|
+
method_name = node.children[0].children[1] || '*unknown*' # not used
|
|
95
|
+
file_name = @source_rewriter.source_buffer.name
|
|
96
|
+
|
|
97
|
+
wrap(node.location.expression, *instrument(node.location.expression.source, file_name, line))
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def on_mlhs(node)
|
|
101
|
+
# Ignore / don't instrument multiple assignment (LHS).
|
|
102
|
+
return
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def on_op_asgn(node)
|
|
106
|
+
process(node.children[2])
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def on_or_asgn(node)
|
|
110
|
+
process(node.children[1])
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def on_and_asgn(node)
|
|
114
|
+
process(node.children[1])
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Handle the method call AST node. If this method doesn't call `super`, no futher rewriting is applied to children.
|
|
118
|
+
def on_send(node)
|
|
119
|
+
# We aren't interested in top level function calls:
|
|
120
|
+
return if @method.empty?
|
|
121
|
+
|
|
122
|
+
if @cache.local_assignments?(node)
|
|
123
|
+
return super
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# This ignores both initial block method invocation `*x*{}`, and subsequent nested invocations `x{*y*}`:
|
|
127
|
+
return if parent_type?(:block)
|
|
128
|
+
|
|
129
|
+
# Extract useful metadata for instrumentation:
|
|
130
|
+
line = node.location.line || 'line?'
|
|
131
|
+
column = node.location.column || 'column?' # not used
|
|
132
|
+
method_name = node.children[1] || '*unknown*' # not used
|
|
133
|
+
file_name = @source_rewriter.source_buffer.name
|
|
134
|
+
|
|
135
|
+
# Wrap the expression with instrumentation:
|
|
136
|
+
wrap(node.location.expression, *instrument(node.location.expression.source, file_name, line))
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# def on_class(node)
|
|
140
|
+
# class_name = node.children[1]
|
|
141
|
+
#
|
|
142
|
+
# Kernel.const_get(class_name).ancestors.include? ActionController::Controller
|
|
143
|
+
#
|
|
144
|
+
# if class_name =~ /.../
|
|
145
|
+
# super # continue processing
|
|
146
|
+
# end
|
|
147
|
+
# end
|
|
148
|
+
|
|
149
|
+
# Invoked for every AST node as it is processed top to bottom.
|
|
150
|
+
def process(node)
|
|
151
|
+
# We are nesting inside this node:
|
|
152
|
+
@nesting.push(node)
|
|
153
|
+
|
|
154
|
+
if node and node.type == :def
|
|
155
|
+
# If the node is a method, push it on the method stack as well:
|
|
156
|
+
@method.push(node)
|
|
157
|
+
super
|
|
158
|
+
@method.pop
|
|
159
|
+
elsif node and node.type == :class
|
|
160
|
+
@scope.push(node.children[0])
|
|
161
|
+
super
|
|
162
|
+
@scope.pop
|
|
163
|
+
else
|
|
164
|
+
super
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
@nesting.pop
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Force any lazy loading to occur here, before we patch iseq_load. Otherwise you might end up in an infinite loop when rewriting code.
|
|
175
|
+
ScoutApm::AutoInstrument::Rails.rewrite('(preload)', '')
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# This is different than other BackgroundJobIntegrations and must be prepended
|
|
2
|
+
# manually in each job.
|
|
3
|
+
#
|
|
4
|
+
# class MyWorker
|
|
5
|
+
# prepend ScoutApm::BackgroundJobIntegrations::LegacySneakers
|
|
6
|
+
#
|
|
7
|
+
# def work(msg)
|
|
8
|
+
# ...
|
|
9
|
+
# end
|
|
10
|
+
# end
|
|
11
|
+
module ScoutApm
|
|
12
|
+
module BackgroundJobIntegrations
|
|
13
|
+
module LegacySneakers
|
|
14
|
+
UNKNOWN_QUEUE_PLACEHOLDER = 'default'.freeze
|
|
15
|
+
|
|
16
|
+
def self.prepended(base)
|
|
17
|
+
ScoutApm::Agent.instance.logger.info("Prepended LegacySneakers in #{base}")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(*args)
|
|
21
|
+
super
|
|
22
|
+
|
|
23
|
+
# Save off the existing value to call the correct existing work
|
|
24
|
+
# function in the instrumentation. But then override Sneakers to always
|
|
25
|
+
# use the extra-argument version, which has data Scout needs
|
|
26
|
+
@call_work = respond_to?(:work)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def work_with_params(msg, delivery_info, metadata)
|
|
30
|
+
queue = delivery_info[:routing_key] || UNKNOWN_QUEUE_PLACEHOLDER
|
|
31
|
+
job_class = self.class.name
|
|
32
|
+
req = ScoutApm::RequestManager.lookup
|
|
33
|
+
|
|
34
|
+
begin
|
|
35
|
+
req.start_layer(ScoutApm::Layer.new('Queue', queue))
|
|
36
|
+
started_queue = true
|
|
37
|
+
req.start_layer(ScoutApm::Layer.new('Job', job_class))
|
|
38
|
+
started_job = true
|
|
39
|
+
|
|
40
|
+
if @call_work
|
|
41
|
+
work(msg)
|
|
42
|
+
else
|
|
43
|
+
super
|
|
44
|
+
end
|
|
45
|
+
rescue Exception
|
|
46
|
+
req.error!
|
|
47
|
+
raise
|
|
48
|
+
ensure
|
|
49
|
+
req.stop_layer if started_job
|
|
50
|
+
req.stop_layer if started_queue
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|