scout_apm 4.0.1 → 4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +4 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.markdown +35 -0
- data/gems/typhoeus.gemfile +3 -0
- data/lib/scout_apm.rb +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/environment.rb +2 -1
- data/lib/scout_apm/error_service.rb +3 -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/framework_integrations/rails_3_or_4.rb +5 -1
- data/lib/scout_apm/ignored_uris.rb +3 -1
- data/lib/scout_apm/instrument_manager.rb +1 -0
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +5 -2
- data/lib/scout_apm/instruments/action_view.rb +13 -9
- data/lib/scout_apm/instruments/active_record.rb +2 -0
- data/lib/scout_apm/instruments/typhoeus.rb +90 -0
- data/lib/scout_apm/layer_converters/find_layer_by_type.rb +4 -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/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 -7
- 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: 8b7247aa1c6545f1bad63c0f911359ba038686c9cf15b30d560ca887467956ef
|
4
|
+
data.tar.gz: 1fd60f4f23521c1274d27cbf710886bbb5af1baa922ddb3642254917c4db9061
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1581588299fe697107ce3fd8be5575bd63dbc811cf1b6827e08e666ff7e24a9f857366592e632a441e53daea21914c9a9ec7dfdc8acaa1cfaabccde9636b3b56
|
7
|
+
data.tar.gz: 41c2b41c8e7875188f671b59ad23aa62974b2e0c2ae3d08e93366e81158809b14b8b4c8f217c198e39d311ec8ef1921ec672da726a095702c1b7d7521ab0f7f3
|
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,38 @@
|
|
1
|
+
# 4.1.1
|
2
|
+
|
3
|
+
* Fix issue with Typheous Hydra instrument (#418)
|
4
|
+
|
5
|
+
# 4.1.0
|
6
|
+
|
7
|
+
* Preload Celluloid in Shoryuken instrumentation (#331)
|
8
|
+
* Fix deprecation warning in Rails 6.1+ (#365)
|
9
|
+
* Set Typheous's desc more directly (#392)
|
10
|
+
* Delegate to ActiveRecord #log more intelligently (#394)
|
11
|
+
* Don't delay starting agent when possible (#397)
|
12
|
+
* Fix template naming issue in Rails 6+ (#399)
|
13
|
+
* Avoid double-counting issue with AutoInstruments (#405)
|
14
|
+
* Renaming test files for Remote::{Server|Route|Message} to be included in test run (#409)
|
15
|
+
* More robust naming of Sidekiq jobs (#412)
|
16
|
+
* Allow render_template instruments to work with older Rails (#413)
|
17
|
+
* Fix function to manually capture exceptions (#415)
|
18
|
+
* Enhance SQL Sanitization (#417)
|
19
|
+
|
20
|
+
# 4.0.4
|
21
|
+
|
22
|
+
* Add Faktory Support (#385)
|
23
|
+
* Remove Regexp hack for 1.8.7 (no longer supported) (#384)
|
24
|
+
* More robust DelayedJob detection (#382)
|
25
|
+
* Fix kwargs handling in Tracing module (#381)
|
26
|
+
|
27
|
+
# 4.0.3
|
28
|
+
|
29
|
+
* Handle edge case with nil Typhoeus current-layer (#380)
|
30
|
+
* Fix args passing to render_partial (#379)
|
31
|
+
|
32
|
+
# 4.0.2
|
33
|
+
|
34
|
+
* Add Typhoeus instrumentation (#376)
|
35
|
+
|
1
36
|
# 4.0.1
|
2
37
|
|
3
38
|
* Add support for Ruby 3.0 (#374)
|
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'
|
@@ -78,6 +79,7 @@ require 'scout_apm/histogram'
|
|
78
79
|
|
79
80
|
require 'scout_apm/instruments/net_http'
|
80
81
|
require 'scout_apm/instruments/http_client'
|
82
|
+
require 'scout_apm/instruments/typhoeus'
|
81
83
|
require 'scout_apm/instruments/moped'
|
82
84
|
require 'scout_apm/instruments/mongoid'
|
83
85
|
require 'scout_apm/instruments/memcached'
|
@@ -191,6 +193,7 @@ require 'scout_apm/tasks/support'
|
|
191
193
|
require 'scout_apm/extensions/config'
|
192
194
|
require 'scout_apm/extensions/transaction_callback_payload'
|
193
195
|
|
196
|
+
require 'scout_apm/error'
|
194
197
|
require 'scout_apm/error_service'
|
195
198
|
require 'scout_apm/error_service/middleware'
|
196
199
|
require 'scout_apm/error_service/notifier'
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
|
@@ -30,6 +30,7 @@ module ScoutApm
|
|
30
30
|
install_instrument(ScoutApm::Instruments::Moped)
|
31
31
|
install_instrument(ScoutApm::Instruments::Mongoid)
|
32
32
|
install_instrument(ScoutApm::Instruments::NetHttp)
|
33
|
+
install_instrument(ScoutApm::Instruments::Typhoeus)
|
33
34
|
install_instrument(ScoutApm::Instruments::HttpClient)
|
34
35
|
install_instrument(ScoutApm::Instruments::Memcached)
|
35
36
|
install_instrument(ScoutApm::Instruments::Redis)
|
@@ -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,11 +146,7 @@ module ScoutApm
|
|
138
146
|
|
139
147
|
begin
|
140
148
|
req.start_layer(layer)
|
141
|
-
|
142
|
-
super(*args, **kwargs)
|
143
|
-
else
|
144
|
-
super(*args)
|
145
|
-
end
|
149
|
+
super(*args)
|
146
150
|
ensure
|
147
151
|
req.stop_layer
|
148
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
|
################################################################################
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module Instruments
|
3
|
+
class Typhoeus
|
4
|
+
attr_reader :context
|
5
|
+
|
6
|
+
def initialize(context)
|
7
|
+
@context = context
|
8
|
+
@installed = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def logger
|
12
|
+
context.logger
|
13
|
+
end
|
14
|
+
|
15
|
+
def installed?
|
16
|
+
@installed
|
17
|
+
end
|
18
|
+
|
19
|
+
def install
|
20
|
+
if defined?(::Typhoeus)
|
21
|
+
@installed = true
|
22
|
+
|
23
|
+
logger.info "Instrumenting Typhoeus"
|
24
|
+
|
25
|
+
::Typhoeus::Request.send(:prepend, TyphoeusInstrumentation)
|
26
|
+
::Typhoeus::Hydra.send(:prepend, TyphoeusHydraInstrumentation)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module TyphoeusHydraInstrumentation
|
31
|
+
def run(*args, &block)
|
32
|
+
layer = ScoutApm::Layer.new("HTTP", "Hydra")
|
33
|
+
layer.desc = scout_desc
|
34
|
+
|
35
|
+
req = ScoutApm::RequestManager.lookup
|
36
|
+
req.start_layer(layer)
|
37
|
+
|
38
|
+
begin
|
39
|
+
super(*args, &block)
|
40
|
+
ensure
|
41
|
+
req.stop_layer
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def scout_desc
|
46
|
+
"#{self.queued_requests.count} requests"
|
47
|
+
rescue
|
48
|
+
""
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module TyphoeusInstrumentation
|
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
|
+
|
57
|
+
req = ScoutApm::RequestManager.lookup
|
58
|
+
req.start_layer(layer)
|
59
|
+
|
60
|
+
begin
|
61
|
+
super(*args, &block)
|
62
|
+
ensure
|
63
|
+
req.stop_layer
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def scout_desc(verb, uri)
|
68
|
+
max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
|
69
|
+
(String(uri).split('?').first)[0..(max_length - 1)]
|
70
|
+
rescue
|
71
|
+
""
|
72
|
+
end
|
73
|
+
|
74
|
+
def scout_request_url
|
75
|
+
self.url
|
76
|
+
rescue
|
77
|
+
""
|
78
|
+
end
|
79
|
+
|
80
|
+
def scout_request_verb
|
81
|
+
self.options[:method].to_s
|
82
|
+
rescue
|
83
|
+
""
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -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
@@ -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.1
|
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-06-08 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
|
@@ -316,6 +318,7 @@ files:
|
|
316
318
|
- lib/scout_apm/instruments/resque.rb
|
317
319
|
- lib/scout_apm/instruments/samplers.rb
|
318
320
|
- lib/scout_apm/instruments/sinatra.rb
|
321
|
+
- lib/scout_apm/instruments/typhoeus.rb
|
319
322
|
- lib/scout_apm/job_record.rb
|
320
323
|
- lib/scout_apm/layaway.rb
|
321
324
|
- lib/scout_apm/layaway_file.rb
|
@@ -396,8 +399,6 @@ files:
|
|
396
399
|
- lib/scout_apm/utils/numbers.rb
|
397
400
|
- lib/scout_apm/utils/scm.rb
|
398
401
|
- lib/scout_apm/utils/sql_sanitizer.rb
|
399
|
-
- lib/scout_apm/utils/sql_sanitizer_regex.rb
|
400
|
-
- lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb
|
401
402
|
- lib/scout_apm/utils/time.rb
|
402
403
|
- lib/scout_apm/utils/unique_id.rb
|
403
404
|
- lib/scout_apm/version.rb
|
@@ -408,11 +409,13 @@ files:
|
|
408
409
|
- test/tmp/README.md
|
409
410
|
- test/unit/agent_context_test.rb
|
410
411
|
- test/unit/agent_test.rb
|
412
|
+
- test/unit/auto_instrument/anonymous_block_value.rb
|
411
413
|
- test/unit/auto_instrument/assignments-instrumented.rb
|
412
414
|
- test/unit/auto_instrument/assignments.rb
|
413
415
|
- test/unit/auto_instrument/controller-ast.txt
|
414
416
|
- test/unit/auto_instrument/controller-instrumented.rb
|
415
417
|
- test/unit/auto_instrument/controller.rb
|
418
|
+
- test/unit/auto_instrument/hanging_method.rb
|
416
419
|
- test/unit/auto_instrument/rescue_from-instrumented.rb
|
417
420
|
- test/unit/auto_instrument/rescue_from.rb
|
418
421
|
- test/unit/auto_instrument_test.rb
|
@@ -433,6 +436,7 @@ files:
|
|
433
436
|
- test/unit/instruments/active_record_test.rb
|
434
437
|
- test/unit/instruments/net_http_test.rb
|
435
438
|
- test/unit/instruments/percentile_sampler_test.rb
|
439
|
+
- test/unit/instruments/typhoeus_test.rb
|
436
440
|
- test/unit/layaway_test.rb
|
437
441
|
- test/unit/layer_children_set_test.rb
|
438
442
|
- test/unit/layer_converters/depth_first_walker_test.rb
|
@@ -441,9 +445,9 @@ files:
|
|
441
445
|
- test/unit/limited_layer_test.rb
|
442
446
|
- test/unit/logger_test.rb
|
443
447
|
- test/unit/metric_set_test.rb
|
444
|
-
- test/unit/remote/
|
445
|
-
- test/unit/remote/
|
446
|
-
- test/unit/remote/
|
448
|
+
- test/unit/remote/message_test.rb
|
449
|
+
- test/unit/remote/route_test.rb
|
450
|
+
- test/unit/remote/server_test.rb
|
447
451
|
- test/unit/request_histograms_test.rb
|
448
452
|
- test/unit/scored_item_set_test.rb
|
449
453
|
- test/unit/serializers/payload_serializer_test.rb
|
@@ -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
|