scout_apm 2.6.10 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 503d33cb7c689cb239c328bee4ba2a1df380df703f3e3a6f5f843676b9bff0e4
4
- data.tar.gz: 444e240dc8c4922ee932684c1c10b3fb5d658e96228c901b33a9a5ca3cb1e469
3
+ metadata.gz: ffcf571075c7f443ebe029e8f07c726cc5cb6c12e156d47e2565c576ac671316
4
+ data.tar.gz: 684f8ed4ba52d2e5819ea319288333a83f8b5481491ae879be2e8031cdd5d6ee
5
5
  SHA512:
6
- metadata.gz: 715c6a3f044d2091207667fc6a9857573da528cf03ee5d64ccb758c10ec387b49829a891ae43bb51b444077c898f57fc73257f2d963407820ed7114fdedb52c4
7
- data.tar.gz: 373b60dc42e0417e6003698c143138ff50024802285ff3754cc706efc1bc305a941fddbe27e6a471d8f09b3ffc84b5a3cfb43e57a1715968d7cac47ee069f5b3
6
+ metadata.gz: a4d90917dc02469213092848211fe779f44ec09e7db4829aaaa737ca15cf1811137dc2a4ae00adaa76cc2be92916b0467f164f79a76fac501c7a4827ba5d6f7d
7
+ data.tar.gz: 776d064902d998cc69330e470fbc9bfb368ae401d8e8a341310e4dc2a9070a256a2e57370505ee3475761d26145e4efced633963243527e39b95ab584bf48b2a
data/.rubocop.yml CHANGED
@@ -10,7 +10,3 @@ AllCops:
10
10
  Metrics/LineLength:
11
11
  Enabled: false
12
12
  Max: 100
