scout_apm 2.6.6 → 4.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +49 -0
  3. data/.rubocop.yml +2 -5
  4. data/.travis.yml +3 -7
  5. data/CHANGELOG.markdown +45 -0
  6. data/Gemfile +1 -8
  7. data/gems/rails6.gemfile +1 -1
  8. data/lib/scout_apm.rb +22 -1
  9. data/lib/scout_apm/agent.rb +22 -0
  10. data/lib/scout_apm/agent_context.rb +14 -2
  11. data/lib/scout_apm/background_job_integrations/sidekiq.rb +2 -2
  12. data/lib/scout_apm/config.rb +17 -2
  13. data/lib/scout_apm/detailed_trace.rb +2 -1
  14. data/lib/scout_apm/environment.rb +16 -1
  15. data/lib/scout_apm/error.rb +27 -0
  16. data/lib/scout_apm/error_service.rb +32 -0
  17. data/lib/scout_apm/error_service/error_buffer.rb +39 -0
  18. data/lib/scout_apm/error_service/error_record.rb +211 -0
  19. data/lib/scout_apm/error_service/ignored_exceptions.rb +66 -0
  20. data/lib/scout_apm/error_service/middleware.rb +32 -0
  21. data/lib/scout_apm/error_service/notifier.rb +33 -0
  22. data/lib/scout_apm/error_service/payload.rb +47 -0
  23. data/lib/scout_apm/error_service/periodic_work.rb +17 -0
  24. data/lib/scout_apm/error_service/railtie.rb +11 -0
  25. data/lib/scout_apm/error_service/sidekiq.rb +80 -0
  26. data/lib/scout_apm/extensions/transaction_callback_payload.rb +1 -1
  27. data/lib/scout_apm/instrument_manager.rb +1 -0
  28. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +47 -26
  29. data/lib/scout_apm/instruments/action_view.rb +21 -8
  30. data/lib/scout_apm/instruments/active_record.rb +17 -28
  31. data/lib/scout_apm/instruments/typhoeus.rb +88 -0
  32. data/lib/scout_apm/layer.rb +1 -1
  33. data/lib/scout_apm/middleware.rb +1 -1
  34. data/lib/scout_apm/remote/server.rb +13 -1
  35. data/lib/scout_apm/reporter.rb +8 -3
  36. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +28 -10
  37. data/lib/scout_apm/slow_policy/age_policy.rb +33 -0
  38. data/lib/scout_apm/slow_policy/percent_policy.rb +22 -0
  39. data/lib/scout_apm/slow_policy/percentile_policy.rb +24 -0
  40. data/lib/scout_apm/slow_policy/policy.rb +21 -0
  41. data/lib/scout_apm/slow_policy/speed_policy.rb +16 -0
  42. data/lib/scout_apm/slow_request_policy.rb +18 -77
  43. data/lib/scout_apm/utils/sql_sanitizer.rb +1 -0
  44. data/lib/scout_apm/utils/sql_sanitizer_regex.rb +3 -3
  45. data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +1 -0
  46. data/lib/scout_apm/version.rb +1 -1
  47. data/scout_apm.gemspec +6 -6
  48. data/test/unit/agent_context_test.rb +29 -0
  49. data/test/unit/environment_test.rb +2 -2
  50. data/test/unit/error_service/error_buffer_test.rb +25 -0
  51. data/test/unit/error_service/ignored_exceptions_test.rb +49 -0
  52. data/test/unit/serializers/payload_serializer_test.rb +36 -0
  53. data/test/unit/slow_request_policy_test.rb +41 -13
  54. data/test/unit/sql_sanitizer_test.rb +38 -0
  55. metadata +26 -61
  56. data/lib/scout_apm/slow_job_policy.rb +0 -111
  57. data/test/unit/slow_job_policy_test.rb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73af2b28e0b054233c1ccd48122741d7b8e27adf9c61ee93fea520f7e3a6b3c7
4
- data.tar.gz: 544c20bf5c56478269232f19a792dcbc00c125befb6e5e7b406be04dc954944e
3
+ metadata.gz: b992479d1f96a3acca3f9cdbbafad0eb87e6fc88ec53e5de8f6360d01fe21eae
4
+ data.tar.gz: 2bd72c79abe8628029ac8c541e0fccff18e54fe5d43558148daebe95e7418e0c
5
5
  SHA512:
