scout_apm 3.0.0.pre28 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|