13
-
14
- Style/HashSyntax:
15
- Enabled: true
16
- EnforcedStyle: hash_rockets
data/.travis.yml CHANGED
@@ -4,12 +4,6 @@ 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
data/CHANGELOG.markdown CHANGED
@@ -1,3 +1,10 @@
1
+ # 4.0.0
2
+
3
+ * Require Ruby >= 2.1 (#270)
4
+ * ErrorService reporting. Enable with `errors_enabled` config setting. (#347)
5
+ * Modular SlowRequestPolicy (#364)
6
+ * Fix deprecation warnings (#354)
7
+
1
8
  # 2.6.10
2
9
 
3
10
  * Fix an edge case in JSON serialization (#360)
data/Gemfile CHANGED
@@ -3,15 +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
- elsif RUBY_VERSION <= "1.9.3"
14
- gem "rake", "~> 10.5"
15
- else
16
- gem "rake", ">= 12.3.3"
17
- end
6
+ gem "rake", ">= 12.3.3"
data/lib/scout_apm.rb CHANGED
@@ -144,8 +144,13 @@ require 'scout_apm/slow_transaction'
144
144
  require 'scout_apm/slow_job_record'
145
145
  require 'scout_apm/detailed_trace'
146
146
  require 'scout_apm/scored_item_set'
147
+
147
148
  require 'scout_apm/slow_request_policy'
148
- require 'scout_apm/slow_job_policy'
149
+ require 'scout_apm/slow_policy/age_policy'
150
+ require 'scout_apm/slow_policy/speed_policy'
151
+ require 'scout_apm/slow_policy/percent_policy'
152
+ require 'scout_apm/slow_policy/percentile_policy'
153
+
149
154
  require 'scout_apm/job_record'
150
155
  require 'scout_apm/request_histograms'
151
156
  require 'scout_apm/transaction_time_consumed'
@@ -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.
@@ -82,15 +82,8 @@ module ScoutApm
82
82
 
83
83
  # Install #log tracing
84
84
  if Utils::KlassHelper.defined?("ActiveRecord::ConnectionAdapters::AbstractAdapter")
85
- if Module.respond_to?(:prepend)
86
- ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecordInstruments)
87
- ::ActiveRecord::ConnectionAdapters::AbstractAdapter.include(Tracer)
88
- else
89
- ::ActiveRecord::ConnectionAdapters::AbstractAdapter.module_eval do
90
- include ::ScoutApm::Instruments::ActiveRecordAliasMethodInstruments
91
- include ::ScoutApm::Tracer
92
- end
93
- end
85
+ ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecordInstruments)
86
+ ::ActiveRecord::ConnectionAdapters::AbstractAdapter.include(Tracer)
94
87
  end
95
88
 
96
89
  if Utils::KlassHelper.defined?("ActiveRecord::Base")
@@ -172,20 +165,12 @@ module ScoutApm
172
165
  # to the real SQL, and an AR generated "name" for the Query
173
166
  #
174
167
  ################################################################################
175
- #
176
- # Note, if you change this instrumentation, you also need to change ActiveRecordInstruments.
177
- module ActiveRecordAliasMethodInstruments
178
- def self.included(instrumented_class)
168
+ module ActiveRecordInstruments
169
+ def self.prepended(instrumented_class)
179
170
  ScoutApm::Agent.instance.context.logger.info "Instrumenting #{instrumented_class.inspect}"
180
- instrumented_class.class_eval do
181
- unless instrumented_class.method_defined?(:log_without_scout_instruments)
182
- alias_method :log_without_scout_instruments, :log
183
- alias_method :log, :log_with_scout_instruments
184
- end
185
- end
186
171
  end
187
172
 
188
- def log_with_scout_instruments(*args, &block)
173
+ def log(*args, &block)
189
174
  # Extract data from the arguments
190
175
  sql, name = args
191
176
  metric_name = Utils::ActiveRecordMetricName.new(sql, name)
@@ -216,7 +201,7 @@ module ScoutApm
216
201
  end
217
202
  current_layer.desc.merge(desc)
218
203
 
219
- log_without_scout_instruments(*args, &block)
204
+ super(*args, &block)
220
205
 
221
206
  # OR: Start a new layer, we didn't pick up instrumentation earlier in the stack.
222
207
  else
@@ -224,7 +209,7 @@ module ScoutApm
224
209
  layer.desc = desc
225
210
  req.start_layer(layer)
226
211
  begin
227
- log_without_scout_instruments(*args, &block)
212
+ super(*args, &block)
228
213
  ensure
229
214
  req.stop_layer
230
215
  end
@@ -323,14 +308,14 @@ module ScoutApm
323
308
  end
324
309
  end
325
310
 
326
- def find_by_sql_with_scout_instruments(*args, &block)
311
+ def find_by_sql_with_scout_instruments(*args, **kwargs, &block)
327
312
  req = ScoutApm::RequestManager.lookup
328
313
  layer = ScoutApm::Layer.new("ActiveRecord", Utils::ActiveRecordMetricName::DEFAULT_METRIC)
329
314
  layer.annotate_layer(:ignorable => true)
330
315
  req.start_layer(layer)
331
316
  req.ignore_children!
332
317
  begin
333
- find_by_sql_without_scout_instruments(*args, &block)
318
+ find_by_sql_without_scout_instruments(*args, **kwargs, &block)
334
319
  ensure
335
320
  req.acknowledge_children!
336
321
  req.stop_layer
@@ -408,7 +393,7 @@ module ScoutApm
408
393
  end
409
394
 
410
395
  module ActiveRecordUpdateInstruments
411
- def save(*args, &block)
396
+ def save(*args, **options, &block)
412
397
  model = self.class.name
413
398
  operation = self.persisted? ? "Update" : "Create"
414
399
 
@@ -418,14 +403,14 @@ module ScoutApm
418
403
  req.start_layer(layer)
419
404
  req.ignore_children!
420
405
  begin
421
- super(*args, &block)
406
+ super(*args, **options, &block)
422
407
  ensure
423
408
  req.acknowledge_children!
424
409
  req.stop_layer
425
410
  end
426
411
  end
427
412
 
428
- def save!(*args, &block)
413
+ def save!(*args, **options, &block)
429
414
  model = self.class.name
430
415
  operation = self.persisted? ? "Update" : "Create"
431
416
 
@@ -434,7 +419,7 @@ module ScoutApm
434
419
  req.start_layer(layer)
435
420
  req.ignore_children!
436
421
  begin
437
- super(*args, &block)
422
+ super(*args, **options, &block)
438
423
  ensure
439
424
  req.acknowledge_children!
440
425
  req.stop_layer
@@ -0,0 +1,33 @@
1
+ require 'scout_apm/slow_policy/policy'
2
+
3
+ module ScoutApm::SlowPolicy
4
+ class AgePolicy < Policy
5
+ # For each minute we haven't seen an endpoint
6
+ POINT_MULTIPLIER_AGE = 0.25
7
+
8
+ # A hash of Endpoint Name to the last time we stored a slow transaction for it.
9
+ #
10
+ # Defaults to a start time that is pretty close to application boot time.
11
+ # So the "age" of an endpoint we've never seen is the time the application
12
+ # has been running.
13
+ attr_reader :last_seen
14
+
15
+ def initialize(context)
16
+ super
17
+
18
+ zero_time = Time.now
19
+ @last_seen = Hash.new { |h, k| h[k] = zero_time }
20
+ end
21
+
22
+ def call(request)
23
+ # How long has it been since we've seen this?
24
+ age = Time.now - last_seen[request.unique_name]
25
+
26
+ age / 60.0 * POINT_MULTIPLIER_AGE
27
+ end
28
+
29
+ def stored!(request)
30
+ last_seen[request.unique_name] = Time.now
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ require 'scout_apm/slow_policy/policy'
2
+
3
+ module ScoutApm::SlowPolicy
4
+ class PercentPolicy < Policy
5
+ # Points for an endpoint's who's throughput * response time is a large % of
6
+ # overall time spent processing requests
7
+ POINT_MULTIPLIER_PERCENT_TIME = 2.5
8
+
9
+ # Of the total time spent handling endpoints in this app, if this endpoint
10
+ # is a higher percent, it should get more points.
11
+ #
12
+ # A: 20 calls @ 100ms each => 2 seconds of total time
13
+ # B: 10 calls @ 100ms each => 1 second of total time
14
+ #
15
+ # Then A is 66% of the total call time
16
+ def call(request) # Scale 0.0 - 1.0
17
+ percent = context.transaction_time_consumed.percent_of_total(request.unique_name)
18
+
19
+ percent * POINT_MULTIPLIER_PERCENT_TIME
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ require 'scout_apm/slow_policy/policy'
2
+
3
+ module ScoutApm::SlowPolicy
4
+ class PercentilePolicy < Policy
5
+ def call(request)
6
+ # What approximate percentile was this request?
7
+ total_time = request.root_layer.total_call_time
8
+ percentile = context.request_histograms.approximate_quantile_of_value(request.unique_name, total_time)
9
+
10
+ if percentile < 40
11
+ 0.4 # Don't put much emphasis on capturing low percentiles.
12
+ elsif percentile < 60
13
+ 1.4 # Highest here to get mean traces
14
+ elsif percentile < 90
15
+ 0.7 # Between 60 & 90% is fine.
16
+ elsif percentile >= 90
17
+ 1.4 # Highest here to get 90+%ile traces
18
+ else
19
+ # impossible.
20
+ percentile
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ # Note that this is semi-internal API. You should not need this, and if you do
2
+ # we're here to help at support@scoutapm.com. TrackedRequest doesn't change
3
+ # often, but we can't promise a perfectly stable API for it either.
4
+ module ScoutApm::SlowPolicy
5
+ class Policy
6
+ attr_reader :context
7
+
8
+ def initialize(context)
9
+ @context = context
10
+ end
11
+
12
+ def call(request)
13
+ raise NotImplementedError
14
+ end
15
+
16
+ # Override in subclasses to execute some behavior if the request gets a
17
+ # slot in the ScoredItemSet. Defaults to no-op
18
+ def stored!(request)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ require 'scout_apm/slow_policy/policy'
2
+
3
+ module ScoutApm::SlowPolicy
4
+ class SpeedPolicy < Policy
5
+ # Adjust speed points. See the function
6
+ POINT_MULTIPLIER_SPEED = 0.25
7
+
8
+ # Time in seconds
9
+ # Logarithm keeps huge times from swamping the other metrics.
10
+ # 1+ is necessary to keep the log function in positive territory.
11
+ def call(request)
12
+ total_time = request.root_layer.total_call_time
13
+ Math.log(1 + total_time) * POINT_MULTIPLIER_SPEED
14
+ end
15
+ end
16
+ end
@@ -3,43 +3,29 @@
3
3
 
4
4
  module ScoutApm
5
5
  class SlowRequestPolicy
6
- CAPTURE_TYPES = [
7
- CAPTURE_DETAIL = "capture_detail",
8
- CAPTURE_NONE = "capture_none",
9
- ]
10
-
11
- # Adjust speed points. See the function
12
- POINT_MULTIPLIER_SPEED = 0.25
13
-
14
- # For each minute we haven't seen an endpoint
15
- POINT_MULTIPLIER_AGE = 0.25
16
-
17
- # Outliers are worth up to "1000ms" of weight
18
- POINT_MULTIPLIER_PERCENTILE = 1.0
19
-
20
- # Points for an endpoint's who's throughput * response time is a large % of
21
- # overall time spent processing requests
22
- POINT_MULTIPLIER_PERCENT_TIME = 2.5
23
-
24
- # A hash of Endpoint Name to the last time we stored a slow transaction for it.
25
- #
26
- # Defaults to a start time that is pretty close to application boot time.
27
- # So the "age" of an endpoint we've never seen is the time the application
28
- # has been running.
29
- attr_reader :last_seen
30
-
31
6
  # The AgentContext we're running in
32
7
  attr_reader :context
8
+ attr_reader :policies
33
9
 
34
10
  def initialize(context)
35
11
  @context = context
12
+ @policies = []
13
+ end
36
14
 
37
- zero_time = Time.now
38
- @last_seen = Hash.new { |h, k| h[k] = zero_time }
15
+ def add_default_policies
16
+ add(SlowPolicy::SpeedPolicy.new(context))
17
+ add(SlowPolicy::PercentilePolicy.new(context))
18
+ add(SlowPolicy::AgePolicy.new(context))
19
+ add(SlowPolicy::PercentilePolicy.new(context))
39
20
  end
40
21
 
41
- def stored!(request)
42
- last_seen[request.unique_name] = Time.now
22
+ # policy is an object that behaves like a policy (responds to .call(req) for the score, and .store!(req))
23
+ def add(policy)
24
+ unless policy.respond_to?(:call) && policy.respond_to?(:stored!)
25
+ raise "SlowRequestPolicy must implement policy api call(req) and stored!(req)"
26
+ end
27
+
28
+ @policies << policy
43
29
  end
44
30
 
45
31
  # Determine if this request trace should be fully analyzed by scoring it
@@ -56,56 +42,11 @@ module ScoutApm
56
42
  return -1 # A negative score, should never be good enough to store.
57
43
  end
58
44
 
59
- total_time = request.root_layer.total_call_time
60
-
61
- # How long has it been since we've seen this?
62
- age = Time.now - last_seen[unique_name]
63
-
64
- # What approximate percentile was this request?
65
- percentile = context.request_histograms.approximate_quantile_of_value(unique_name, total_time)
66
-
67
- percent_of_total_time = context.transaction_time_consumed.percent_of_total(unique_name)
68
-
69
- return speed_points(total_time) + percentile_points(percentile) + age_points(age) + percent_time_points(percent_of_total_time)
70
- end
71
-
72
- private
73
-
74
- # Time in seconds
75
- # Logarithm keeps huge times from swamping the other metrics.
76
- # 1+ is necessary to keep the log function in positive territory.
77
- def speed_points(time)
78
- Math.log(1 + time) * POINT_MULTIPLIER_SPEED
79
- end
80
-
81
- def percentile_points(percentile)
82
- if percentile < 40
83
- 0.4 # Don't put much emphasis on capturing low percentiles.
84
- elsif percentile < 60
85
- 1.4 # Highest here to get mean traces
86
- elsif percentile < 90
87
- 0.7 # Between 60 & 90% is fine.
88
- elsif percentile >= 90
89
- 1.4 # Highest here to get 90+%ile traces
90
- else
91
- # impossible.
92
- percentile
93
- end
94
- end
95
-
96
- def age_points(age)
97
- age / 60.0 * POINT_MULTIPLIER_AGE
45
+ policies.map{ |p| p.call(request) }.sum
98
46
  end
99
47
 
100
- # Of the total time spent handling endpoints in this app, if this endpoint
101
- # is a higher percent, it should get more points.
102
- #
103
- # A: 20 calls @ 100ms each => 2 seconds of total time
104
- # B: 10 calls @ 100ms each => 1 second of total time
105
- #
106
- # Then A is 66% of the total call time
107
- def percent_time_points(percent) # Scale 0.0 - 1.0
108
- percent * POINT_MULTIPLIER_PERCENT_TIME
48
+ def stored!(request)
49
+ policies.each{ |p| p.stored!(request) }
109
50
  end
110
51
  end
111
52
  end
@@ -1,3 +1,3 @@
1
1
  module ScoutApm
2
- VERSION = "2.6.10"
2
+ VERSION = "4.0.0"
3
3
  end
data/scout_apm.gemspec CHANGED
@@ -21,6 +21,8 @@ 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'
25
+
24
26
  s.add_development_dependency "minitest"
25
27
  s.add_development_dependency "mocha"
26
28
  s.add_development_dependency "pry"
@@ -36,10 +38,8 @@ Gem::Specification.new do |s|
36
38
  s.add_development_dependency "activerecord"
37
39
  s.add_development_dependency "sqlite3"
38
40
 
39
- if RUBY_VERSION >= "1.9.3"
40
- s.add_development_dependency "rubocop"
41
- s.add_development_dependency "guard"
42
- s.add_development_dependency "guard-minitest"
43
- s.add_development_dependency "m"
44
- end
41
+ s.add_development_dependency "rubocop"
42
+ s.add_development_dependency "guard"
43
+ s.add_development_dependency "guard-minitest"
44
+ s.add_development_dependency "m"
45
45
  end
@@ -12,4 +12,18 @@ class AgentContextTest < Minitest::Test
12
12
  context = ScoutApm::AgentContext.new
13
13
  assert ScoutApm::ErrorService::ErrorBuffer, context.error_buffer.class
14
14
  end
15
+
16
+
17
+ class TestPolicy
18
+ def call(req); 1; end
19
+ def stored!(req); end
20
+ end
21
+
22
+ def test_customize_slow_request_policy
23
+ context = ScoutApm::AgentContext.new
24
+ assert 4, context.slow_request_policy.policies
25
+
26
+ context.slow_request_policy.add(TestPolicy.new)
27
+ assert 5, context.slow_request_policy.policies
28
+ end
15
29
  end
@@ -1,6 +1,7 @@
1
1
  require 'test_helper'
2
2
 
3
3
  require 'scout_apm/slow_request_policy'
4
+ require 'scout_apm/slow_policy/policy'
4
5
  require 'scout_apm/layer'
5
6
 
6
7
  class FakeRequest
@@ -16,35 +17,62 @@ class FakeRequest
16
17
  end
17
18
  end
18
19
 
20
+ class FixedPolicy < ScoutApm::SlowPolicy::Policy
21
+ attr_reader :stored
22
+
23
+ def initialize(x)
24
+ @x = x
25
+ end
26
+
27
+ def call(req)
28
+ @x
29
+ end
30
+
31
+ def stored!(req)
32
+ @stored = true
33
+ end
34
+ end
35
+
19
36
  class SlowRequestPolicyTest < Minitest::Test
20
37
  def setup
21
38
  @context = ScoutApm::AgentContext.new
22
39
  end
23
40
 
24
- def test_stored_records_current_time
41
+ def test_age_policy_stored_records_current_time
25
42
  test_start = Time.now
26
- policy = ScoutApm::SlowRequestPolicy.new(@context)
43
+ policy = ScoutApm::SlowPolicy::AgePolicy.new(@context)
27
44
  request = FakeRequest.new("users/index")
28
45
 
29
46
  policy.stored!(request)
30
47
  assert policy.last_seen[request.unique_name] > test_start
31
48
  end
32
49
 
33
- def test_score
50
+ def test_sums_up_score
34
51
  policy = ScoutApm::SlowRequestPolicy.new(@context)
35
52
  request = FakeRequest.new("users/index")
36
53
 
37
- request.set_duration(10) # 10 seconds
38
- policy.last_seen[request.unique_name] = Time.now - 120 # 2 minutes since last seen
39
- @context.request_histograms.add(request.unique_name, 1)
40
- @context.transaction_time_consumed.add(request.unique_name, 1)
54
+ policy.add(FixedPolicy.new(1))
55
+ policy.add(FixedPolicy.new(2))
41
56
 
42
- # Actual value I have in console is 4.01
43
- # Score uses Time.now to compare w/ last_seen, and will tick up slowly as
44
- # time passes, hence the range below.
45
- score = policy.score(request)
57
+ assert_equal 3, policy.score(request)
58
+ end
59
+
60
+ def test_calls_store_on_policies
61
+ policy = ScoutApm::SlowRequestPolicy.new(@context)
62
+ request = FakeRequest.new("users/index")
63
+
64
+ policy.add(fp1 = FixedPolicy.new(1))
65
+ policy.add(fp2 = FixedPolicy.new(2))
66
+ policy.stored!(request)
67
+
68
+ assert_equal true, fp1.stored
69
+ assert_equal true, fp2.stored
70
+ end
71
+
72
+ def test_checks_new_policy_api
73
+ policy = ScoutApm::SlowRequestPolicy.new(@context)
46
74
 
47
- assert score > 3.95
48
- assert score < 4.05
75
+ assert_raises { policy.add(Object.new) }
76
+ assert_raises { policy.add(->(req){1}) } # only implements call
49
77
  end
50
78
  end
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: 2.6.10
4
+ version: 4.0.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-10-20 00:00:00.000000000 Z
12
+ date: 2020-11-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -369,8 +369,12 @@ files:
369
369
  - lib/scout_apm/server_integrations/thin.rb
370
370
  - lib/scout_apm/server_integrations/unicorn.rb
371
371
  - lib/scout_apm/server_integrations/webrick.rb
372
- - lib/scout_apm/slow_job_policy.rb
373
372
  - lib/scout_apm/slow_job_record.rb
373
+ - lib/scout_apm/slow_policy/age_policy.rb
374
+ - lib/scout_apm/slow_policy/percent_policy.rb
375
+ - lib/scout_apm/slow_policy/percentile_policy.rb
376
+ - lib/scout_apm/slow_policy/policy.rb
377
+ - lib/scout_apm/slow_policy/speed_policy.rb
374
378
  - lib/scout_apm/slow_request_policy.rb
375
379
  - lib/scout_apm/slow_transaction.rb
376
380
  - lib/scout_apm/stack_item.rb
@@ -442,7 +446,6 @@ files:
442
446
  - test/unit/request_histograms_test.rb
443
447
  - test/unit/scored_item_set_test.rb
444
448
  - test/unit/serializers/payload_serializer_test.rb
445
- - test/unit/slow_job_policy_test.rb
446
449
  - test/unit/slow_request_policy_test.rb
447
450
  - test/unit/sql_sanitizer_test.rb
448
451
  - test/unit/store_test.rb
@@ -465,16 +468,16 @@ require_paths:
465
468
  - data
466
469
  required_ruby_version: !ruby/object:Gem::Requirement
467
470
  requirements:
468
- - - ">="
471
+ - - "~>"
469
472
  - !ruby/object:Gem::Version
470
- version: '0'
473
+ version: '2.1'
471
474
  required_rubygems_version: !ruby/object:Gem::Requirement
472
475
  requirements:
473
476
  - - ">="
474
477
  - !ruby/object:Gem::Version
475
478
  version: '0'
476
479
  requirements: []
477
- rubygems_version: 3.0.8
480
+ rubygems_version: 3.0.3
478
481
  signing_key:
479
482
  specification_version: 4
480
483
  summary: Ruby application performance monitoring
@@ -1,111 +0,0 @@
1
- # Long running class that determines if, and in how much detail a potentially
2
- # slow job should be recorded in
3
-
4
- module ScoutApm
5
- class SlowJobPolicy
6
- CAPTURE_TYPES = [
7
- CAPTURE_DETAIL = "capture_detail",
8
- CAPTURE_NONE = "capture_none",
9
- ]
10
-
11
- # Adjust speed points. See the function
12
- POINT_MULTIPLIER_SPEED = 0.25
13
-
14
- # For each minute we haven't seen an endpoint
15
- POINT_MULTIPLIER_AGE = 0.25
16
-
17
- # Outliers are worth up to "1000ms" of weight
18
- POINT_MULTIPLIER_PERCENTILE = 1.0
19
-
20
- # Points for an endpoint's who's throughput * response time is a large % of
21
- # overall time spent processing requests
22
- POINT_MULTIPLIER_PERCENT_TIME = 2.5
23
-
24
- # A hash of Job Names to the last time we stored a slow trace for it.
25
- #
26
- # Defaults to a start time that is pretty close to application boot time.
27
- # So the "age" of an endpoint we've never seen is the time the application
28
- # has been running.
29
- attr_reader :last_seen
30
-
31
- # The AgentContext we're running in
32
- attr_reader :context
33
-
34
- def initialize(context)
35
- @context = context
36
-
37
- zero_time = Time.now
38
- @last_seen = Hash.new { |h, k| h[k] = zero_time }
39
- end
40
-
41
- def stored!(request)
42
- last_seen[request.unique_name] = Time.now
43
- end
44
-
45
- # Determine if this job trace should be fully analyzed by scoring it
46
- # across several metrics, and then determining if that's good enough to
47
- # make it into this minute's payload.
48
- #
49
- # Due to the combining nature of the agent & layaway file, there's no
50
- # guarantee that a high scoring local champion will still be a winner when
51
- # they go up to "regionals" and are compared against the other processes
52
- # running on a node.
53
- def score(request)
54
- unique_name = request.unique_name
55
- if unique_name == :unknown
56
- return -1 # A negative score, should never be good enough to store.
57
- end
58
-
59
- total_time = request.root_layer.total_call_time
60
-
61
- # How long has it been since we've seen this?
62
- age = Time.now - last_seen[unique_name]
63
-
64
- # What approximate percentile was this request?
65
- percentile = context.request_histograms.approximate_quantile_of_value(unique_name, total_time)
66
-
67
- percent_of_total_time = context.transaction_time_consumed.percent_of_total(unique_name)
68
-
69
- return speed_points(total_time) + percentile_points(percentile) + age_points(age) + percent_time_points(percent_of_total_time)
70
- end
71
-
72
- private
73
-
74
- # Time in seconds
75
- # Logarithm keeps huge times from swamping the other metrics.
76
- # 1+ is necessary to keep the log function in positive territory.
77
- def speed_points(time)
78
- Math.log(1 + time) * POINT_MULTIPLIER_SPEED
79
- end
80
-
81
- def percentile_points(percentile)
82
- if percentile < 40
83
- 0.4 # Don't put much emphasis on capturing low percentiles.
84
- elsif percentile < 60
85
- 1.4 # Highest here to get mean traces
86
- elsif percentile < 90
87
- 0.7 # Between 60 & 90% is fine.
88
- elsif percentile >= 90
89
- 1.4 # Highest here to get 90+%ile traces
90
- else
91
- # impossible.
92
- percentile
93
- end
94
- end
95
-
96
- def age_points(age)
97
- age / 60.0 * POINT_MULTIPLIER_AGE
98
- end
99
-
100
- # Of the total time spent handling endpoints in this app, if this endpoint
101
- # is a higher percent, it should get more points.
102
- #
103
- # A: 20 calls @ 100ms each => 2 seconds of total time
104
- # B: 10 calls @ 100ms each => 1 second of total time
105
- #
106
- # Then A is 66% of the total call time
107
- def percent_time_points(percent) # Scale 0.0 - 1.0
108
- percent * POINT_MULTIPLIER_PERCENT_TIME
109
- end
110
- end
111
- end
@@ -1,6 +0,0 @@
1
- require 'test_helper'
2
-
3
- require 'scout_apm/slow_job_policy'
4
-
5
- class SlowJobPolicyTest < Minitest::Test
6
- end