6
- metadata.gz: c8558cd998a6c54ca7cd3c3174ba96c28230b0041a49e2d494460116a6f9065ba28f998ce8a74b546f69a8f1d5ec5dd81b9be29e27f4a874f51e1352f02798f0
7
- data.tar.gz: 18f7152a257b7ff61cb0c4953542527a2af25220d726ac26ade9242421f817afc83ce18fa28ea018188215717cedb25336ba79a61572cb706f99f16278be3545
6
+ metadata.gz: edf84c2a3b8a60961e9f3452b2ca0f10b963501becd9d30889a0acb671ac39267ee44e31279a78ae6a6375365e9c06c9f8d24b8c8f426fa2e512be6c5b141a1f
7
+ data.tar.gz: 713bd41d14c74e0655e38002195355263f9413fab013deddb67019437c193169968d26b6a33a058cf8480b8249bd7cf87a6ccf530f2a9b319c8fd1b5875af8e7
@@ -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,13 +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
13
-
14
- Style/HashSyntax:
15
- Enabled: true
16
- EnforcedStyle: hash_rockets
data/.travis.yml CHANGED
@@ -1,15 +1,9 @@
1
1
  language: ruby
2
- dist: trusty
2
+ dist: xenial
3
3
  cache: bundler
4
4
 
5
5
  matrix:
6
6
  include:
7
- - rvm: "1.8.7"
8
- gemfile: gems/rails3.gemfile
9
- - rvm: "1.9.3"
10
- gemfile: gems/rails3.gemfile
11
- - rvm: 2.0
12
- gemfile: gems/rails3.gemfile
13
7
  - rvm: 2.1
14
8
  gemfile: gems/rails3.gemfile
15
9
  - rvm: 2.2
@@ -17,6 +11,8 @@ matrix:
17
11
  - rvm: 2.4
18
12
  - rvm: 2.5
19
13
  - rvm: 2.6
14
+ - rvm: 2.7
15
+ - rvm: 3.0
20
16
  - rvm: 2.6
21
17
  gemfile: gems/octoshark.gemfile
22
18
  - rvm: 2.6
