scout_apm 3.0.0.pre13 → 3.0.0.pre14
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/lib/scout_apm.rb +20 -10
- data/lib/scout_apm/agent.rb +114 -319
- data/lib/scout_apm/agent/exit_handler.rb +66 -0
- data/lib/scout_apm/agent/preconditions.rb +69 -0
- data/lib/scout_apm/agent_context.rb +234 -0
- data/lib/scout_apm/app_server_load.rb +24 -14
- data/lib/scout_apm/background_job_integrations/resque.rb +7 -8
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +2 -2
- data/lib/scout_apm/background_recorder.rb +8 -3
- data/lib/scout_apm/background_worker.rb +14 -7
- data/lib/scout_apm/config.rb +35 -26
- data/lib/scout_apm/context.rb +11 -4
- data/lib/scout_apm/db_query_metric_set.rb +17 -5
- data/lib/scout_apm/debug.rb +1 -1
- data/lib/scout_apm/environment.rb +10 -14
- data/lib/scout_apm/framework_integrations/sinatra.rb +1 -1
- data/lib/scout_apm/git_revision.rb +13 -8
- data/lib/scout_apm/histogram.rb +1 -1
- data/lib/scout_apm/instant/middleware.rb +7 -7
- data/lib/scout_apm/instant_reporting.rb +7 -7
- data/lib/scout_apm/instrument_manager.rb +87 -0
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +12 -7
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +17 -12
- data/lib/scout_apm/instruments/action_view.rb +11 -7
- data/lib/scout_apm/instruments/active_record.rb +25 -11
- data/lib/scout_apm/instruments/elasticsearch.rb +10 -6
- data/lib/scout_apm/instruments/grape.rb +12 -8
- data/lib/scout_apm/instruments/http_client.rb +10 -6
- data/lib/scout_apm/instruments/influxdb.rb +10 -6
- data/lib/scout_apm/instruments/middleware_detailed.rb +11 -5
- data/lib/scout_apm/instruments/middleware_summary.rb +11 -5
- data/lib/scout_apm/instruments/mongoid.rb +10 -5
- data/lib/scout_apm/instruments/moped.rb +11 -6
- data/lib/scout_apm/instruments/net_http.rb +10 -6
- data/lib/scout_apm/instruments/percentile_sampler.rb +8 -6
- data/lib/scout_apm/instruments/process/process_cpu.rb +8 -4
- data/lib/scout_apm/instruments/process/process_memory.rb +15 -10
- data/lib/scout_apm/instruments/rails_router.rb +12 -6
- data/lib/scout_apm/instruments/redis.rb +10 -6
- data/lib/scout_apm/instruments/samplers.rb +11 -0
- data/lib/scout_apm/instruments/sinatra.rb +5 -4
- data/lib/scout_apm/layaway.rb +21 -20
- data/lib/scout_apm/layaway_file.rb +8 -3
- data/lib/scout_apm/layer.rb +3 -3
- data/lib/scout_apm/layer_converters/converter_base.rb +6 -7
- data/lib/scout_apm/layer_converters/database_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/histograms.rb +2 -2
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +4 -3
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +5 -4
- data/lib/scout_apm/logger.rb +143 -0
- data/lib/scout_apm/middleware.rb +7 -9
- data/lib/scout_apm/periodic_work.rb +28 -0
- data/lib/scout_apm/reporter.rb +14 -8
- data/lib/scout_apm/reporting.rb +135 -0
- data/lib/scout_apm/request_manager.rb +4 -6
- data/lib/scout_apm/serializers/payload_serializer.rb +1 -1
- data/lib/scout_apm/slow_job_policy.rb +6 -2
- data/lib/scout_apm/slow_job_record.rb +5 -5
- data/lib/scout_apm/slow_request_policy.rb +6 -2
- data/lib/scout_apm/slow_transaction.rb +5 -5
- data/lib/scout_apm/store.rb +22 -16
- data/lib/scout_apm/synchronous_recorder.rb +7 -3
- data/lib/scout_apm/tasks/doctor.rb +75 -0
- data/lib/scout_apm/tasks/support.rb +22 -0
- data/lib/scout_apm/tracer.rb +5 -5
- data/lib/scout_apm/tracked_request.rb +43 -19
- data/lib/scout_apm/utils/active_record_metric_name.rb +66 -8
- data/lib/scout_apm/utils/backtrace_parser.rb +1 -1
- data/lib/scout_apm/utils/installed_gems.rb +7 -3
- data/lib/scout_apm/utils/klass_helper.rb +8 -2
- data/lib/scout_apm/utils/scm.rb +1 -1
- data/lib/scout_apm/utils/sql_sanitizer.rb +3 -3
- data/lib/scout_apm/version.rb +1 -1
- data/lib/tasks/doctor.rake +11 -0
- data/scout_apm.gemspec +1 -0
- data/test/test_helper.rb +17 -2
- data/test/unit/agent_test.rb +1 -54
- data/test/unit/config_test.rb +9 -5
- data/test/unit/context_test.rb +4 -4
- data/test/unit/db_query_metric_set_test.rb +11 -4
- data/test/unit/fake_store_test.rb +1 -1
- data/test/unit/git_revision_test.rb +3 -3
- data/test/unit/instruments/net_http_test.rb +2 -1
- data/test/unit/instruments/percentile_sampler_test.rb +5 -9
- data/test/unit/layaway_test.rb +10 -5
- data/test/unit/layer_converters/metric_converter_test.rb +2 -2
- data/test/unit/slow_request_policy_test.rb +7 -3
- data/test/unit/sql_sanitizer_test.rb +0 -6
- data/test/unit/store_test.rb +11 -8
- data/test/unit/utils/active_record_metric_name_test.rb +45 -7
- metadata +27 -5
- data/lib/scout_apm/agent/logging.rb +0 -74
- data/lib/scout_apm/agent/reporting.rb +0 -129
- data/lib/scout_apm/utils/null_logger.rb +0 -13
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
module ScoutApm
|
|
6
6
|
class SynchronousRecorder
|
|
7
|
-
attr_reader :
|
|
7
|
+
attr_reader :context
|
|
8
8
|
|
|
9
|
-
def initialize(
|
|
10
|
-
@
|
|
9
|
+
def initialize(context)
|
|
10
|
+
@context = context
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def logger
|
|
14
|
+
context.logger
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def start
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module ScoutApm
|
|
2
|
+
module Tasks
|
|
3
|
+
class Doctor
|
|
4
|
+
def self.run!
|
|
5
|
+
new.run!
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def initialize()
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def run!
|
|
12
|
+
puts "Scout Doctor"
|
|
13
|
+
puts "============"
|
|
14
|
+
puts
|
|
15
|
+
puts "Detected App Server: #{agent_context.environment.app_server_integration.name}"
|
|
16
|
+
# puts "Detected Background Job: #{agent_context.environment.background_job_integration.name}"
|
|
17
|
+
puts
|
|
18
|
+
puts "Instruments:"
|
|
19
|
+
puts "----------------------------------------"
|
|
20
|
+
puts installed_instruments
|
|
21
|
+
puts
|
|
22
|
+
puts
|
|
23
|
+
puts "Configuration Settings:"
|
|
24
|
+
puts "-------------|------------------------------|-------"
|
|
25
|
+
puts " From | Key | Value "
|
|
26
|
+
puts "-------------|------------------------------|-------"
|
|
27
|
+
puts configuration_settings
|
|
28
|
+
puts
|
|
29
|
+
puts
|
|
30
|
+
puts "Misc:"
|
|
31
|
+
puts "---------------"
|
|
32
|
+
puts "Layaway Files stored at: #{agent_context.layaway.directory}"
|
|
33
|
+
puts "Logs stored at: #{log_details}"
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def agent_context
|
|
38
|
+
ScoutApm::Agent.instance.context
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def installed_instruments
|
|
42
|
+
ScoutApm::Agent.
|
|
43
|
+
instance.
|
|
44
|
+
instrument_manager.
|
|
45
|
+
installed_instruments.
|
|
46
|
+
map{|instance| "#{instance.installed? ? "Installed " : "Not Installed"} - #{instance.class.to_s}"}.
|
|
47
|
+
join("\n")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def configuration_settings
|
|
51
|
+
all_settings = agent_context.config.all_settings
|
|
52
|
+
|
|
53
|
+
longest_key = all_settings.
|
|
54
|
+
map{|setting| setting[:key] }.
|
|
55
|
+
inject(0) { |len, key| key.length > len ? key.length : len }
|
|
56
|
+
|
|
57
|
+
format_string = "%12s | %-#{longest_key}s | %s"
|
|
58
|
+
|
|
59
|
+
all_settings.
|
|
60
|
+
map{|setting| sprintf format_string, setting[:source], setting[:key], setting[:value]}.
|
|
61
|
+
join("\n")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def log_details
|
|
65
|
+
if agent_context.logger.log_destination == STDOUT
|
|
66
|
+
"STDOUT"
|
|
67
|
+
elsif agent_context.logger.log_destination == STDERR
|
|
68
|
+
"STDERR"
|
|
69
|
+
else
|
|
70
|
+
"#{agent_context.logger.log_file_path}"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module ScoutApm
|
|
2
|
+
module Tasks
|
|
3
|
+
class Support
|
|
4
|
+
def self.run!
|
|
5
|
+
puts "Support Task"
|
|
6
|
+
new.run!
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@doctor = ScoutApm::Tasks::Doctor.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def run!
|
|
14
|
+
instruments = @doctor.installed_instruments
|
|
15
|
+
config = @doctor.configuration_settings
|
|
16
|
+
collect_logs
|
|
17
|
+
|
|
18
|
+
post_data
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/scout_apm/tracer.rb
CHANGED
|
@@ -45,13 +45,13 @@ module ScoutApm
|
|
|
45
45
|
# type - "View" or "ActiveRecord" and similar
|
|
46
46
|
# name - "users/show", "App#find"
|
|
47
47
|
def instrument_method(method_name, options = {})
|
|
48
|
-
ScoutApm::Agent.instance.logger.info "Instrumenting #{method_name}"
|
|
48
|
+
ScoutApm::Agent.instance.context.logger.info "Instrumenting #{method_name}"
|
|
49
49
|
type = options[:type] || "Custom"
|
|
50
50
|
name = options[:name] || "#{self.name}/#{method_name.to_s}"
|
|
51
51
|
|
|
52
52
|
instrumented_name, uninstrumented_name = _determine_instrumented_name(method_name, type)
|
|
53
53
|
|
|
54
|
-
ScoutApm::Agent.instance.logger.info "Instrumenting #{instrumented_name}, #{uninstrumented_name}"
|
|
54
|
+
ScoutApm::Agent.instance.context.logger.info "Instrumenting #{instrumented_name}, #{uninstrumented_name}"
|
|
55
55
|
|
|
56
56
|
return if !_instrumentable?(method_name) or _instrumented?(instrumented_name, method_name)
|
|
57
57
|
|
|
@@ -91,7 +91,7 @@ module ScoutApm
|
|
|
91
91
|
name = begin
|
|
92
92
|
"#{name}"
|
|
93
93
|
rescue => e
|
|
94
|
-
ScoutApm::Agent.instance.logger.error("Error raised while interpreting instrumented name: %s, %s" % ['#{name}', e.message])
|
|
94
|
+
ScoutApm::Agent.instance.context.logger.error("Error raised while interpreting instrumented name: %s, %s" % ['#{name}', e.message])
|
|
95
95
|
"Unknown"
|
|
96
96
|
end
|
|
97
97
|
|
|
@@ -110,14 +110,14 @@ module ScoutApm
|
|
|
110
110
|
# The method must exist to be instrumented.
|
|
111
111
|
def _instrumentable?(method_name)
|
|
112
112
|
exists = method_defined?(method_name) || private_method_defined?(method_name)
|
|
113
|
-
ScoutApm::Agent.instance.logger.warn "The method [#{self.name}##{method_name}] does not exist and will not be instrumented" unless exists
|
|
113
|
+
ScoutApm::Agent.instance.context.logger.warn "The method [#{self.name}##{method_name}] does not exist and will not be instrumented" unless exists
|
|
114
114
|
exists
|
|
115
115
|
end
|
|
116
116
|
|
|
117
117
|
# +True+ if the method is already instrumented.
|
|
118
118
|
def _instrumented?(instrumented_name, method_name)
|
|
119
119
|
instrumented = method_defined?(instrumented_name)
|
|
120
|
-
ScoutApm::Agent.instance.logger.warn("The method [#{self.name}##{method_name}] has already been instrumented") if instrumented
|
|
120
|
+
ScoutApm::Agent.instance.context.logger.warn("The method [#{self.name}##{method_name}] has already been instrumented") if instrumented
|
|
121
121
|
instrumented
|
|
122
122
|
end
|
|
123
123
|
|
|
@@ -39,26 +39,23 @@ module ScoutApm
|
|
|
39
39
|
# this is set in the controller instumentation (ActionControllerRails3Rails4 according)
|
|
40
40
|
attr_accessor :instant_key
|
|
41
41
|
|
|
42
|
-
# Whereas the instant_key gets set per-request in reponse to a URL param, dev_trace is set in the config file
|
|
43
|
-
attr_accessor :dev_trace
|
|
44
|
-
|
|
45
42
|
# An object that responds to `record!(TrackedRequest)` to store this tracked request
|
|
46
43
|
attr_reader :recorder
|
|
47
44
|
|
|
48
|
-
def initialize(store)
|
|
45
|
+
def initialize(agent_context, store)
|
|
46
|
+
@agent_context = agent_context
|
|
49
47
|
@store = store #this is passed in so we can use a real store (normal operation) or fake store (instant mode only)
|
|
50
48
|
@layers = []
|
|
51
49
|
@call_set = Hash.new { |h, k| h[k] = CallSet.new }
|
|
52
50
|
@annotations = {}
|
|
53
51
|
@ignoring_children = 0
|
|
54
|
-
@context = Context.new
|
|
52
|
+
@context = Context.new(agent_context)
|
|
55
53
|
@root_layer = nil
|
|
56
54
|
@error = false
|
|
57
55
|
@stopping = false
|
|
58
56
|
@instant_key = nil
|
|
59
57
|
@mem_start = mem_usage
|
|
60
|
-
@
|
|
61
|
-
@recorder = ScoutApm::Agent.instance.recorder
|
|
58
|
+
@recorder = agent_context.recorder
|
|
62
59
|
|
|
63
60
|
ignore_request! if @recorder.nil?
|
|
64
61
|
end
|
|
@@ -113,7 +110,7 @@ module ScoutApm
|
|
|
113
110
|
if finalized?
|
|
114
111
|
stop_request
|
|
115
112
|
else
|
|
116
|
-
continue_sampling_for_layers if
|
|
113
|
+
continue_sampling_for_layers if @agent_context.config.value('profile')
|
|
117
114
|
end
|
|
118
115
|
end
|
|
119
116
|
|
|
@@ -157,9 +154,15 @@ module ScoutApm
|
|
|
157
154
|
@call_set[layer.name].update!(layer.desc)
|
|
158
155
|
end
|
|
159
156
|
|
|
157
|
+
# Grab backtraces more aggressively when running in dev trace mode
|
|
158
|
+
def backtrace_threshold
|
|
159
|
+
@agent_context.dev_trace_enabled? ? 0.05 : 0.5 # the minimum threshold in seconds to record the backtrace for a metric.
|
|
160
|
+
end
|
|
161
|
+
|
|
160
162
|
# This may be in bytes or KB based on the OSX. We store this as-is here and only do conversion to MB in Layer Converters.
|
|
163
|
+
# XXX: Move this to environment?
|
|
161
164
|
def mem_usage
|
|
162
|
-
ScoutApm::Instruments::Process::ProcessMemory.rss
|
|
165
|
+
ScoutApm::Instruments::Process::ProcessMemory.new(@agent_context).rss
|
|
163
166
|
end
|
|
164
167
|
|
|
165
168
|
def capture_mem_delta!
|
|
@@ -196,7 +199,7 @@ module ScoutApm
|
|
|
196
199
|
def stop_request
|
|
197
200
|
@stopping = true
|
|
198
201
|
|
|
199
|
-
if
|
|
202
|
+
if @agent_context.config.value('profile')
|
|
200
203
|
ScoutApm::Instruments::Stacks.stop_sampling(true)
|
|
201
204
|
ScoutApm::Instruments::Stacks.update_indexes(0, 0)
|
|
202
205
|
end
|
|
@@ -288,7 +291,7 @@ module ScoutApm
|
|
|
288
291
|
restore_store if @store.nil?
|
|
289
292
|
|
|
290
293
|
# Bail out early if the user asked us to ignore this uri
|
|
291
|
-
return if
|
|
294
|
+
return if @agent_context.ignored_uris.ignore?(annotations[:uri])
|
|
292
295
|
|
|
293
296
|
converters = [
|
|
294
297
|
LayerConverters::Histograms,
|
|
@@ -306,7 +309,7 @@ module ScoutApm
|
|
|
306
309
|
layer_finder = LayerConverters::FindLayerByType.new(self)
|
|
307
310
|
walker = LayerConverters::DepthFirstWalker.new(self.root_layer)
|
|
308
311
|
converters = converters.map do |klass|
|
|
309
|
-
instance = klass.new(self, layer_finder, @store)
|
|
312
|
+
instance = klass.new(@agent_context, self, layer_finder, @store)
|
|
310
313
|
instance.register_hooks(walker)
|
|
311
314
|
instance
|
|
312
315
|
end
|
|
@@ -319,8 +322,26 @@ module ScoutApm
|
|
|
319
322
|
trace = converter.call
|
|
320
323
|
ScoutApm::InstantReporting.new(trace, instant_key).call
|
|
321
324
|
end
|
|
325
|
+
|
|
326
|
+
if web? || job?
|
|
327
|
+
ensure_background_worker
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Ensure the background worker thread is up & running - a fallback if other
|
|
332
|
+
# detection doesn't achieve this at boot.
|
|
333
|
+
def ensure_background_worker
|
|
334
|
+
agent = ScoutApm::Agent.instance
|
|
335
|
+
agent.start
|
|
336
|
+
|
|
337
|
+
if agent.start_background_worker(:quiet)
|
|
338
|
+
agent.logger.info("Force Started BG Worker")
|
|
339
|
+
end
|
|
340
|
+
rescue => e
|
|
341
|
+
true
|
|
322
342
|
end
|
|
323
343
|
|
|
344
|
+
|
|
324
345
|
# Only call this after the request is complete
|
|
325
346
|
def unique_name
|
|
326
347
|
return nil if ignoring_request?
|
|
@@ -385,11 +406,6 @@ module ScoutApm
|
|
|
385
406
|
@ignoring_children > 0
|
|
386
407
|
end
|
|
387
408
|
|
|
388
|
-
# Grab backtraces more aggressively when running in dev trace mode
|
|
389
|
-
def backtrace_threshold
|
|
390
|
-
dev_trace ? 0.05 : 0.5 # the minimum threshold in seconds to record the backtrace for a metric.
|
|
391
|
-
end
|
|
392
|
-
|
|
393
409
|
################################################################################
|
|
394
410
|
# Ignoring the rest of a request
|
|
395
411
|
################################################################################
|
|
@@ -399,6 +415,10 @@ module ScoutApm
|
|
|
399
415
|
# layers, and delete any existing layer info. This class will still exist,
|
|
400
416
|
# and respond to methods as normal, but `record!` won't be called, and no
|
|
401
417
|
# data will be recorded.
|
|
418
|
+
#
|
|
419
|
+
# We still need to keep track of the current layer depth (via
|
|
420
|
+
# @ignoring_depth counter) so we know when to report that the class was
|
|
421
|
+
# "reported", and ready to be recreated for the next request.
|
|
402
422
|
|
|
403
423
|
def ignore_request!
|
|
404
424
|
return if @ignoring_request
|
|
@@ -434,9 +454,13 @@ module ScoutApm
|
|
|
434
454
|
end
|
|
435
455
|
|
|
436
456
|
def logger
|
|
437
|
-
|
|
457
|
+
@agent_context.logger
|
|
438
458
|
end
|
|
439
459
|
|
|
460
|
+
###########################
|
|
461
|
+
# Serialization Helpers
|
|
462
|
+
###########################
|
|
463
|
+
|
|
440
464
|
# Actually go fetch & make-real any lazily created data.
|
|
441
465
|
# Clean up any cleverness in objects.
|
|
442
466
|
# Makes this object ready to be Marshal Dumped (or otherwise serialized)
|
|
@@ -449,7 +473,7 @@ module ScoutApm
|
|
|
449
473
|
# Go re-fetch the store based on what the Agent's official one is. Used
|
|
450
474
|
# after hydrating a dumped TrackedRequest
|
|
451
475
|
def restore_store
|
|
452
|
-
@store =
|
|
476
|
+
@store = @agent_context.store
|
|
453
477
|
end
|
|
454
478
|
end
|
|
455
479
|
end
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
module ScoutApm
|
|
2
2
|
module Utils
|
|
3
3
|
class ActiveRecordMetricName
|
|
4
|
-
DEFAULT_METRIC = "SQL/Unknown"
|
|
5
|
-
|
|
6
4
|
attr_reader :sql, :name
|
|
5
|
+
DEFAULT_METRIC = 'SQL/other'.freeze
|
|
7
6
|
|
|
8
7
|
def initialize(sql, name)
|
|
9
|
-
@sql = sql
|
|
8
|
+
@sql = sql || ""
|
|
10
9
|
@name = name.to_s
|
|
11
10
|
end
|
|
12
11
|
|
|
@@ -17,13 +16,11 @@ module ScoutApm
|
|
|
17
16
|
# name: Place Load
|
|
18
17
|
# metric_name: Place/find
|
|
19
18
|
def to_s
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if parsed = parse_operation
|
|
19
|
+
parsed = parse_operation
|
|
20
|
+
if parsed
|
|
24
21
|
"#{model}/#{parsed}"
|
|
25
22
|
else
|
|
26
|
-
|
|
23
|
+
regex_name(sql)
|
|
27
24
|
end
|
|
28
25
|
end
|
|
29
26
|
|
|
@@ -76,6 +73,67 @@ module ScoutApm
|
|
|
76
73
|
end
|
|
77
74
|
end
|
|
78
75
|
end
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
########################
|
|
79
|
+
# Regex based naming #
|
|
80
|
+
########################
|
|
81
|
+
#
|
|
82
|
+
WHITE_SPACE = '\s*'
|
|
83
|
+
REGEX_OPERATION = '(SELECT|UPDATE|INSERT|DELETE)'
|
|
84
|
+
FROM = 'FROM'
|
|
85
|
+
INTO = 'INTO'
|
|
86
|
+
NON_GREEDY_CONSUME = '.*?'
|
|
87
|
+
TABLE = '(?:"|`)?(.*?)(?:"|`)?\s'
|
|
88
|
+
COUNT = 'COUNT\(.*?\)'
|
|
89
|
+
|
|
90
|
+
SELECT_REGEX = /\A#{WHITE_SPACE}(SELECT)#{WHITE_SPACE}(#{COUNT})?#{NON_GREEDY_CONSUME}#{FROM}#{WHITE_SPACE}#{TABLE}/i.freeze
|
|
91
|
+
UPDATE_REGEX = /\A#{WHITE_SPACE}(UPDATE)#{WHITE_SPACE}#{TABLE}/i.freeze
|
|
92
|
+
INSERT_REGEX = /\A#{WHITE_SPACE}(INSERT)#{WHITE_SPACE}#{INTO}#{WHITE_SPACE}#{TABLE}/i.freeze
|
|
93
|
+
DELETE_REGEX = /\A#{WHITE_SPACE}(DELETE)#{WHITE_SPACE}#{FROM}#{TABLE}/i.freeze
|
|
94
|
+
|
|
95
|
+
COUNT_LABEL = 'count'.freeze
|
|
96
|
+
SELECT_LABEL = 'find'.freeze
|
|
97
|
+
UPDATE_LABEL = 'save'.freeze
|
|
98
|
+
INSERT_LABEL = 'create'.freeze
|
|
99
|
+
DELETE_LABEL = 'destroy'.freeze
|
|
100
|
+
UNKNOWN_LABEL = 'SQL/other'.freeze
|
|
101
|
+
|
|
102
|
+
# Attempt to do some basic parsing of SQL via regexes to extract the SQL
|
|
103
|
+
# verb (select, update, etc) and the table being operated on.
|
|
104
|
+
#
|
|
105
|
+
# This is a fallback from what ActiveRecord gives us, we prefer its
|
|
106
|
+
# names. But sometimes it is giving us a no-name query, and we have to
|
|
107
|
+
# attempt to figure it out ourselves.
|
|
108
|
+
#
|
|
109
|
+
# This relies on ActiveSupport's classify method. If it's not present,
|
|
110
|
+
# just skip the attempt to rename here. This could happen in a Grape or
|
|
111
|
+
# Sinatra application that doesn't import ActiveSupport. At this point,
|
|
112
|
+
# you're already using ActiveRecord, so it's likely loaded anyway.
|
|
113
|
+
def regex_name(sql)
|
|
114
|
+
# We rely on the ActiveSupport inflections code here. Bail early if we can't use it.
|
|
115
|
+
return UNKNOWN_LABEL unless UNKNOWN_LABEL.respond_to?(:classify)
|
|
116
|
+
|
|
117
|
+
if match = SELECT_REGEX.match(sql)
|
|
118
|
+
operation =
|
|
119
|
+
if match[2]
|
|
120
|
+
COUNT_LABEL
|
|
121
|
+
else
|
|
122
|
+
SELECT_LABEL
|
|
123
|
+
end
|
|
124
|
+
"#{match[3].classify}/#{operation}"
|
|
125
|
+
elsif match = UPDATE_REGEX.match(sql)
|
|
126
|
+
"#{match[2].classify}/#{UPDATE_LABEL}"
|
|
127
|
+
elsif match = INSERT_REGEX.match(sql)
|
|
128
|
+
"#{match[2].classify}/#{INSERT_LABEL}"
|
|
129
|
+
elsif match = DELETE_REGEX.match(sql)
|
|
130
|
+
"#{match[2].classify}/#{DELETE_LABEL}"
|
|
131
|
+
else
|
|
132
|
+
UNKNOWN_LABEL
|
|
133
|
+
end
|
|
134
|
+
rescue
|
|
135
|
+
UNKNOWN_LABEL
|
|
136
|
+
end
|
|
79
137
|
end
|
|
80
138
|
end
|
|
81
139
|
end
|
|
@@ -11,7 +11,7 @@ module ScoutApm
|
|
|
11
11
|
|
|
12
12
|
attr_reader :call_stack
|
|
13
13
|
|
|
14
|
-
def initialize(call_stack, root=ScoutApm::
|
|
14
|
+
def initialize(call_stack, root=ScoutApm::Agent.instance.context.environment.root)
|
|
15
15
|
@call_stack = call_stack
|
|
16
16
|
# We can't use a constant as it'd be too early to fetch environment info
|
|
17
17
|
#
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
module ScoutApm
|
|
2
2
|
module Utils
|
|
3
3
|
class InstalledGems
|
|
4
|
-
attr_reader :
|
|
4
|
+
attr_reader :context
|
|
5
5
|
|
|
6
|
-
def initialize(
|
|
7
|
-
@
|
|
6
|
+
def initialize(context)
|
|
7
|
+
@context = context
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def logger
|
|
11
|
+
context.logger
|
|
8
12
|
end
|
|
9
13
|
|
|
10
14
|
def run
|
|
@@ -5,6 +5,12 @@ module ScoutApm
|
|
|
5
5
|
# KlassHelper.defined?("ActiveRecord::Base") #=> true / false
|
|
6
6
|
|
|
7
7
|
def self.defined?(*names)
|
|
8
|
+
lookup(*names) != :missing_class
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# KlassHelper.lookup("ActiveRecord::Base") => ActiveRecord::Base
|
|
12
|
+
# KlassHelper.lookup("ActiveRecord::SomethingThatDoesNotExist") => :missing_class
|
|
13
|
+
def self.lookup(*names)
|
|
8
14
|
if names.length == 1
|
|
9
15
|
names = names[0].split("::")
|
|
10
16
|
end
|
|
@@ -15,11 +21,11 @@ module ScoutApm
|
|
|
15
21
|
begin
|
|
16
22
|
obj = obj.const_get(name)
|
|
17
23
|
rescue NameError
|
|
18
|
-
return
|
|
24
|
+
return :missing_class
|
|
19
25
|
end
|
|
20
26
|
end
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
obj
|
|
23
29
|
end
|
|
24
30
|
end
|
|
25
31
|
end
|