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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +49 -0
  3. data/.rubocop.yml +2 -1
  4. data/.travis.yml +3 -1
  5. data/CHANGELOG.markdown +37 -0
  6. data/gems/rails6.gemfile +1 -1
  7. data/lib/scout_apm.rb +3 -0
  8. data/lib/scout_apm/agent/preconditions.rb +3 -3
  9. data/lib/scout_apm/auto_instrument/rails.rb +0 -1
  10. data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -1
  11. data/lib/scout_apm/background_job_integrations/faktory.rb +103 -0
  12. data/lib/scout_apm/background_job_integrations/shoryuken.rb +2 -0
  13. data/lib/scout_apm/background_job_integrations/sidekiq.rb +13 -2
  14. data/lib/scout_apm/environment.rb +17 -1
  15. data/lib/scout_apm/error_service.rb +3 -1
  16. data/lib/scout_apm/error_service/middleware.rb +2 -2
  17. data/lib/scout_apm/error_service/payload.rb +1 -1
  18. data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +5 -1
  19. data/lib/scout_apm/ignored_uris.rb +3 -1
  20. data/lib/scout_apm/instrument_manager.rb +1 -0
  21. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +5 -2
  22. data/lib/scout_apm/instruments/action_view.rb +23 -7
  23. data/lib/scout_apm/instruments/active_record.rb +7 -1
  24. data/lib/scout_apm/instruments/typhoeus.rb +90 -0
  25. data/lib/scout_apm/layer.rb +1 -1
  26. data/lib/scout_apm/layer_converters/find_layer_by_type.rb +4 -0
  27. data/lib/scout_apm/logger.rb +1 -1
  28. data/lib/scout_apm/remote/server.rb +13 -1
  29. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +11 -25
  30. data/lib/scout_apm/tracer.rb +2 -2
  31. data/lib/scout_apm/utils/sql_sanitizer.rb +30 -6
  32. data/lib/scout_apm/version.rb +1 -1
  33. data/scout_apm.gemspec +1 -1
  34. data/test/unit/auto_instrument/anonymous_block_value.rb +7 -0
  35. data/test/unit/auto_instrument/hanging_method.rb +6 -0
  36. data/test/unit/auto_instrument_test.rb +8 -0
  37. data/test/unit/environment_test.rb +2 -2
  38. data/test/unit/ignored_uris_test.rb +6 -0
  39. data/test/unit/remote/{test_message.rb → message_test.rb} +0 -0
  40. data/test/unit/remote/{test_router.rb → route_test.rb} +0 -0
  41. data/test/unit/remote/{test_server.rb → server_test.rb} +4 -1
  42. data/test/unit/sql_sanitizer_test.rb +48 -2
  43. data/test/unit/tracer_test.rb +25 -0
  44. metadata +12 -9
  45. data/lib/scout_apm/utils/sql_sanitizer_regex.rb +0 -32
  46. 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: ffcf571075c7f443ebe029e8f07c726cc5cb6c12e156d47e2565c576ac671316
4
- data.tar.gz: 684f8ed4ba52d2e5819ea319288333a83f8b5481491ae879be2e8031cdd5d6ee
3
+ metadata.gz: 5af3fd0ff7d9f43aa8ec1b205104fb90784ba83b98250ef871d5bd1282734432
4
+ data.tar.gz: aae89c06e87f165d0ee87491fbb58253b7b38cdad29375e555e9e0c1d5607129
5
5
  SHA512:
6
- metadata.gz: a4d90917dc02469213092848211fe779f44ec09e7db4829aaaa737ca15cf1811137dc2a4ae00adaa76cc2be92916b0467f164f79a76fac501c7a4827ba5d6f7d
7
- data.tar.gz: 776d064902d998cc69330e470fbc9bfb368ae401d8e8a341310e4dc2a9070a256a2e57370505ee3475761d26145e4efced633963243527e39b95ab584bf48b2a
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
- Metrics/LineLength:
11
+ Layout/LineLength:
11
12
  Enabled: false
12
13
  Max: 100
data/.travis.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  language: ruby
2
- dist: trusty
2
+ dist: xenial
3
3
  cache: bundler
4
4
 
5
5
  matrix:
@@ -11,6 +11,8 @@ matrix:
11
11
  - rvm: 2.4
12
12
  - rvm: 2.5
13
13
  - rvm: 2.6
14
+ - rvm: 2.7
15
+ - rvm: 3.0
14
16
  - rvm: 2.6
15
17
  gemfile: gems/octoshark.gemfile
16
18
  - rvm: 2.6
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
@@ -1,4 +1,4 @@
1
1
  eval_gemfile("../Gemfile")
