scout_apm 2.6.10 → 4.0.0

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