scout_apm 4.0.3 → 4.1.2
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/.github/workflows/test.yml +4 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.markdown +30 -0
- data/gems/typhoeus.gemfile +3 -0
- data/lib/scout_apm/agent/preconditions.rb +3 -3
- data/lib/scout_apm/auto_instrument/rails.rb +0 -1
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -1
- data/lib/scout_apm/background_job_integrations/faktory.rb +103 -0
- data/lib/scout_apm/background_job_integrations/shoryuken.rb +2 -0
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +13 -2
- data/lib/scout_apm/config.rb +4 -0
- data/lib/scout_apm/environment.rb +2 -1
- data/lib/scout_apm/error_service/middleware.rb +2 -2
- data/lib/scout_apm/error_service/payload.rb +1 -1
- data/lib/scout_apm/error_service.rb +3 -1
- data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +5 -1
- data/lib/scout_apm/ignored_uris.rb +3 -1
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +5 -2
- data/lib/scout_apm/instruments/action_view.rb +13 -5
- data/lib/scout_apm/instruments/active_record.rb +2 -0
- data/lib/scout_apm/instruments/typhoeus.rb +8 -6
- data/lib/scout_apm/layer_converters/find_layer_by_type.rb +4 -0
- data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +2 -0
- data/lib/scout_apm/logger.rb +1 -1
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +11 -25
- data/lib/scout_apm/tracer.rb +2 -2
- data/lib/scout_apm/utils/sql_sanitizer.rb +30 -6
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +2 -0
- data/test/unit/auto_instrument/anonymous_block_value.rb +7 -0
- data/test/unit/auto_instrument/hanging_method.rb +6 -0
- data/test/unit/auto_instrument_test.rb +8 -0
- data/test/unit/ignored_uris_test.rb +6 -0
- data/test/unit/instruments/typhoeus_test.rb +42 -0
- data/test/unit/remote/{test_message.rb → message_test.rb} +0 -0
- data/test/unit/remote/{test_router.rb → route_test.rb} +0 -0
- data/test/unit/remote/{test_server.rb → server_test.rb} +4 -1
- data/test/unit/sql_sanitizer_test.rb +17 -2
- data/test/unit/tracer_test.rb +25 -0
- metadata +11 -8
- data/lib/scout_apm/utils/sql_sanitizer_regex.rb +0 -32
- data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +0 -32
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd64c67ede26abdf0cf372b50d0cf025531d5719353b033160e9497fe2abf94f
|
|
4
|
+
data.tar.gz: c31ae7044cce7eb1d869ef8d1130f269b17b53864c9ad8f2c114d822478a1eda
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9c12b178b6b6f4ee7ea6add58a5e2b13d362684c7825441e7164dcb878ec75befa4312efd7566dab95f5461401f4591eaa2bc86f477ec4776fed175bdb3f6321
|
|
7
|
+
data.tar.gz: 3b832fc6ab0c0e676d574e29e37f15f849ddf886ea1d76972bdf81b42431d9daab5caacbc2867b2b22dfb71c3a31ae94d6b64372a297de0f5ec384d7d9ef45bc
|
data/.github/workflows/test.yml
CHANGED
|
@@ -26,6 +26,9 @@ jobs:
|
|
|
26
26
|
- ruby: 2.4
|
|
27
27
|
- ruby: 2.5
|
|
28
28
|
- ruby: 2.6
|
|
29
|
+
- ruby: 2.6
|
|
30
|
+
gemfile: gems/typhoeus.gemfile
|
|
31
|
+
test_features: "typhoeus"
|
|
29
32
|
- ruby: 2.6
|
|
30
33
|
gemfile: gems/octoshark.gemfile
|
|
31
34
|
- ruby: 2.6
|
|
@@ -36,6 +39,7 @@ jobs:
|
|
|
36
39
|
|
|
37
40
|
env:
|
|
38
41
|
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
|
42
|
+
SCOUT_TEST_FEATURES: ${{ matrix.test_features }}
|
|
39
43
|
|
|
40
44
|
runs-on: ubuntu-latest
|
|
41
45
|
|
data/.travis.yml
CHANGED
data/CHANGELOG.markdown
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
# 4.1.2
|
|
2
|
+
|
|
3
|
+
* Add record_queue_time configuration (PR #422)
|
|
4
|
+
|
|
5
|
+
# 4.1.1
|
|
6
|
+
|
|
7
|
+
* Fix issue with Typheous Hydra instrument (#418)
|
|
8
|
+
|
|
9
|
+
# 4.1.0
|
|
10
|
+
|
|
11
|
+
* Preload Celluloid in Shoryuken instrumentation (#331)
|
|
12
|
+
* Fix deprecation warning in Rails 6.1+ (#365)
|
|
13
|
+
* Set Typheous's desc more directly (#392)
|
|
14
|
+
* Delegate to ActiveRecord #log more intelligently (#394)
|
|
15
|
+
* Don't delay starting agent when possible (#397)
|
|
16
|
+
* Fix template naming issue in Rails 6+ (#399)
|
|
17
|
+
* Avoid double-counting issue with AutoInstruments (#405)
|
|
18
|
+
* Renaming test files for Remote::{Server|Route|Message} to be included in test run (#409)
|
|
19
|
+
* More robust naming of Sidekiq jobs (#412)
|
|
20
|
+
* Allow render_template instruments to work with older Rails (#413)
|
|
21
|
+
* Fix function to manually capture exceptions (#415)
|
|
22
|
+
* Enhance SQL Sanitization (#417)
|
|
23
|
+
|
|
24
|
+
# 4.0.4
|
|
25
|
+
|
|
26
|
+
* Add Faktory Support (#385)
|
|
27
|
+
* Remove Regexp hack for 1.8.7 (no longer supported) (#384)
|
|
28
|
+
* More robust DelayedJob detection (#382)
|
|
29
|
+
* Fix kwargs handling in Tracing module (#381)
|
|
30
|
+
|
|
1
31
|
# 4.0.3
|
|
2
32
|
|
|
3
33
|
* Handle edge case with nil Typhoeus current-layer (#380)
|
|
@@ -27,10 +27,10 @@ module ScoutApm
|
|
|
27
27
|
PRECONDITION_DETECTED_SERVER = {
|
|
28
28
|
:message => proc {|environ| "Deferring agent start. Standing by for first request" },
|
|
29
29
|
:check => proc { |context|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
app_server_found = context.environment.app_server_integration(true).found?
|
|
31
|
+
background_job_integration_found = context.environment.background_job_integrations.length > 0
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
app_server_found || background_job_integration_found
|
|
34
34
|
},
|
|
35
35
|
:severity => :info,
|
|
36
36
|
},
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module ScoutApm
|
|
2
|
+
module BackgroundJobIntegrations
|
|
3
|
+
class Faktory
|
|
4
|
+
attr_reader :logger
|
|
5
|
+
|
|
6
|
+
def name
|
|
7
|
+
:faktory
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def present?
|
|
11
|
+
defined?(::Faktory)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def forking?
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def install
|
|
19
|
+
add_middleware
|
|
20
|
+
install_processor
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def add_middleware
|
|
24
|
+
::Faktory.configure_worker do |config|
|
|
25
|
+
config.worker_middleware do |chain|
|
|
26
|
+
chain.add FaktoryMiddleware
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def install_processor
|
|
32
|
+
require 'faktory/processor' # sidekiq v4 has not loaded this file by this point
|
|
33
|
+
|
|
34
|
+
::Faktory::Processor.class_eval do
|
|
35
|
+
def initialize_with_scout(*args)
|
|
36
|
+
agent = ::ScoutApm::Agent.instance
|
|
37
|
+
agent.start
|
|
38
|
+
initialize_without_scout(*args)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
alias_method :initialize_without_scout, :initialize
|
|
42
|
+
alias_method :initialize, :initialize_with_scout
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# We insert this middleware into the Sidekiq stack, to capture each job,
|
|
48
|
+
# and time them.
|
|
49
|
+
class FaktoryMiddleware
|
|
50
|
+
def call(worker_instance, job)
|
|
51
|
+
queue = job["queue"]
|
|
52
|
+
|
|
53
|
+
req = ScoutApm::RequestManager.lookup
|
|
54
|
+
req.annotate_request(:queue_latency => latency(job))
|
|
55
|
+
|
|
56
|
+
begin
|
|
57
|
+
req.start_layer(ScoutApm::Layer.new('Queue', queue))
|
|
58
|
+
started_queue = true
|
|
59
|
+
req.start_layer(ScoutApm::Layer.new('Job', job_class(job)))
|
|
60
|
+
started_job = true
|
|
61
|
+
|
|
62
|
+
yield
|
|
63
|
+
rescue
|
|
64
|
+
req.error!
|
|
65
|
+
raise
|
|
66
|
+
ensure
|
|
67
|
+
req.stop_layer if started_job
|
|
68
|
+
req.stop_layer if started_queue
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
UNKNOWN_CLASS_PLACEHOLDER = 'UnknownJob'.freeze
|
|
73
|
+
ACTIVE_JOB_KLASS = 'ActiveJob::QueueAdapters::FaktoryAdapter::JobWrapper'.freeze
|
|
74
|
+
|
|
75
|
+
def job_class(job)
|
|
76
|
+
job_class = job.fetch('jobtype', UNKNOWN_CLASS_PLACEHOLDER)
|
|
77
|
+
|
|
78
|
+
if job_class == ACTIVE_JOB_KLASS && job.key?('custom') && job['custom'].key?('wrapped')
|
|
79
|
+
begin
|
|
80
|
+
job_class = job['custom']['wrapped']
|
|
81
|
+
rescue
|
|
82
|
+
ACTIVE_JOB_KLASS
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
job_class
|
|
87
|
+
rescue
|
|
88
|
+
UNKNOWN_CLASS_PLACEHOLDER
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def latency(job, time = Time.now)
|
|
92
|
+
created_at = Time.parse(job['enqueued_at'] || job['created_at'])
|
|
93
|
+
if created_at
|
|
94
|
+
(time - created_at)
|
|
95
|
+
else
|
|
96
|
+
0
|
|
97
|
+
end
|
|
98
|
+
rescue
|
|
99
|
+
0
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -37,6 +37,8 @@ module ScoutApm
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def install_processor
|
|
40
|
+
# celluloid has not loaded by this point and older versions of `shorykuen/processor` assume that it did
|
|
41
|
+
require 'celluloid' if defined?(::Shoryuken::VERSION) && ::Shoryuken::VERSION < '3'
|
|
40
42
|
require 'shoryuken/processor' # sidekiq v4 has not loaded this file by this point
|
|
41
43
|
|
|
42
44
|
::Shoryuken::Processor.class_eval do
|
|
@@ -80,12 +80,23 @@ module ScoutApm
|
|
|
80
80
|
DELAYED_WRAPPER_KLASS = 'Sidekiq::Extensions::DelayedClass'.freeze
|
|
81
81
|
|
|
82
82
|
|
|
83
|
+
# Capturing the class name is a little tricky, since we need to handle several cases:
|
|
84
|
+
# 1. ActiveJob, with the class in the key 'wrapped'
|
|
85
|
+
# 2. ActiveJob, but the 'wrapped' key is wrong (due to YAJL serializing weirdly), find it in args.job_class
|
|
86
|
+
# 3. DelayedJob wrapper, deserializing using YAML into the real object, which can be introspected
|
|
87
|
+
# 4. No wrapper, just sidekiq's class
|
|
83
88
|
def job_class(msg)
|
|
84
89
|
job_class = msg.fetch('class', UNKNOWN_CLASS_PLACEHOLDER)
|
|
85
90
|
|
|
86
|
-
if job_class == ACTIVE_JOB_KLASS && msg.key?('wrapped')
|
|
91
|
+
if job_class == ACTIVE_JOB_KLASS && msg.key?('wrapped') && msg['wrapped'].is_a?(String)
|
|
87
92
|
begin
|
|
88
|
-
job_class = msg['wrapped']
|
|
93
|
+
job_class = msg['wrapped'].to_s
|
|
94
|
+
rescue
|
|
95
|
+
ACTIVE_JOB_KLASS
|
|
96
|
+
end
|
|
97
|
+
elsif job_class == ACTIVE_JOB_KLASS && msg.try(:[], 'args').try(:[], 'job_class')
|
|
98
|
+
begin
|
|
99
|
+
job_class = msg['args']['job_class'].to_s
|
|
89
100
|
rescue
|
|
90
101
|
ACTIVE_JOB_KLASS
|
|
91
102
|
end
|
data/lib/scout_apm/config.rb
CHANGED
|
@@ -29,6 +29,7 @@ require 'scout_apm/environment'
|
|
|
29
29
|
# report_format - 'json' or 'marshal'. Marshal is legacy and will be removed.
|
|
30
30
|
# scm_subdirectory - if the app root lives in source management in a subdirectory. E.g. #{SCM_ROOT}/src
|
|
31
31
|
# uri_reporting - 'path' or 'full_path' default is 'full_path', which reports URL params as well as the path.
|
|
32
|
+
# record_queue_time - true/false to enable recording of queuetime.
|
|
32
33
|
# remote_agent_host - Internal: What host to bind to, and also send messages to for remote. Default: 127.0.0.1.
|
|
33
34
|
# remote_agent_port - What port to bind the remote webserver to
|
|
34
35
|
# start_resque_server_instrument - Used in special situations with certain Resque installs
|
|
@@ -69,6 +70,7 @@ module ScoutApm
|
|
|
69
70
|
'name',
|
|
70
71
|
'profile',
|
|
71
72
|
'proxy',
|
|
73
|
+
'record_queue_time',
|
|
72
74
|
'remote_agent_host',
|
|
73
75
|
'remote_agent_port',
|
|
74
76
|
'report_format',
|
|
@@ -178,6 +180,7 @@ module ScoutApm
|
|
|
178
180
|
'database_metric_limit' => IntegerCoercion.new,
|
|
179
181
|
'database_metric_report_limit' => IntegerCoercion.new,
|
|
180
182
|
'instrument_http_url_length' => IntegerCoercion.new,
|
|
183
|
+
'record_queue_time' => BooleanCoercion.new,
|
|
181
184
|
'start_resque_server_instrument' => BooleanCoercion.new,
|
|
182
185
|
'timeline_traces' => BooleanCoercion.new,
|
|
183
186
|
'auto_instruments' => BooleanCoercion.new,
|
|
@@ -292,6 +295,7 @@ module ScoutApm
|
|
|
292
295
|
'instrument_http_url_length' => 300,
|
|
293
296
|
'start_resque_server_instrument' => true, # still only starts if Resque is detected
|
|
294
297
|
'collect_remote_ip' => true,
|
|
298
|
+
'record_queue_time' => true,
|
|
295
299
|
'timeline_traces' => true,
|
|
296
300
|
'auto_instruments' => false,
|
|
297
301
|
'auto_instruments_ignore' => [],
|
|
@@ -30,6 +30,7 @@ module ScoutApm
|
|
|
30
30
|
ScoutApm::BackgroundJobIntegrations::Sneakers.new,
|
|
31
31
|
ScoutApm::BackgroundJobIntegrations::DelayedJob.new,
|
|
32
32
|
ScoutApm::BackgroundJobIntegrations::Que.new,
|
|
33
|
+
ScoutApm::BackgroundJobIntegrations::Faktory.new,
|
|
33
34
|
]
|
|
34
35
|
|
|
35
36
|
FRAMEWORK_INTEGRATIONS = [
|
|
@@ -197,7 +198,7 @@ module ScoutApm
|
|
|
197
198
|
ruby_2? || ruby_3?
|
|
198
199
|
end
|
|
199
200
|
|
|
200
|
-
# Returns true if this Ruby version
|
|
201
|
+
# Returns true if this Ruby version makes positional and keyword arguments incompatible
|
|
201
202
|
def supports_kwarg_delegation?
|
|
202
203
|
ruby_3? || (ruby_2? && ruby_minor >= 7)
|
|
203
204
|
end
|
|
@@ -9,10 +9,10 @@ module ScoutApm
|
|
|
9
9
|
begin
|
|
10
10
|
response = @app.call(env)
|
|
11
11
|
rescue Exception => exception
|
|
12
|
-
puts "[Scout Error Service] Caught Exception: #{exception.class.name}"
|
|
13
|
-
|
|
14
12
|
context = ScoutApm::Agent.instance.context
|
|
15
13
|
|
|
14
|
+
context.logger.debug "[Scout Error Service] Caught Exception: #{exception.class.name}"
|
|
15
|
+
|
|
16
16
|
# Bail out early, and reraise if the error is not interesting.
|
|
17
17
|
if context.ignored_exceptions.ignored?(exception)
|
|
18
18
|
raise
|
|
@@ -16,7 +16,9 @@ module ScoutApm
|
|
|
16
16
|
# Used internally by SidekiqException
|
|
17
17
|
def self.capture(exception, params = {})
|
|
18
18
|
return if disabled?
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
context = ScoutApm::Agent.instance.context
|
|
21
|
+
return if context.ignored_exceptions.ignore?(exception)
|
|
20
22
|
|
|
21
23
|
context.errors_buffer.capture(exception, env)
|
|
22
24
|
end
|
|
@@ -71,7 +71,11 @@ module ScoutApm
|
|
|
71
71
|
#
|
|
72
72
|
# We avoid this issue by not calling .respond_to? here, and instead using the less optimal `rescue nil` approach
|
|
73
73
|
def raw_database_adapter
|
|
74
|
-
adapter = ActiveRecord::Base.
|
|
74
|
+
adapter = ActiveRecord::Base.connection_db_config.configuration_hash[:adapter] rescue nil
|
|
75
|
+
|
|
76
|
+
if adapter.nil?
|
|
77
|
+
adapter = ActiveRecord::Base.connection_config[:adapter].to_s rescue nil
|
|
78
|
+
end
|
|
75
79
|
|
|
76
80
|
if adapter.nil?
|
|
77
81
|
adapter = ActiveRecord::Base.configurations[env]["adapter"]
|
|
@@ -4,7 +4,9 @@ module ScoutApm
|
|
|
4
4
|
attr_reader :regex
|
|
5
5
|
|
|
6
6
|
def initialize(prefixes)
|
|
7
|
-
regexes = Array(prefixes).
|
|
7
|
+
regexes = Array(prefixes).
|
|
8
|
+
reject{|prefix| prefix == ""}.
|
|
9
|
+
map {|prefix| %r{\A#{prefix}} }
|
|
8
10
|
@regex = Regexp.union(*regexes)
|
|
9
11
|
end
|
|
10
12
|
|
|
@@ -95,8 +95,11 @@ module ScoutApm
|
|
|
95
95
|
req.instant_key = instant_key
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
# Don't start a new layer if ActionController::API or
|
|
99
|
+
# ActionController::Base handled it already. Needs to account for
|
|
100
|
+
# any layers started during a around_action (most likely
|
|
101
|
+
# AutoInstrument, but could be another custom instrument)
|
|
102
|
+
if current_layer && (current_layer.type == "Controller" || current_layer.type == "AutoInstrument" || req.web?)
|
|
100
103
|
super
|
|
101
104
|
else
|
|
102
105
|
begin
|
|
@@ -83,7 +83,7 @@ module ScoutApm
|
|
|
83
83
|
maybe_template = args[1]
|
|
84
84
|
|
|
85
85
|
template_name = @template.virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
|
86
|
-
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.3
|
|
86
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.3.5
|
|
87
87
|
template_name ||= "Unknown Partial"
|
|
88
88
|
|
|
89
89
|
layer_name = template_name + "/Rendering"
|
|
@@ -105,7 +105,10 @@ module ScoutApm
|
|
|
105
105
|
def collection_with_template(*args, **kwargs)
|
|
106
106
|
req = ScoutApm::RequestManager.lookup
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
maybe_template = args[1]
|
|
109
|
+
|
|
110
|
+
template_name = @template.virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
|
111
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.3.5
|
|
109
112
|
template_name ||= "Unknown Collection"
|
|
110
113
|
layer_name = template_name + "/Rendering"
|
|
111
114
|
|
|
@@ -126,10 +129,15 @@ module ScoutApm
|
|
|
126
129
|
end
|
|
127
130
|
|
|
128
131
|
module ActionViewTemplateRendererInstruments
|
|
129
|
-
|
|
132
|
+
# Don't forward kwargs here, since Rails 3, 4, 5, 6 don't use them, and
|
|
133
|
+
# it causes annoyances in the instrumentation
|
|
134
|
+
def render_template(*args)
|
|
130
135
|
req = ScoutApm::RequestManager.lookup
|
|
131
136
|
|
|
132
|
-
|
|
137
|
+
maybe_template = args[1]
|
|
138
|
+
|
|
139
|
+
template_name = args[0].virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
|
140
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.1.3
|
|
133
141
|
template_name ||= "Unknown"
|
|
134
142
|
layer_name = template_name + "/Rendering"
|
|
135
143
|
|
|
@@ -138,7 +146,7 @@ module ScoutApm
|
|
|
138
146
|
|
|
139
147
|
begin
|
|
140
148
|
req.start_layer(layer)
|
|
141
|
-
super(*args
|
|
149
|
+
super(*args)
|
|
142
150
|
ensure
|
|
143
151
|
req.stop_layer
|
|
144
152
|
end
|
|
@@ -215,6 +215,7 @@ module ScoutApm
|
|
|
215
215
|
end
|
|
216
216
|
end
|
|
217
217
|
end
|
|
218
|
+
ruby2_keywords :log if respond_to?(:ruby2_keywords, true)
|
|
218
219
|
end
|
|
219
220
|
|
|
220
221
|
module ActiveRecordInstruments
|
|
@@ -267,6 +268,7 @@ module ScoutApm
|
|
|
267
268
|
end
|
|
268
269
|
end
|
|
269
270
|
end
|
|
271
|
+
ruby2_keywords :log if respond_to?(:ruby2_keywords, true)
|
|
270
272
|
end
|
|
271
273
|
|
|
272
274
|
################################################################################
|
|
@@ -29,10 +29,11 @@ module ScoutApm
|
|
|
29
29
|
|
|
30
30
|
module TyphoeusHydraInstrumentation
|
|
31
31
|
def run(*args, &block)
|
|
32
|
+
layer = ScoutApm::Layer.new("HTTP", "Hydra")
|
|
33
|
+
layer.desc = scout_desc
|
|
34
|
+
|
|
32
35
|
req = ScoutApm::RequestManager.lookup
|
|
33
|
-
req.start_layer(
|
|
34
|
-
current_layer = req.current_layer
|
|
35
|
-
current_layer.desc = scout_desc if current_layer
|
|
36
|
+
req.start_layer(layer)
|
|
36
37
|
|
|
37
38
|
begin
|
|
38
39
|
super(*args, &block)
|
|
@@ -50,10 +51,11 @@ module ScoutApm
|
|
|
50
51
|
|
|
51
52
|
module TyphoeusInstrumentation
|
|
52
53
|
def run(*args, &block)
|
|
54
|
+
layer = ScoutApm::Layer.new("HTTP", scout_request_verb)
|
|
55
|
+
layer.desc = scout_desc(scout_request_verb, scout_request_url)
|
|
56
|
+
|
|
53
57
|
req = ScoutApm::RequestManager.lookup
|
|
54
|
-
req.start_layer(
|
|
55
|
-
current_layer = req.current_layer
|
|
56
|
-
current_layer.desc = scout_desc(scout_request_verb, scout_request_url) if current_layer
|
|
58
|
+
req.start_layer(layer)
|
|
57
59
|
|
|
58
60
|
begin
|
|
59
61
|
super(*args, &block)
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
# show
|
|
6
6
|
# render :update
|
|
7
7
|
# end
|
|
8
|
+
|
|
9
|
+
# This doesn't cache the negative result when searching for a controller / job,
|
|
10
|
+
# so that we can ask again later after more of the request has occurred and
|
|
11
|
+
# correctly find it.
|
|
8
12
|
module ScoutApm
|
|
9
13
|
module LayerConverters
|
|
10
14
|
class FindLayerByType
|
data/lib/scout_apm/logger.rb
CHANGED
|
@@ -45,31 +45,17 @@ module ScoutApm
|
|
|
45
45
|
"{#{str_parts.join(",")}}"
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
'\\' => '\\\\',
|
|
60
|
-
}
|
|
61
|
-
else
|
|
62
|
-
ESCAPE_MAPPINGS = {
|
|
63
|
-
# Stackoverflow answer on gsub matches and backslashes - https://stackoverflow.com/a/4149087/2705125
|
|
64
|
-
'\\' => '\\\\\\\\',
|
|
65
|
-
"\b" => '\\b',
|
|
66
|
-
"\t" => '\\t',
|
|
67
|
-
"\n" => '\\n',
|
|
68
|
-
"\f" => '\\f',
|
|
69
|
-
"\r" => '\\r',
|
|
70
|
-
'"' => '\\"',
|
|
71
|
-
}
|
|
72
|
-
end
|
|
48
|
+
ESCAPE_MAPPINGS = {
|
|
49
|
+
# Stackoverflow answer on gsub matches and backslashes
|
|
50
|
+
# https://stackoverflow.com/a/4149087/2705125
|
|
51
|
+
'\\' => '\\\\\\\\',
|
|
52
|
+
"\b" => '\\b',
|
|
53
|
+
"\t" => '\\t',
|
|
54
|
+
"\n" => '\\n',
|
|
55
|
+
"\f" => '\\f',
|
|
56
|
+
"\r" => '\\r',
|
|
57
|
+
'"' => '\\"',
|
|
58
|
+
}
|
|
73
59
|
|
|
74
60
|
def escape(string)
|
|
75
61
|
ESCAPE_MAPPINGS.inject(string.to_s) {|s, (bad, good)|
|
data/lib/scout_apm/tracer.rb
CHANGED
|
@@ -91,7 +91,7 @@ module ScoutApm
|
|
|
91
91
|
|
|
92
92
|
def _instrumented_method_string(instrumented_name, uninstrumented_name, type, name, options={})
|
|
93
93
|
method_str = <<-EOF
|
|
94
|
-
def #{instrumented_name}(*args, &block)
|
|
94
|
+
def #{instrumented_name}(*args#{", **kwargs" if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?}, &block)
|
|
95
95
|
name = begin
|
|
96
96
|
"#{name}"
|
|
97
97
|
rescue => e
|
|
@@ -103,7 +103,7 @@ module ScoutApm
|
|
|
103
103
|
name,
|
|
104
104
|
{:scope => #{options[:scope] || false}}
|
|
105
105
|
) do
|
|
106
|
-
#{uninstrumented_name}(*args, &block)
|
|
106
|
+
#{uninstrumented_name}(*args#{", **kwargs" if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?}, &block)
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
109
|
EOF
|
|
@@ -5,12 +5,34 @@ require 'scout_apm/environment'
|
|
|
5
5
|
module ScoutApm
|
|
6
6
|
module Utils
|
|
7
7
|
class SqlSanitizer
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
MULTIPLE_SPACES = %r|\s+|.freeze
|
|
9
|
+
MULTIPLE_QUESTIONS = /\?(,\?)+/.freeze
|
|
10
|
+
|
|
11
|
+
PSQL_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*\z|.freeze
|
|
12
|
+
PSQL_REMOVE_STRINGS = /'(?:[^']|'')*'/.freeze
|
|
13
|
+
PSQL_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
|
|
14
|
+
PSQL_AFTER_SELECT = /(?:SELECT\s+).*?(?:WHERE|FROM\z)/im.freeze # Should be everything between a FROM and a WHERE
|
|
15
|
+
PSQL_PLACEHOLDER = /\$\d+/.freeze
|
|
16
|
+
PSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
|
17
|
+
PSQL_AFTER_FROM = /(?:FROM\s+).*?(?:WHERE|\z)/im.freeze # Should be everything between a FROM and a WHERE
|
|
18
|
+
PSQL_AFTER_JOIN = /(?:JOIN\s+).*?\z/im.freeze
|
|
19
|
+
PSQL_AFTER_WHERE = /(?:WHERE\s+).*?(?:SELECT|\z)/im.freeze
|
|
20
|
+
PSQL_AFTER_SET = /(?:SET\s+).*?(?:WHERE|\z)/im.freeze
|
|
21
|
+
|
|
22
|
+
MYSQL_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*$|.freeze
|
|
23
|
+
MYSQL_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
|
|
24
|
+
MYSQL_REMOVE_SINGLE_QUOTE_STRINGS = %r{'(?:\\'|[^']|'')*'}.freeze
|
|
25
|
+
MYSQL_REMOVE_DOUBLE_QUOTE_STRINGS = %r{"(?:\\"|[^"]|"")*"}.freeze
|
|
26
|
+
MYSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
|
27
|
+
|
|
28
|
+
SQLITE_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*$|.freeze
|
|
29
|
+
SQLITE_REMOVE_STRINGS = /'(?:[^']|'')*'/.freeze
|
|
30
|
+
SQLITE_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
|
|
31
|
+
|
|
32
|
+
# => "EXEC sp_executesql N'SELECT [users].* FROM [users] WHERE (age > 50) ORDER BY [users].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 10"
|
|
33
|
+
SQLSERVER_EXECUTESQL = /EXEC sp_executesql N'(.*?)'.*/
|
|
34
|
+
SQLSERVER_REMOVE_INTEGERS = /(?<!LIMIT )\b(?<!@)\d+\b/.freeze
|
|
35
|
+
SQLSERVER_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
|
14
36
|
|
|
15
37
|
attr_accessor :database_engine
|
|
16
38
|
|
|
@@ -50,7 +72,9 @@ module ScoutApm
|
|
|
50
72
|
def to_s_postgres
|
|
51
73
|
sql.gsub!(PSQL_PLACEHOLDER, '?')
|
|
52
74
|
sql.gsub!(PSQL_VAR_INTERPOLATION, '')
|
|
75
|
+
# sql.gsub!(PSQL_REMOVE_STRINGS, '?')
|
|
53
76
|
sql.gsub!(PSQL_AFTER_WHERE) {|c| c.gsub(PSQL_REMOVE_STRINGS, '?')}
|
|
77
|
+
sql.gsub!(PSQL_AFTER_JOIN) {|c| c.gsub(PSQL_REMOVE_STRINGS, '?')}
|
|
54
78
|
sql.gsub!(PSQL_AFTER_SET) {|c| c.gsub(PSQL_REMOVE_STRINGS, '?')}
|
|
55
79
|
sql.gsub!(PSQL_REMOVE_INTEGERS, '?')
|
|
56
80
|
sql.gsub!(PSQL_IN_CLAUSE, 'IN (?)')
|
data/lib/scout_apm/version.rb
CHANGED
data/lib/scout_apm.rb
CHANGED
|
@@ -58,6 +58,7 @@ require 'scout_apm/server_integrations/webrick'
|
|
|
58
58
|
require 'scout_apm/server_integrations/null'
|
|
59
59
|
|
|
60
60
|
require 'scout_apm/background_job_integrations/sidekiq'
|
|
61
|
+
require 'scout_apm/background_job_integrations/faktory'
|
|
61
62
|
require 'scout_apm/background_job_integrations/delayed_job'
|
|
62
63
|
require 'scout_apm/background_job_integrations/resque'
|
|
63
64
|
require 'scout_apm/background_job_integrations/shoryuken'
|
|
@@ -192,6 +193,7 @@ require 'scout_apm/tasks/support'
|
|
|
192
193
|
require 'scout_apm/extensions/config'
|
|
193
194
|
require 'scout_apm/extensions/transaction_callback_payload'
|
|
194
195
|
|
|
196
|
+
require 'scout_apm/error'
|
|
195
197
|
require 'scout_apm/error_service'
|
|
196
198
|
require 'scout_apm/error_service/middleware'
|
|
197
199
|
require 'scout_apm/error_service/notifier'
|
|
@@ -51,4 +51,12 @@ class AutoInstrumentTest < Minitest::Test
|
|
|
51
51
|
assert_equal instrumented_source("assignments"),
|
|
52
52
|
normalize_backtrace(::ScoutApm::AutoInstrument::Rails.rewrite(source_path("assignments")))
|
|
53
53
|
end
|
|
54
|
+
|
|
55
|
+
def test_hanging_method_rewrite
|
|
56
|
+
::ScoutApm::AutoInstrument::Rails.rewrite(source_path("hanging_method"))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_anonymous_block_value
|
|
60
|
+
::ScoutApm::AutoInstrument::Rails.rewrite(source_path("anonymous_block_value"))
|
|
61
|
+
end
|
|
54
62
|
end if defined? ScoutApm::AutoInstrument
|
|
@@ -13,4 +13,10 @@ class IgnoredUrlsTest < Minitest::Test
|
|
|
13
13
|
i = ScoutApm::IgnoredUris.new(["/slow", "/health"])
|
|
14
14
|
assert_equal false, i.ignore?("/users/2/health")
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
def test_does_not_ignore_empty_string
|
|
18
|
+
i = ScoutApm::IgnoredUris.new(["", "/admin"])
|
|
19
|
+
assert_equal false, i.ignore?("/users/2/health")
|
|
20
|
+
assert_equal true, i.ignore?("/admin/dashboard")
|
|
21
|
+
end
|
|
16
22
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
if (ENV["SCOUT_TEST_FEATURES"] || "").include?("typhoeus")
|
|
2
|
+
require 'test_helper'
|
|
3
|
+
|
|
4
|
+
require 'scout_apm/instruments/typhoeus'
|
|
5
|
+
|
|
6
|
+
require 'typhoeus'
|
|
7
|
+
|
|
8
|
+
class TyphoeusTest < Minitest::Test
|
|
9
|
+
def setup
|
|
10
|
+
@context = ScoutApm::AgentContext.new
|
|
11
|
+
@recorder = FakeRecorder.new
|
|
12
|
+
ScoutApm::Agent.instance.context.recorder = @recorder
|
|
13
|
+
ScoutApm::Instruments::Typhoeus.new(@context).install
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_instruments_typhoeus_hydra
|
|
17
|
+
hydra = Typhoeus::Hydra.new
|
|
18
|
+
2.times.map{ hydra.queue(Typhoeus::Request.new("example.com", followlocation: true)) }
|
|
19
|
+
|
|
20
|
+
assert_equal "2 requests", hydra.scout_desc
|
|
21
|
+
|
|
22
|
+
hydra.run
|
|
23
|
+
assert_equal "0 requests", hydra.scout_desc
|
|
24
|
+
assert_recorded(@recorder, "HTTP", "Hydra", "2 requests")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_instruments_typhoeus
|
|
28
|
+
Typhoeus.get("example.com", followlocation: true)
|
|
29
|
+
assert_recorded(@recorder, "HTTP", "get", "example.com")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def assert_recorded(recorder, type, name, desc = nil)
|
|
35
|
+
req = recorder.requests.first
|
|
36
|
+
assert req, "recorder recorded no layers"
|
|
37
|
+
assert_equal type, req.root_layer.type
|
|
38
|
+
assert_equal name, req.root_layer.name
|
|
39
|
+
assert_equal desc, req.root_layer.desc if !desc.nil?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
File without changes
|
|
File without changes
|
|
@@ -8,8 +8,11 @@ class TestRemoteServer < Minitest::Test
|
|
|
8
8
|
logger_io = StringIO.new
|
|
9
9
|
server = ScoutApm::Remote::Server.new(bind, port, router, Logger.new(logger_io))
|
|
10
10
|
|
|
11
|
+
# Cannot test this if we can't require webrick. Ruby 3 stopped including by default
|
|
12
|
+
skip unless server.require_webrick
|
|
13
|
+
|
|
11
14
|
server.start
|
|
12
|
-
sleep 0.
|
|
15
|
+
sleep 0.05 # Let the server finish starting. The assert should instead allow a time
|
|
13
16
|
assert server.running?
|
|
14
17
|
end
|
|
15
18
|
end
|
|
@@ -38,9 +38,9 @@ module ScoutApm
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def test_postgres_strips_subquery_strings
|
|
41
|
-
raw_sql = %q|"SELECT
|
|
41
|
+
raw_sql = %q|"SELECT "orgs".* FROM "orgs" WHERE "orgs"."name" = 'Scout' AND "orgs"."created_by_user_id" IN (SELECT "users"."id" FROM "users" WHERE (id > AVG(id)) AND "type" = 'USER' AND "created_at" BETWEEN '2019-04-17 12:28:00.000000' AND '2019-04-18 12:28:00.000000')"|
|
|
42
42
|
sanitized_sql = SqlSanitizer.new(raw_sql).tap { |it| it.database_engine = :postgres}
|
|
43
|
-
expected_sql = %q|"SELECT
|
|
43
|
+
expected_sql = %q|"SELECT "orgs".* FROM "orgs" WHERE "orgs"."name" = ? AND "orgs"."created_by_user_id" IN (SELECT "users"."id" FROM "users" WHERE (id > AVG(id)) AND "type" = ? AND "created_at" BETWEEN ? AND ?)"|
|
|
44
44
|
assert_equal expected_sql, sanitized_sql.to_s
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -66,6 +66,21 @@ module ScoutApm
|
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
def test_postgres_inner_join_subquery
|
|
70
|
+
sql = %q{SELECT x AS y
|
|
71
|
+
FROM t1
|
|
72
|
+
INNER JOIN (
|
|
73
|
+
SELECT id,
|
|
74
|
+
(ts_rank((to_tsvector('simple', coalesce("pg_search_documents"."content"::text, ''))), (to_tsquery('simple', 'xyz' || 'omg' || 'secret')), ?)) AS rank
|
|
75
|
+
FROM t2
|
|
76
|
+
WHERE name = 'literal') sub ON sub.id = t1.id WHERE age > 10}
|
|
77
|
+
|
|
78
|
+
expected = %q{SELECT x AS y FROM t1 INNER JOIN ( SELECT id, (ts_rank((to_tsvector(?, coalesce("pg_search_documents"."content"::text, ?))), (to_tsquery(?, ? || ? || ?)), ?)) AS rank FROM t2 WHERE name = ?) sub ON sub.id = t1.id WHERE age > ?}
|
|
79
|
+
ss = SqlSanitizer.new(sql).tap{ |it| it.database_engine = :postgres }
|
|
80
|
+
|
|
81
|
+
assert_equal expected, ss.to_s
|
|
82
|
+
end
|
|
83
|
+
|
|
69
84
|
def test_mysql_where
|
|
70
85
|
sql = %q|SELECT `users`.* FROM `users` WHERE `users`.`name` = ? [["name", "chris"]]|
|
|
71
86
|
ss = SqlSanitizer.new(sql).tap{ |it| it.database_engine = :mysql }
|
data/test/unit/tracer_test.rb
CHANGED
|
@@ -65,6 +65,31 @@ class TracerTest < Minitest::Test
|
|
|
65
65
|
assert_recorded(recorder, "Test", "name")
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
+
if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?
|
|
69
|
+
def test_instrument_method_with_keyword_args
|
|
70
|
+
initial_value = Warning[:deprecated]
|
|
71
|
+
Warning[:deprecated] = true
|
|
72
|
+
recorder = FakeRecorder.new
|
|
73
|
+
ScoutApm::Agent.instance.context.recorder = recorder
|
|
74
|
+
|
|
75
|
+
klass = Class.new { include ScoutApm::Tracer }
|
|
76
|
+
|
|
77
|
+
invoked = false
|
|
78
|
+
klass.send(:define_method, :work) { |run:| invoked = true }
|
|
79
|
+
klass.instrument_method(:work, :type => "Test", :name => "name")
|
|
80
|
+
|
|
81
|
+
args = { run: false }
|
|
82
|
+
assert_output(nil, '') do
|
|
83
|
+
klass.new.work(**args)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
assert invoked, "instrumented code was not invoked"
|
|
87
|
+
assert_recorded(recorder, "Test", "name")
|
|
88
|
+
ensure
|
|
89
|
+
Warning[:deprecated] = initial_value
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
68
93
|
private
|
|
69
94
|
|
|
70
95
|
def assert_recorded(recorder, type, name)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: scout_apm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Derek Haynes
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2021-
|
|
12
|
+
date: 2021-08-23 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: minitest
|
|
@@ -238,6 +238,7 @@ files:
|
|
|
238
238
|
- gems/rails4.gemfile
|
|
239
239
|
- gems/rails5.gemfile
|
|
240
240
|
- gems/rails6.gemfile
|
|
241
|
+
- gems/typhoeus.gemfile
|
|
241
242
|
- lib/scout_apm.rb
|
|
242
243
|
- lib/scout_apm/agent.rb
|
|
243
244
|
- lib/scout_apm/agent/exit_handler.rb
|
|
@@ -251,6 +252,7 @@ files:
|
|
|
251
252
|
- lib/scout_apm/auto_instrument/parser.rb
|
|
252
253
|
- lib/scout_apm/auto_instrument/rails.rb
|
|
253
254
|
- lib/scout_apm/background_job_integrations/delayed_job.rb
|
|
255
|
+
- lib/scout_apm/background_job_integrations/faktory.rb
|
|
254
256
|
- lib/scout_apm/background_job_integrations/legacy_sneakers.rb
|
|
255
257
|
- lib/scout_apm/background_job_integrations/que.rb
|
|
256
258
|
- lib/scout_apm/background_job_integrations/resque.rb
|
|
@@ -397,8 +399,6 @@ files:
|
|
|
397
399
|
- lib/scout_apm/utils/numbers.rb
|
|
398
400
|
- lib/scout_apm/utils/scm.rb
|
|
399
401
|
- lib/scout_apm/utils/sql_sanitizer.rb
|
|
400
|
-
- lib/scout_apm/utils/sql_sanitizer_regex.rb
|
|
401
|
-
- lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb
|
|
402
402
|
- lib/scout_apm/utils/time.rb
|
|
403
403
|
- lib/scout_apm/utils/unique_id.rb
|
|
404
404
|
- lib/scout_apm/version.rb
|
|
@@ -409,11 +409,13 @@ files:
|
|
|
409
409
|
- test/tmp/README.md
|
|
410
410
|
- test/unit/agent_context_test.rb
|
|
411
411
|
- test/unit/agent_test.rb
|
|
412
|
+
- test/unit/auto_instrument/anonymous_block_value.rb
|
|
412
413
|
- test/unit/auto_instrument/assignments-instrumented.rb
|
|
413
414
|
- test/unit/auto_instrument/assignments.rb
|
|
414
415
|
- test/unit/auto_instrument/controller-ast.txt
|
|
415
416
|
- test/unit/auto_instrument/controller-instrumented.rb
|
|
416
417
|
- test/unit/auto_instrument/controller.rb
|
|
418
|
+
- test/unit/auto_instrument/hanging_method.rb
|
|
417
419
|
- test/unit/auto_instrument/rescue_from-instrumented.rb
|
|
418
420
|
- test/unit/auto_instrument/rescue_from.rb
|
|
419
421
|
- test/unit/auto_instrument_test.rb
|
|
@@ -434,6 +436,7 @@ files:
|
|
|
434
436
|
- test/unit/instruments/active_record_test.rb
|
|
435
437
|
- test/unit/instruments/net_http_test.rb
|
|
436
438
|
- test/unit/instruments/percentile_sampler_test.rb
|
|
439
|
+
- test/unit/instruments/typhoeus_test.rb
|
|
437
440
|
- test/unit/layaway_test.rb
|
|
438
441
|
- test/unit/layer_children_set_test.rb
|
|
439
442
|
- test/unit/layer_converters/depth_first_walker_test.rb
|
|
@@ -442,9 +445,9 @@ files:
|
|
|
442
445
|
- test/unit/limited_layer_test.rb
|
|
443
446
|
- test/unit/logger_test.rb
|
|
444
447
|
- test/unit/metric_set_test.rb
|
|
445
|
-
- test/unit/remote/
|
|
446
|
-
- test/unit/remote/
|
|
447
|
-
- test/unit/remote/
|
|
448
|
+
- test/unit/remote/message_test.rb
|
|
449
|
+
- test/unit/remote/route_test.rb
|
|
450
|
+
- test/unit/remote/server_test.rb
|
|
448
451
|
- test/unit/request_histograms_test.rb
|
|
449
452
|
- test/unit/scored_item_set_test.rb
|
|
450
453
|
- test/unit/serializers/payload_serializer_test.rb
|
|
@@ -479,7 +482,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
479
482
|
- !ruby/object:Gem::Version
|
|
480
483
|
version: '0'
|
|
481
484
|
requirements: []
|
|
482
|
-
rubygems_version: 3.0.
|
|
485
|
+
rubygems_version: 3.0.3
|
|
483
486
|
signing_key:
|
|
484
487
|
specification_version: 4
|
|
485
488
|
summary: Ruby application performance monitoring
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module ScoutApm
|
|
3
|
-
module Utils
|
|
4
|
-
module SqlRegex
|
|
5
|
-
MULTIPLE_SPACES = %r|\s+|.freeze
|
|
6
|
-
MULTIPLE_QUESTIONS = /\?(,\?)+/.freeze
|
|
7
|
-
|
|
8
|
-
PSQL_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*\z|.freeze
|
|
9
|
-
PSQL_REMOVE_STRINGS = /'(?:[^']|'')*'/.freeze
|
|
10
|
-
PSQL_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
|
|
11
|
-
PSQL_PLACEHOLDER = /\$\d+/.freeze
|
|
12
|
-
PSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
|
13
|
-
PSQL_AFTER_WHERE = /(?:WHERE\s+).*?(?:SELECT|\z)/im.freeze
|
|
14
|
-
PSQL_AFTER_SET = /(?:SET\s+).*?(?:WHERE|\z)/im.freeze
|
|
15
|
-
|
|
16
|
-
MYSQL_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*$|.freeze
|
|
17
|
-
MYSQL_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
|
|
18
|
-
MYSQL_REMOVE_SINGLE_QUOTE_STRINGS = %r{'(?:\\'|[^']|'')*'}.freeze
|
|
19
|
-
MYSQL_REMOVE_DOUBLE_QUOTE_STRINGS = %r{"(?:\\"|[^"]|"")*"}.freeze
|
|
20
|
-
MYSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
|
21
|
-
|
|
22
|
-
SQLITE_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*$|.freeze
|
|
23
|
-
SQLITE_REMOVE_STRINGS = /'(?:[^']|'')*'/.freeze
|
|
24
|
-
SQLITE_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
|
|
25
|
-
|
|
26
|
-
# => "EXEC sp_executesql N'SELECT [users].* FROM [users] WHERE (age > 50) ORDER BY [users].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 10"
|
|
27
|
-
SQLSERVER_EXECUTESQL = /EXEC sp_executesql N'(.*?)'.*/
|
|
28
|
-
SQLSERVER_REMOVE_INTEGERS = /(?<!LIMIT )\b(?<!@)\d+\b/.freeze
|
|
29
|
-
SQLSERVER_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module ScoutApm
|
|
3
|
-
module Utils
|
|
4
|
-
module SqlRegex
|
|
5
|
-
MULTIPLE_SPACES = %r|\s+|.freeze
|
|
6
|
-
MULTIPLE_QUESTIONS = /\?(,\?)+/.freeze
|
|
7
|
-
|
|
8
|
-
PSQL_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*$|.freeze
|
|
9
|
-
PSQL_REMOVE_STRINGS = /'(?:[^']|'')*'/.freeze
|
|
10
|
-
PSQL_REMOVE_INTEGERS = /\b\d+\b/.freeze
|
|
11
|
-
PSQL_PLACEHOLDER = /\$\d+/.freeze
|
|
12
|
-
PSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
|
13
|
-
PSQL_AFTER_WHERE = /(?:WHERE\s+).*?(?:SELECT|$)/i.freeze
|
|
14
|
-
PSQL_AFTER_SET = /(?:SET\s+).*?(?:WHERE|$)/i.freeze
|
|
15
|
-
|
|
16
|
-
MYSQL_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*$|.freeze
|
|
17
|
-
MYSQL_REMOVE_INTEGERS = /\b\d+\b/.freeze
|
|
18
|
-
MYSQL_REMOVE_SINGLE_QUOTE_STRINGS = /'(?:\\'|[^']|'')*'/.freeze
|
|
19
|
-
MYSQL_REMOVE_DOUBLE_QUOTE_STRINGS = /"(?:\\"|[^"]|"")*"/.freeze
|
|
20
|
-
MYSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
|
21
|
-
|
|
22
|
-
SQLITE_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*$|.freeze
|
|
23
|
-
SQLITE_REMOVE_STRINGS = /'(?:[^']|'')*'/.freeze
|
|
24
|
-
SQLITE_REMOVE_INTEGERS = /\b\d+\b/.freeze
|
|
25
|
-
|
|
26
|
-
# This is not officially supported, but will do its best.
|
|
27
|
-
SQLSERVER_EXECUTESQL = /EXEC sp_executesql N'(.*?)'.*/
|
|
28
|
-
SQLSERVER_REMOVE_INTEGERS = /\b\d+\b/.freeze
|
|
29
|
-
SQLSERVER_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|