scout_apm 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +49 -0
- data/.rubocop.yml +2 -1
- data/.travis.yml +3 -1
- data/CHANGELOG.markdown +37 -0
- data/gems/rails6.gemfile +1 -1
- 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 +17 -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 +23 -7
- data/lib/scout_apm/instruments/active_record.rb +7 -1
- data/lib/scout_apm/instruments/typhoeus.rb +90 -0
- data/lib/scout_apm/layer.rb +1 -1
- 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/remote/server.rb +13 -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/scout_apm.gemspec +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/environment_test.rb +2 -2
- data/test/unit/ignored_uris_test.rb +6 -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 +48 -2
- data/test/unit/tracer_test.rb +25 -0
- metadata +12 -9
- 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: 5af3fd0ff7d9f43aa8ec1b205104fb90784ba83b98250ef871d5bd1282734432
|
4
|
+
data.tar.gz: aae89c06e87f165d0ee87491fbb58253b7b38cdad29375e555e9e0c1d5607129
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 304283c6c853ce0b2421eccaf3e331c784bd97bda96014777a3a52b64212250595c8d6f7ff390ee154a090d096d1b37f93f221a5e22b2133e47b4a4e36dc4d8e
|
7
|
+
data.tar.gz: 453dd2e29eb7d8bc44ef3527a7ab8ba486d5c9c709b9be275a0223df96deb1d7628c97418a220ca0425e5c6706e1e60c1a8f750f544b223a3b3fcd6c4e924d4f
|
@@ -0,0 +1,49 @@
|
|
1
|
+
name: Tests
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
lint:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
|
9
|
+
steps:
|
10
|
+
- uses: actions/checkout@v2
|
11
|
+
- uses: ruby/setup-ruby@v1
|
12
|
+
with:
|
13
|
+
bundler-cache: true
|
14
|
+
ruby-version: 2.6
|
15
|
+
- run: bundle exec rubocop
|
16
|
+
|
17
|
+
test:
|
18
|
+
strategy:
|
19
|
+
fail-fast: false
|
20
|
+
matrix:
|
21
|
+
include:
|
22
|
+
- ruby: 2.1
|
23
|
+
gemfile: gems/rails3.gemfile
|
24
|
+
- ruby: 2.2
|
25
|
+
- ruby: 2.3
|
26
|
+
- ruby: 2.4
|
27
|
+
- ruby: 2.5
|
28
|
+
- ruby: 2.6
|
29
|
+
- ruby: 2.6
|
30
|
+
gemfile: gems/octoshark.gemfile
|
31
|
+
- ruby: 2.6
|
32
|
+
gemfile: gems/rails3.gemfile
|
33
|
+
bundler: 1.17.3
|
34
|
+
- ruby: 2.7
|
35
|
+
- ruby: 3.0
|
36
|
+
|
37
|
+
env:
|
38
|
+
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
39
|
+
|
40
|
+
runs-on: ubuntu-latest
|
41
|
+
|
42
|
+
steps:
|
43
|
+
- uses: actions/checkout@v2
|
44
|
+
- uses: ruby/setup-ruby@v1
|
45
|
+
with:
|
46
|
+
bundler-cache: true
|
47
|
+
bundler: ${{matrix.bundler}}
|
48
|
+
ruby-version: ${{ matrix.ruby }}
|
49
|
+
- run: bundle exec rake
|
data/.rubocop.yml
CHANGED
@@ -4,9 +4,10 @@ AllCops:
|
|
4
4
|
Exclude:
|
5
5
|
- 'test/unit/auto_instrument/*'
|
6
6
|
- vendor/bundle/**/*
|
7
|
+
SuggestExtensions: false
|
7
8
|
|
8
9
|
# 80 is stifling, especially with a few levels of nesting before we even start.
|
9
10
|
# So bump it to 100 to keep really long lines from creeping in.
|
10
|
-
|
11
|
+
Layout/LineLength:
|
11
12
|
Enabled: false
|
12
13
|
Max: 100
|
data/.travis.yml
CHANGED
data/CHANGELOG.markdown
CHANGED
@@ -1,3 +1,40 @@
|
|
1
|
+
# 4.1.0
|
2
|
+
|
3
|
+
* Preload Celluloid in Shoryuken instrumentation (#331)
|
4
|
+
* Fix deprecation warning in Rails 6.1+ (#365)
|
5
|
+
* Set Typheous's desc more directly (#392)
|
6
|
+
* Delegate to ActiveRecord #log more intelligently (#394)
|
7
|
+
* Don't delay starting agent when possible (#397)
|
8
|
+
* Fix template naming issue in Rails 6+ (#399)
|
9
|
+
* Avoid double-counting issue with AutoInstruments (#405)
|
10
|
+
* Renaming test files for Remote::{Server|Route|Message} to be included in test run (#409)
|
11
|
+
* More robust naming of Sidekiq jobs (#412)
|
12
|
+
* Allow render_template instruments to work with older Rails (#413)
|
13
|
+
* Fix function to manually capture exceptions (#415)
|
14
|
+
* Enhance SQL Sanitization (#417)
|
15
|
+
|
16
|
+
# 4.0.4
|
17
|
+
|
18
|
+
* Add Faktory Support (#385)
|
19
|
+
* Remove Regexp hack for 1.8.7 (no longer supported) (#384)
|
20
|
+
* More robust DelayedJob detection (#382)
|
21
|
+
* Fix kwargs handling in Tracing module (#381)
|
22
|
+
|
23
|
+
# 4.0.3
|
24
|
+
|
25
|
+
* Handle edge case with nil Typhoeus current-layer (#380)
|
26
|
+
* Fix args passing to render_partial (#379)
|
27
|
+
|
28
|
+
# 4.0.2
|
29
|
+
|
30
|
+
* Add Typhoeus instrumentation (#376)
|
31
|
+
|
32
|
+
# 4.0.1
|
33
|
+
|
34
|
+
* Add support for Ruby 3.0 (#374)
|
35
|
+
* Use Github Actions for CI (#370)
|
36
|
+
* Fix edge case in sanitization of Postgres SQL (#368)
|
37
|
+
|
1
38
|
# 4.0.0
|
2
39
|
|
3
40
|
* Require Ruby >= 2.1 (#270)
|
data/gems/rails6.gemfile
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'
|
@@ -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 = [
|
@@ -182,9 +183,24 @@ module ScoutApm
|
|
182
183
|
@ruby_2 = defined?(RUBY_VERSION) && RUBY_VERSION.match(/^2/)
|
183
184
|
end
|
184
185
|
|
186
|
+
def ruby_3?
|
187
|
+
return @ruby_3 if defined?(@ruby_3)
|
188
|
+
@ruby_3 = defined?(RUBY_VERSION) && RUBY_VERSION.match(/^3/)
|
189
|
+
end
|
190
|
+
|
191
|
+
def ruby_minor
|
192
|
+
return @ruby_minor if defined?(@ruby_minor)
|
193
|
+
@ruby_minor = defined?(RUBY_VERSION) && RUBY_VERSION.split(".")[1].to_i
|
194
|
+
end
|
195
|
+
|
185
196
|
# Returns true if this Ruby version supports Module#prepend.
|
186
197
|
def supports_module_prepend?
|
187
|
-
ruby_2?
|
198
|
+
ruby_2? || ruby_3?
|
199
|
+
end
|
200
|
+
|
201
|
+
# Returns true if this Ruby version makes positional and keyword arguments incompatible
|
202
|
+
def supports_kwarg_delegation?
|
203
|
+
ruby_3? || (ruby_2? && ruby_minor >= 7)
|
188
204
|
end
|
189
205
|
|
190
206
|
# Returns a string representation of the OS (ex: darwin, linux)
|
@@ -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
|
@@ -77,13 +77,13 @@ module ScoutApm
|
|
77
77
|
module ActionViewPartialRendererInstruments
|
78
78
|
# In Rails 6, the signature changed to pass the view & template args directly, as opposed to through the instance var
|
79
79
|
# New signature is: def render_partial(view, template)
|
80
|
-
def render_partial(*args)
|
80
|
+
def render_partial(*args, **kwargs)
|
81
81
|
req = ScoutApm::RequestManager.lookup
|
82
82
|
|
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"
|
@@ -92,16 +92,23 @@ module ScoutApm
|
|
92
92
|
|
93
93
|
begin
|
94
94
|
req.start_layer(layer)
|
95
|
-
|
95
|
+
if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?
|
96
|
+
super(*args, **kwargs)
|
97
|
+
else
|
98
|
+
super(*args)
|
99
|
+
end
|
96
100
|
ensure
|
97
101
|
req.stop_layer
|
98
102
|
end
|
99
103
|
end
|
100
104
|
|
101
|
-
def collection_with_template(*args)
|
105
|
+
def collection_with_template(*args, **kwargs)
|
102
106
|
req = ScoutApm::RequestManager.lookup
|
103
107
|
|
104
|
-
|
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
|
105
112
|
template_name ||= "Unknown Collection"
|
106
113
|
layer_name = template_name + "/Rendering"
|
107
114
|
|
@@ -110,7 +117,11 @@ module ScoutApm
|
|
110
117
|
|
111
118
|
begin
|
112
119
|
req.start_layer(layer)
|
113
|
-
|
120
|
+
if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?
|
121
|
+
super(*args, **kwargs)
|
122
|
+
else
|
123
|
+
super(*args)
|
124
|
+
end
|
114
125
|
ensure
|
115
126
|
req.stop_layer
|
116
127
|
end
|
@@ -118,10 +129,15 @@ module ScoutApm
|
|
118
129
|
end
|
119
130
|
|
120
131
|
module ActionViewTemplateRendererInstruments
|
132
|
+
# Don't forward kwargs here, since Rails 3, 4, 5, 6 don't use them, and
|
133
|
+
# it causes annoyances in the instrumentation
|
121
134
|
def render_template(*args)
|
122
135
|
req = ScoutApm::RequestManager.lookup
|
123
136
|
|
124
|
-
|
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
|
125
141
|
template_name ||= "Unknown"
|
126
142
|
layer_name = template_name + "/Rendering"
|
127
143
|
|
@@ -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
|
################################################################################
|
@@ -315,7 +317,11 @@ module ScoutApm
|
|
315
317
|
req.start_layer(layer)
|
316
318
|
req.ignore_children!
|
317
319
|
begin
|
318
|
-
|
320
|
+
if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?
|
321
|
+
find_by_sql_without_scout_instruments(*args, **kwargs, &block)
|
322
|
+
else
|
323
|
+
find_by_sql_without_scout_instruments(*args, &block)
|
324
|
+
end
|
319
325
|
ensure
|
320
326
|
req.acknowledge_children!
|
321
327
|
req.stop_layer
|
@@ -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 if current_layer
|
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
|
data/lib/scout_apm/layer.rb
CHANGED
@@ -116,7 +116,7 @@ module ScoutApm
|
|
116
116
|
# In Ruby 2.0+, we can pass the range directly to the caller to reduce the memory footprint.
|
117
117
|
def caller_array
|
118
118
|
# omits the first several callers which are in the ScoutAPM stack.
|
119
|
-
if ScoutApm::Agent.instance.context.environment.ruby_2?
|
119
|
+
if ScoutApm::Agent.instance.context.environment.ruby_2? || ScoutApm::Agent.instance.context.environment.ruby_3?
|
120
120
|
caller(3...BACKTRACE_CALLER_LIMIT)
|
121
121
|
else
|
122
122
|
caller[3...BACKTRACE_CALLER_LIMIT]
|
@@ -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
@@ -16,8 +16,20 @@ module ScoutApm
|
|
16
16
|
@server = nil
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
19
|
+
def require_webrick
|
20
20
|
require 'webrick'
|
21
|
+
true
|
22
|
+
rescue LoadError
|
23
|
+
@logger.warn(
|
24
|
+
%q|Could not require Webrick. Ruby 3.0 stopped bundling it
|
25
|
+
automatically, but it is required to instrument Resque. Please add
|
26
|
+
Webrick to your Gemfile.|
|
27
|
+
)
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def start
|
32
|
+
return false unless require_webrick
|
21
33
|
|
22
34
|
@server = WEBrick::HTTPServer.new(
|
23
35
|
:BindAddress => bind,
|
@@ -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/scout_apm.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.extensions << 'ext/allocations/extconf.rb'
|
22
22
|
s.extensions << 'ext/rusage/extconf.rb'
|
23
23
|
|
24
|
-
s.required_ruby_version = '
|
24
|
+
s.required_ruby_version = '>= 2.1'
|
25
25
|
|
26
26
|
s.add_development_dependency "minitest"
|
27
27
|
s.add_development_dependency "mocha"
|
@@ -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
|
@@ -44,8 +44,8 @@ class EnvironmentTest < Minitest::Test
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def clean_fake_rails
|
47
|
-
Kernel.
|
48
|
-
Kernel.
|
47
|
+
Kernel.send(:remove_const, "Rails") if defined?(Kernel::Rails)
|
48
|
+
Kernel.send(:remove_const, "ActionController") if defined?(Kernel::ActionController)
|
49
49
|
end
|
50
50
|
|
51
51
|
def fake_sinatra
|
@@ -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
|
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 }
|
@@ -146,6 +161,37 @@ module ScoutApm
|
|
146
161
|
assert_equal %q|UPDATE "mytable" SET "myfield" = ?, "countofthings" = ? WHERE "user_id" = ?|, ss.to_s
|
147
162
|
end
|
148
163
|
|
164
|
+
def test_postgres_multiline_sql
|
165
|
+
sql = %q|
|
166
|
+
SELECT "html_form_payloads".*
|
167
|
+
FROM "html_form_payloads"
|
168
|
+
INNER JOIN "leads" ON "leads"."payload_id" = "html_form_payloads"."id"
|
169
|
+
AND "leads"."payload_type" = ?
|
170
|
+
WHERE html_form_payloads.id < 10
|
171
|
+
AND "form_type" = 'xyz'
|
172
|
+
AND (params::varchar = '{"name":"Chris","resident":"Yes","e-content":"Secret content"}')
|
173
|
+
AND (leads.url = 'http://example.com')
|
174
|
+
ORDER BY "html_form_payloads"."id" ASC
|
175
|
+
LIMIT ?
|
176
|
+
|
|
177
|
+
|
178
|
+
ss = SqlSanitizer.new(sql).tap{ |it| it.database_engine = :postgres }
|
179
|
+
assert_equal %q|SELECT "html_form_payloads".* FROM "html_form_payloads" INNER JOIN "leads" ON "leads"."payload_id" = "html_form_payloads"."id" AND "leads"."payload_type" = ? WHERE html_form_payloads.id < ? AND "form_type" = ? AND (params::varchar = ?) AND (leads.url = ?) ORDER BY "html_form_payloads"."id" ASC LIMIT ?|, ss.to_s
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_mysql_multiline
|
183
|
+
sql = %q|
|
184
|
+
SELECT `blogs`.*
|
185
|
+
FROM `blogs`
|
186
|
+
WHERE (title = 'abc')
|
187
|
+
|
|
188
|
+
|
189
|
+
ss = SqlSanitizer.new(sql).tap{ |it| it.database_engine = :mysql }
|
190
|
+
assert_equal %q|SELECT `blogs`.*
|
191
|
+
FROM `blogs`
|
192
|
+
WHERE (title = ?)|, ss.to_s
|
193
|
+
end
|
194
|
+
|
149
195
|
def assert_faster_than(target_seconds)
|
150
196
|
t1 = ::Time.now
|
151
197
|
yield
|
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.0
|
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:
|
12
|
+
date: 2021-06-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -216,6 +216,7 @@ extensions:
|
|
216
216
|
- ext/rusage/extconf.rb
|
217
217
|
extra_rdoc_files: []
|
218
218
|
files:
|
219
|
+
- ".github/workflows/test.yml"
|
219
220
|
- ".gitignore"
|
220
221
|
- ".rubocop.yml"
|
221
222
|
- ".travis.yml"
|
@@ -250,6 +251,7 @@ files:
|
|
250
251
|
- lib/scout_apm/auto_instrument/parser.rb
|
251
252
|
- lib/scout_apm/auto_instrument/rails.rb
|
252
253
|
- lib/scout_apm/background_job_integrations/delayed_job.rb
|
254
|
+
- lib/scout_apm/background_job_integrations/faktory.rb
|
253
255
|
- lib/scout_apm/background_job_integrations/legacy_sneakers.rb
|
254
256
|
- lib/scout_apm/background_job_integrations/que.rb
|
255
257
|
- lib/scout_apm/background_job_integrations/resque.rb
|
@@ -315,6 +317,7 @@ files:
|
|
315
317
|
- lib/scout_apm/instruments/resque.rb
|
316
318
|
- lib/scout_apm/instruments/samplers.rb
|
317
319
|
- lib/scout_apm/instruments/sinatra.rb
|
320
|
+
- lib/scout_apm/instruments/typhoeus.rb
|
318
321
|
- lib/scout_apm/job_record.rb
|
319
322
|
- lib/scout_apm/layaway.rb
|
320
323
|
- lib/scout_apm/layaway_file.rb
|
@@ -395,8 +398,6 @@ files:
|
|
395
398
|
- lib/scout_apm/utils/numbers.rb
|
396
399
|
- lib/scout_apm/utils/scm.rb
|
397
400
|
- lib/scout_apm/utils/sql_sanitizer.rb
|
398
|
-
- lib/scout_apm/utils/sql_sanitizer_regex.rb
|
399
|
-
- lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb
|
400
401
|
- lib/scout_apm/utils/time.rb
|
401
402
|
- lib/scout_apm/utils/unique_id.rb
|
402
403
|
- lib/scout_apm/version.rb
|
@@ -407,11 +408,13 @@ files:
|
|
407
408
|
- test/tmp/README.md
|
408
409
|
- test/unit/agent_context_test.rb
|
409
410
|
- test/unit/agent_test.rb
|
411
|
+
- test/unit/auto_instrument/anonymous_block_value.rb
|
410
412
|
- test/unit/auto_instrument/assignments-instrumented.rb
|
411
413
|
- test/unit/auto_instrument/assignments.rb
|
412
414
|
- test/unit/auto_instrument/controller-ast.txt
|
413
415
|
- test/unit/auto_instrument/controller-instrumented.rb
|
414
416
|
- test/unit/auto_instrument/controller.rb
|
417
|
+
- test/unit/auto_instrument/hanging_method.rb
|
415
418
|
- test/unit/auto_instrument/rescue_from-instrumented.rb
|
416
419
|
- test/unit/auto_instrument/rescue_from.rb
|
417
420
|
- test/unit/auto_instrument_test.rb
|
@@ -440,9 +443,9 @@ files:
|
|
440
443
|
- test/unit/limited_layer_test.rb
|
441
444
|
- test/unit/logger_test.rb
|
442
445
|
- test/unit/metric_set_test.rb
|
443
|
-
- test/unit/remote/
|
444
|
-
- test/unit/remote/
|
445
|
-
- test/unit/remote/
|
446
|
+
- test/unit/remote/message_test.rb
|
447
|
+
- test/unit/remote/route_test.rb
|
448
|
+
- test/unit/remote/server_test.rb
|
446
449
|
- test/unit/request_histograms_test.rb
|
447
450
|
- test/unit/scored_item_set_test.rb
|
448
451
|
- test/unit/serializers/payload_serializer_test.rb
|
@@ -468,7 +471,7 @@ require_paths:
|
|
468
471
|
- data
|
469
472
|
required_ruby_version: !ruby/object:Gem::Requirement
|
470
473
|
requirements:
|
471
|
-
- - "
|
474
|
+
- - ">="
|
472
475
|
- !ruby/object:Gem::Version
|
473
476
|
version: '2.1'
|
474
477
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
@@ -477,7 +480,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
477
480
|
- !ruby/object:Gem::Version
|
478
481
|
version: '0'
|
479
482
|
requirements: []
|
480
|
-
rubygems_version: 3.
|
483
|
+
rubygems_version: 3.1.2
|
481
484
|
signing_key:
|
482
485
|
specification_version: 4
|
483
486
|
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*$|.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|$)/i.freeze
|
14
|
-
PSQL_AFTER_SET = /(?:SET\s+).*?(?:WHERE|$)/i.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
|