2
2
 
3
- gem "rails", "~> 6.0.0rc1"
3
+ gem "rails", "~> 6.0"
4
4
  gem "sqlite3", "~> 1.4"
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
- app_server_missing = !context.environment.app_server_integration(true).found?
31
- background_job_missing = context.environment.background_job_integrations.length == 0
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
- !app_server_missing && !background_job_missing
33
+ app_server_found || background_job_integration_found
34
34
  },
35
35
  :severity => :info,
36
36
  },
@@ -69,7 +69,6 @@ module ScoutApm
69
69
  end
70
70
 
71
71
  method_name = @method.last.children[0]
72
- class_name = @scope.last.children[1]
73
72
  bt = ["#{file_name}:#{line}:in `#{method_name}'"]
74
73
 
75
74
  return [
@@ -11,7 +11,7 @@ module ScoutApm
11
11
  end
12
12
 
13
13
  def present?
14
- defined?(::Delayed::Job)
14
+ defined?(::Delayed::Worker)
15
15
  end
16
16
 
17
17
  def forking?
@@ -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
- return if ScoutApm::Agent.instance.context.ignored_exceptions.ignore?(exception)
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
@@ -12,7 +12,7 @@ module ScoutApm
12
12
  # TODO: Don't use to_json since it isn't supported in Ruby 1.8.7
13
13
  def serialize
14
14
  payload = as_json.to_json
15
- context.logger.info(payload)
15
+ context.logger.debug(payload)
16
16
  payload
17
17
  end
18
18
 
@@ -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.connection_config[:adapter].to_s rescue nil
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).map {|prefix| %r{\A#{prefix}} }
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
- if current_layer && current_layer.type == "Controller"
99
- # Don't start a new layer if ActionController::API or ActionController::Base handled it already.
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
- super(*args)
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
- template_name = @template.virtual_path rescue "Unknown Collection"
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
- super(*args)
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
- template_name = args[0].virtual_path rescue "Unknown"
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
- find_by_sql_without_scout_instruments(*args, **kwargs, &block)
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
@@ -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
@@ -76,7 +76,7 @@ module ScoutApm
76
76
  klass = @opts.fetch(:logger_class, ::Logger)
77
77
  case klass
78
78
  when String
79
- result = KlassHelper.lookup(klass)
79
+ result = Utils::KlassHelper.lookup(klass)
80
80
  if result == :missing_class
81
81
  ::Logger
82
82
  else
@@ -16,8 +16,20 @@ module ScoutApm
16
16
  @server = nil
17
17
  end
18
18
 
19
- def start
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
- # Ruby 1.8.7 seems to be fundamentally different in how gsub or regexes
49
- # work. This is a hack and will be removed as soon as we can drop
50
- # support
51
- if RUBY_VERSION == "1.8.7"
52
- ESCAPE_MAPPINGS = {
53
- "\b" => '\\b',
54
- "\t" => '\\t',
55
- "\n" => '\\n',
56
- "\f" => '\\f',
57
- "\r" => '\\r',
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)|
@@ -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
- if ScoutApm::Environment.instance.ruby_187?
9
- require 'scout_apm/utils/sql_sanitizer_regex_1_8_7'
10
- else
11
- require 'scout_apm/utils/sql_sanitizer_regex'
12
- end
13
- include ScoutApm::Utils::SqlRegex
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 (?)')
@@ -1,3 +1,3 @@
1
1
  module ScoutApm
2
- VERSION = "4.0.0"
2
+ VERSION = "4.1.0"
3
3
  end
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 = '~> 2.1'
24
+ s.required_ruby_version = '>= 2.1'
25
25
 
26
26
  s.add_development_dependency "minitest"
27
27
  s.add_development_dependency "mocha"
@@ -0,0 +1,7 @@
1
+ class TestController < ApplicationController
2
+ def index
3
+ quests = policy_scope(Quest.open)
4
+ quests.each { _1.current_user = current_user }
5
+ respond_with_proto quests
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ class TestController < ApplicationController
2
+ end
3
+
4
+ def hanging_method
5
+ Test.first
6
+ end
@@ -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.const_unset("Rails") if defined?(Kernel::Rails)
48
- Kernel.const_unset("ActionController") if defined?(Kernel::ActionController)
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
@@ -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.01 # Let the server finish starting. The assert should instead allow a time
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 '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')"|
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 '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 ?)"|
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
@@ -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.0.0
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: 2020-11-25 00:00:00.000000000 Z
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/test_message.rb
444
- - test/unit/remote/test_router.rb
445
- - test/unit/remote/test_server.rb
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.0.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