scout_apm 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
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