data/CHANGELOG.markdown CHANGED
@@ -1,3 +1,48 @@
1
+ # 4.0.3
2
+
3
+ * Handle edge case with nil Typhoeus current-layer (#380)
4
+ * Fix args passing to render_partial (#379)
5
+
6
+ # 4.0.2
7
+
8
+ * Add Typhoeus instrumentation (#376)
9
+
10
+ # 4.0.1
11
+
12
+ * Add support for Ruby 3.0 (#374)
13
+ * Use Github Actions for CI (#370)
14
+ * Fix edge case in sanitization of Postgres SQL (#368)
15
+
16
+ # 4.0.0
17
+
18
+ * Require Ruby >= 2.1 (#270)
19
+ * ErrorService reporting. Enable with `errors_enabled` config setting. (#347)
20
+ * Modular SlowRequestPolicy (#364)
21
+ * Fix deprecation warnings (#354)
22
+
23
+ # 2.6.10
24
+
25
+ * Fix an edge case in JSON serialization (#360)
26
+
27
+ # 2.6.9
28
+
29
+ * Add `ssl_cert_file` config option (#352)
30
+ * Improve sanitization of Postgres UPDATE SQL (#351)
31
+ * Allow custom URL sanitization (#341)
32
+
33
+ # 2.6.8
34
+
35
+ * Lock rake version for 1.8.7 to older version (#329)
36
+ * Delete unneeded .DS_Store file that snuck in (#334)
37
+ * Fix typo in "queue_time_ms"
38
+ * Fix Rails 6 deprecation warning at boot time (#337)
39
+ * Fix partial naming on Rails 6.0 (#339)
40
+ * Support Sidekiq 6.1 instrumentation (#340)
41
+
42
+ # 2.6.7
43
+
44
+ * Remove accidental call to `as_json`
45
+
1
46
  # 2.6.6
2
47
 
3
48
  * Add basic support for parsing Microsoft SQLServer queries (#317)
data/Gemfile CHANGED
@@ -3,11 +3,4 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in scout_apm.gemspec
4
4
  gemspec
5
5
 
6
- # Pin development dependencies more conservatively for Ruby 1.8.7
7
- if RUBY_VERSION <= "1.8.7"
8
- gem "activesupport", "~> 3.2"
9
- gem "i18n", "~> 0.6.11"
10
- gem "pry", "~> 0.9.12"
11
- gem "rake", "~> 10.5"
12
- gem "minitest", "~> 5.11.3"
13
- end
6
+ gem "rake", ">= 12.3.3"
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
@@ -78,6 +78,7 @@ require 'scout_apm/histogram'
78
78
 
79
79
  require 'scout_apm/instruments/net_http'
80
80
  require 'scout_apm/instruments/http_client'
81
+ require 'scout_apm/instruments/typhoeus'
81
82
  require 'scout_apm/instruments/moped'
82
83
  require 'scout_apm/instruments/mongoid'
83
84
  require 'scout_apm/instruments/memcached'
@@ -144,8 +145,13 @@ require 'scout_apm/slow_transaction'
144
145
  require 'scout_apm/slow_job_record'
145
146
  require 'scout_apm/detailed_trace'
146
147
  require 'scout_apm/scored_item_set'
148
+
147
149
  require 'scout_apm/slow_request_policy'
148
- require 'scout_apm/slow_job_policy'
150
+ require 'scout_apm/slow_policy/age_policy'
151
+ require 'scout_apm/slow_policy/speed_policy'
152
+ require 'scout_apm/slow_policy/percent_policy'
153
+ require 'scout_apm/slow_policy/percentile_policy'
154
+
149
155
  require 'scout_apm/job_record'
150
156
  require 'scout_apm/request_histograms'
151
157
  require 'scout_apm/transaction_time_consumed'
@@ -186,6 +192,16 @@ require 'scout_apm/tasks/support'
186
192
  require 'scout_apm/extensions/config'
187
193
  require 'scout_apm/extensions/transaction_callback_payload'
188
194
 
195
+ require 'scout_apm/error_service'
196
+ require 'scout_apm/error_service/middleware'
197
+ require 'scout_apm/error_service/notifier'
198
+ require 'scout_apm/error_service/sidekiq'
199
+ require 'scout_apm/error_service/ignored_exceptions'
200
+ require 'scout_apm/error_service/error_buffer'
201
+ require 'scout_apm/error_service/error_record'
202
+ require 'scout_apm/error_service/periodic_work'
203
+ require 'scout_apm/error_service/payload'
204
+
189
205
  if defined?(Rails) && defined?(Rails::VERSION) && defined?(Rails::VERSION::MAJOR) && Rails::VERSION::MAJOR >= 3 && defined?(Rails::Railtie)
190
206
  module ScoutApm
191
207
  class Railtie < Rails::Railtie
@@ -205,6 +221,11 @@ if defined?(Rails) && defined?(Rails::VERSION) && defined?(Rails::VERSION::MAJOR
205
221
  ScoutApm::Agent.instance.context.logger.debug("AutoInstruments is disabled.")
206
222
  end
207
223
 
224
+ if ScoutApm::Agent.instance.context.config.value("errors_enabled")
225
+ app.config.middleware.insert_after ActionDispatch::DebugExceptions, ScoutApm::ErrorService::Middleware
226
+ ScoutApm::ErrorService::Sidekiq.new.install
227
+ end
228
+
208
229
  # Install the middleware every time in development mode.
209
230
  # The middleware is a noop if dev_trace is not enabled in config
210
231
  if Rails.env.development?
@@ -66,6 +66,7 @@ module ScoutApm
66
66
 
67
67
  if context.started?
68
68
  start_background_worker unless background_worker_running?
69
+ start_error_service_background_worker unless error_service_background_worker_running?
69
70
  return
70
71
  end
71
72
 
@@ -81,6 +82,7 @@ module ScoutApm
81
82
  @app_server_load ||= AppServerLoad.new(context).run
82
83
 
83
84
  start_background_worker
85
+ start_error_service_background_worker
84
86
  end
85
87
 
86
88
  def instrument_manager
@@ -198,5 +200,25 @@ module ScoutApm
198
200
  @background_worker &&
199
201
  @background_worker.running?
200
202
  end
203
+
204
+ # seconds to batch error reports
205
+ ERROR_SEND_FREQUENCY = 5
206
+ def start_error_service_background_worker
207
+ periodic_work = ScoutApm::ErrorService::PeriodicWork.new(context)
208
+
209
+ @error_service_background_worker = ScoutApm::BackgroundWorker.new(context, ERROR_SEND_FREQUENCY)
210
+ @error_service_background_worker_thread = Thread.new do
211
+ @error_service_background_worker.start {
212
+ periodic_work.run
213
+ }
214
+ end
215
+ end
216
+
217
+ def error_service_background_worker_running?
218
+ @error_service_background_worker_thread &&
219
+ @error_service_background_worker_thread.alive? &&
220
+ @error_service_background_worker &&
221
+ @error_service_background_worker.running?
222
+ end
201
223
  end
202
224
  end
@@ -96,11 +96,11 @@ module ScoutApm
96
96
  end
97
97
 
98
98
  def slow_request_policy
99
- @slow_request_policy ||= ScoutApm::SlowRequestPolicy.new(self)
99
+ @slow_request_policy ||= ScoutApm::SlowRequestPolicy.new(self).tap{|p| p.add_default_policies }
100
100
  end
101
101
 
102
102
  def slow_job_policy
103
- @slow_job_policy ||= ScoutApm::SlowJobPolicy.new(self)
103
+ @slow_job_policy ||= ScoutApm::SlowRequestPolicy.new(self).tap{|p| p.add_default_policies }
104
104
  end
105
105
 
106
106
  # Maintains a Histogram of insignificant/significant autoinstrument layers.
@@ -142,6 +142,18 @@ module ScoutApm
142
142
  config.value('dev_trace') && environment.env == "development"
143
143
  end
144
144
 
145
+ ###################
146
+ # Error Service #
147
+ ###################
148
+
149
+ def error_buffer
150
+ @error_buffer ||= ScoutApm::ErrorService::ErrorBuffer.new(self)
151
+ end
152
+
153
+ def ignored_exceptions
154
+ @ignored_exceptions ||= ScoutApm::ErrorService::IgnoredExceptions.new(self, config.value('errors_ignored_exceptions'))
155
+ end
156
+
145
157
  #############
146
158
  # Setters #
147
159
  #############
@@ -40,10 +40,10 @@ module ScoutApm
40
40
  require 'sidekiq/processor' # sidekiq v4 has not loaded this file by this point
41
41
 
42
42
  ::Sidekiq::Processor.class_eval do
43
- def initialize_with_scout(boss)
43
+ def initialize_with_scout(*args)
44
44
  agent = ::ScoutApm::Agent.instance
45
45
  agent.start
46
- initialize_without_scout(boss)
46
+ initialize_without_scout(*args)
47
47
  end
48
48
 
49
49
  alias_method :initialize_without_scout, :initialize
@@ -75,11 +75,18 @@ module ScoutApm
75
75
  'revision_sha',
76
76
  'scm_subdirectory',
77
77
  'start_resque_server_instrument',
78
+ 'ssl_cert_file',
78
79
  'uri_reporting',
79
80
  'instrument_http_url_length',
80
81
  'timeline_traces',
81
82
  'auto_instruments',
82
- 'auto_instruments_ignore'
83
+ 'auto_instruments_ignore',
84
+
85
+ # Error Service Related Configuration
86
+ 'errors_enabled',
87
+ 'errors_ignored_exceptions',
88
+ 'errors_filtered_params',
89
+ 'errors_host',
83
90
  ]
84
91
 
85
92
  ################################################################################
@@ -175,6 +182,9 @@ module ScoutApm
175
182
  'timeline_traces' => BooleanCoercion.new,
176
183
  'auto_instruments' => BooleanCoercion.new,
177
184
  'auto_instruments_ignore' => JsonCoercion.new,
185
+ 'errors_enabled' => BooleanCoercion.new,
186
+ 'errors_ignored_exceptions' => JsonCoercion.new,
187
+ 'errors_filtered_params' => JsonCoercion.new,
178
188
  }
179
189
 
180
190
 
@@ -284,7 +294,12 @@ module ScoutApm
284
294
  'collect_remote_ip' => true,
285
295
  'timeline_traces' => true,
286
296
  'auto_instruments' => false,
287
- 'auto_instruments_ignore' => []
297
+ 'auto_instruments_ignore' => [],
298
+ 'ssl_cert_file' => File.join( File.dirname(__FILE__), *%w[.. .. data cacert.pem] ),
299
+ 'errors_enabled' => false,
300
+ 'errors_ignored_exceptions' => %w(ActiveRecord::RecordNotFound ActionController::RoutingError),
301
+ 'errors_filtered_params' => %w(password s3-key),
302
+ 'errors_host' => 'https://errors.scoutapm.com',
288
303
  }.freeze
289
304
 
290
305
  def value(key)
@@ -200,8 +200,9 @@ class DetailedTraceTags
200
200
  @tags = hash
201
201
  end
202
202
 
203
+ # @tags is already a hash, so no conversion needed
203
204
  def as_json(*)
204
- @tags.as_json
205
+ @tags
205
206
  end
206
207
  end
207
208
 
@@ -182,9 +182,24 @@ module ScoutApm
182
182
  @ruby_2 = defined?(RUBY_VERSION) && RUBY_VERSION.match(/^2/)
183
183
  end
184
184
 
185
+ def ruby_3?
186
+ return @ruby_3 if defined?(@ruby_3)
187
+ @ruby_3 = defined?(RUBY_VERSION) && RUBY_VERSION.match(/^3/)
188
+ end
189
+
190
+ def ruby_minor
191
+ return @ruby_minor if defined?(@ruby_minor)
192
+ @ruby_minor = defined?(RUBY_VERSION) && RUBY_VERSION.split(".")[1].to_i
193
+ end
194
+
185
195
  # Returns true if this Ruby version supports Module#prepend.
186
196
  def supports_module_prepend?
187
- ruby_2?
197
+ ruby_2? || ruby_3?
198
+ end
199
+
200
+ # Returns true if this Ruby version supports Module#prepend.
201
+ def supports_kwarg_delegation?
202
+ ruby_3? || (ruby_2? && ruby_minor >= 7)
188
203
  end
189
204
 
190
205
  # Returns a string representation of the OS (ex: darwin, linux)
@@ -0,0 +1,27 @@
1
+ # Public API for the Scout Error Monitoring service
2
+ #
3
+ # See-Also ScoutApm::Transaction and ScoutApm::Tracing for APM related APIs
4
+ module ScoutApm
5
+ module Error
6
+ # Capture an exception, optionally with an environment hash. This may be a
7
+ # Rack environment, but is not required.
8
+ def self.capture(exception, env={})
9
+ context = ScoutApm::Agent.instance.context
10
+
11
+ # Skip if error monitoring isn't enabled at all
12
+ if ! context.config.value("errors_enabled")
13
+ return false
14
+ end
15
+
16
+ # Skip if this one error is ignored
17
+ if context.ignored_exceptions.ignored?(exception)
18
+ return false
19
+ end
20
+
21
+ # Capture the error for further processing and shipping
22
+ context.error_buffer.capture(exception, env)
23
+
24
+ return true
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ require "net/http"
2
+ require "net/https"
3
+ require "uri"
4
+
5
+ module ScoutApm
6
+ module ErrorService
7
+ API_VERSION = "1"
8
+
9
+ HEADERS = {
10
+ "Content-type" => "application/json",
11
+ "Accept" => "application/json"
12
+ }
13
+
14
+ # Public API to force a given exception to be captured.
15
+ # Still obeys the ignore list
16
+ # Used internally by SidekiqException
17
+ def self.capture(exception, params = {})
18
+ return if disabled?
19
+ return if ScoutApm::Agent.instance.context.ignored_exceptions.ignore?(exception)
20
+
21
+ context.errors_buffer.capture(exception, env)
22
+ end
23
+
24
+ def self.enabled?
25
+ ScoutApm::Agent.instance.context.config.value("errors_enabled")
26
+ end
27
+
28
+ def self.disabled?
29
+ !enabled?
30
+ end
31
+ end
32
+ end