scout_apm 5.5.0 → 5.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +10 -0
- data/lib/scout_apm/agent_context.rb +4 -0
- data/lib/scout_apm/config.rb +62 -34
- data/lib/scout_apm/sampling.rb +96 -0
- data/lib/scout_apm/tracked_request.rb +5 -1
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +1 -0
- data/test/test_helper.rb +29 -0
- data/test/unit/sampling_test.rb +215 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46df05a2d67c23f243d1eedfddd2eabdd8a6151ef8974e185ea7c8efbe01d076
|
4
|
+
data.tar.gz: 072bcd67b72f477c924dd647f2e3f87e586f3a97ff8dbbd727d5f5991c060f54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31ab64b2f5a212322989d118edba3f92cb4c4a223618bebd7f4fae318ebb243459dcd730388a080d7e7d6e0ce1147af857bb656f67bdeda902878e167f195f90
|
7
|
+
data.tar.gz: 9191e3bf53423a7db1e9c0265a6b8cf03eb7bce0374ed392c137ab1c592dffee312c8efe8e6301e3a0fad17e033429af4c0b98b7608438be44251682236874d6
|
data/CHANGELOG.markdown
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 5.6.0
|
4
|
+
- New options for sampling and ignore configurationn (#521)
|
5
|
+
- `sample_rate` - Set the rate at which requests are sampled globally (1-100, a percentage of requests to keep).
|
6
|
+
- `ignore_endpoints` - Ignore endpoints by regex matching prefix (Same as and replaces `ignore`)
|
7
|
+
- `sample_endpoints` - Sample endpoints by regex matching prefix (i.e. ['/foo:70']).
|
8
|
+
- `endpoint_sample_rate` - Set the rate at which all non-matching web requests are sampled.
|
9
|
+
- `ignore_jobs` - Ignore Jobs by explicit name match.
|
10
|
+
- `sample_jobs` - Sample Jobs by explicit name match (i.e. ['MyJob:70']).
|
11
|
+
- `job_sample_rate` - Set the rate at which all non-matching background jobs are sampled.
|
12
|
+
|
3
13
|
# 5.5.0
|
4
14
|
- Fix undeclared logger in grape instruments (#510)
|
5
15
|
- Drop guaranteed support for Rubies <= 2.4
|
data/lib/scout_apm/config.rb
CHANGED
@@ -42,6 +42,13 @@ require 'scout_apm/environment'
|
|
42
42
|
# any instruments listed in this array. Default: []
|
43
43
|
# prepend_instruments - If `use_prepend` is false, force using Module#prepend for any
|
44
44
|
# instruments listed in this array. Default: []
|
45
|
+
# ignore_endpoints - An array of endpoints to ignore. These are matched as regular expressions. (supercedes 'ignore')
|
46
|
+
# ignore_jobs - An array of job names to ignore.
|
47
|
+
# sample_rate - Rate to sample entire application. An integer between 0 and 100. 0 means no traces are sent, 100 means all traces are sent.
|
48
|
+
# sample_endpoints - An array of endpoints to sample. These are matched as regular expressions with individual sample rate of 0 to 100.
|
49
|
+
# sample_jobs - An array of job names with individual sample rate of 0 to 100.
|
50
|
+
# endpoint_sample_rate - Rate to sample all endpoints. An integer between 0 and 100. 0 means no traces are sent, 100 means all traces are sent. (supercedes 'sample_rate')
|
51
|
+
# job_sample_rate - Rate to sample all jobs. An integer between 0 and 100. 0 means no traces are sent, 100 means all traces are sent. (supercedes 'sample_rate')
|
45
52
|
#
|
46
53
|
# Any of these config settings can be set with an environment variable prefixed
|
47
54
|
# by SCOUT_ and uppercasing the key: SCOUT_LOG_LEVEL for instance.
|
@@ -67,6 +74,8 @@ module ScoutApm
|
|
67
74
|
'host',
|
68
75
|
'hostname',
|
69
76
|
'ignore',
|
77
|
+
'ignore_endpoints',
|
78
|
+
'ignore_jobs',
|
70
79
|
'key',
|
71
80
|
'log_class',
|
72
81
|
'log_file_path',
|
@@ -83,6 +92,11 @@ module ScoutApm
|
|
83
92
|
'remote_agent_port',
|
84
93
|
'report_format',
|
85
94
|
'revision_sha',
|
95
|
+
'sample_rate',
|
96
|
+
'sample_endpoints',
|
97
|
+
'sample_jobs',
|
98
|
+
'endpoint_sample_rate',
|
99
|
+
'job_sample_rate',
|
86
100
|
'scm_subdirectory',
|
87
101
|
'start_resque_server_instrument',
|
88
102
|
'ssl_cert_file',
|
@@ -184,6 +198,8 @@ module ScoutApm
|
|
184
198
|
'dev_trace' => BooleanCoercion.new,
|
185
199
|
'enable_background_jobs' => BooleanCoercion.new,
|
186
200
|
'ignore' => JsonCoercion.new,
|
201
|
+
'ignore_endpoints' => JsonCoercion.new,
|
202
|
+
'ignore_jobs' => JsonCoercion.new,
|
187
203
|
'max_traces' => IntegerCoercion.new,
|
188
204
|
'monitor' => BooleanCoercion.new,
|
189
205
|
'collect_remote_ip' => BooleanCoercion.new,
|
@@ -194,6 +210,11 @@ module ScoutApm
|
|
194
210
|
'external_service_metric_report_limit' => IntegerCoercion.new,
|
195
211
|
'instrument_http_url_length' => IntegerCoercion.new,
|
196
212
|
'record_queue_time' => BooleanCoercion.new,
|
213
|
+
'sample_rate' => IntegerCoercion.new,
|
214
|
+
'sample_endpoints' => JsonCoercion.new,
|
215
|
+
'sample_jobs' => JsonCoercion.new,
|
216
|
+
'endpoint_sample_rate' => IntegerCoercion.new,
|
217
|
+
'job_sample_rate' => IntegerCoercion.new,
|
197
218
|
'start_resque_server_instrument' => BooleanCoercion.new,
|
198
219
|
'timeline_traces' => BooleanCoercion.new,
|
199
220
|
'auto_instruments' => BooleanCoercion.new,
|
@@ -290,41 +311,48 @@ module ScoutApm
|
|
290
311
|
|
291
312
|
class ConfigDefaults
|
292
313
|
DEFAULTS = {
|
293
|
-
'compress_payload'
|
294
|
-
'detailed_middleware'
|
295
|
-
'dev_trace'
|
296
|
-
'direct_host'
|
297
|
-
'disabled_instruments'
|
298
|
-
'enable_background_jobs'
|
299
|
-
'host'
|
300
|
-
'ignore'
|
301
|
-
'
|
302
|
-
'
|
303
|
-
'
|
304
|
-
'
|
305
|
-
'
|
306
|
-
'
|
307
|
-
'
|
308
|
-
'
|
309
|
-
'
|
310
|
-
'
|
311
|
-
'
|
314
|
+
'compress_payload' => true,
|
315
|
+
'detailed_middleware' => false,
|
316
|
+
'dev_trace' => false,
|
317
|
+
'direct_host' => 'https://apm.scoutapp.com',
|
318
|
+
'disabled_instruments' => [],
|
319
|
+
'enable_background_jobs' => true,
|
320
|
+
'host' => 'https://checkin.scoutapp.com',
|
321
|
+
'ignore' => [],
|
322
|
+
'ignore_endpoints' => [],
|
323
|
+
'ignore_jobs' => [],
|
324
|
+
'log_level' => 'info',
|
325
|
+
'max_traces' => 10,
|
326
|
+
'profile' => true, # for scoutprof
|
327
|
+
'report_format' => 'json',
|
328
|
+
'scm_subdirectory' => '',
|
329
|
+
'uri_reporting' => 'full_path',
|
330
|
+
'remote_agent_host' => '127.0.0.1',
|
331
|
+
'remote_agent_port' => 7721, # picked at random
|
332
|
+
'database_metric_limit' => 5000, # The hard limit on db metrics
|
333
|
+
'database_metric_report_limit' => 1000,
|
334
|
+
'external_service_metric_limit' => 5000, # The hard limit on external service metrics
|
312
335
|
'external_service_metric_report_limit' => 1000,
|
313
|
-
'instrument_http_url_length'
|
314
|
-
'
|
315
|
-
'
|
316
|
-
'
|
317
|
-
'
|
318
|
-
'
|
319
|
-
'
|
320
|
-
'
|
321
|
-
'
|
322
|
-
'
|
323
|
-
'
|
324
|
-
'
|
325
|
-
'
|
326
|
-
'
|
327
|
-
'
|
336
|
+
'instrument_http_url_length' => 300,
|
337
|
+
'sample_rate' => 100,
|
338
|
+
'sample_endpoints' => [],
|
339
|
+
'sample_jobs' => [],
|
340
|
+
'endpoint_sample_rate' => 100,
|
341
|
+
'job_sample_rate' => 100,
|
342
|
+
'start_resque_server_instrument' => true, # still only starts if Resque is detected
|
343
|
+
'collect_remote_ip' => true,
|
344
|
+
'record_queue_time' => true,
|
345
|
+
'timeline_traces' => true,
|
346
|
+
'auto_instruments' => false,
|
347
|
+
'auto_instruments_ignore' => [],
|
348
|
+
'use_prepend' => false,
|
349
|
+
'alias_method_instruments' => [],
|
350
|
+
'prepend_instruments' => [],
|
351
|
+
'ssl_cert_file' => File.join( File.dirname(__FILE__), *%w[.. .. data cacert.pem] ),
|
352
|
+
'errors_enabled' => false,
|
353
|
+
'errors_ignored_exceptions' => %w(ActiveRecord::RecordNotFound ActionController::RoutingError),
|
354
|
+
'errors_filtered_params' => %w(password s3-key),
|
355
|
+
'errors_host' => 'https://errors.scoutapm.com',
|
328
356
|
}.freeze
|
329
357
|
|
330
358
|
def value(key)
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
class Sampling
|
3
|
+
attr_reader :global_sample_rate, :sample_endpoints, :sample_uri_regex, :sample_jobs, :ignore_uri_regex, :ignore_jobs
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@global_sample_rate = config.value('sample_rate')
|
7
|
+
# web endpoints matched prefix by regex
|
8
|
+
# jobs matched explicitly by name
|
9
|
+
|
10
|
+
# for now still support old config key ('ignore') for backwards compatibility
|
11
|
+
@ignore_endpoints = config.value('ignore').present? ? config.value('ignore') : config.value('ignore_endpoints')
|
12
|
+
@sample_endpoints = individual_sample_to_hash(config.value('sample_endpoints'))
|
13
|
+
@endpoint_sample_rate = config.value('endpoint_sample_rate')
|
14
|
+
|
15
|
+
@ignore_jobs = config.value('ignore_jobs')
|
16
|
+
@sample_jobs = individual_sample_to_hash(config.value('sample_jobs'))
|
17
|
+
@job_sample_rate = config.value('job_sample_rate')
|
18
|
+
|
19
|
+
logger.info("Sampling initialized with config: global_sample_rate: #{@global_sample_rate}, endpoint_sample_rate: #{@endpoint_sample_rate}, sample_endpoints: #{@sample_endpoints}, ignore_endpoints: #{@ignore_endpoints}, job_sample_rate: #@job_sample_rate}, sample_jobs: #{@sample_jobs}, ignore_jobs: #{@ignore_jobs}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def drop_request?(transaction)
|
23
|
+
# Individual endpoint/job sampling takes precedence over ignoring.
|
24
|
+
# Individual endpoint/job sample rate always takes precedence over general endpoint/job rate.
|
25
|
+
# General endpoint/job rate always takes precedence over global sample rate
|
26
|
+
if transaction.job?
|
27
|
+
job_name = transaction.layer_finder.job.name
|
28
|
+
rate = job_sample_rate(job_name)
|
29
|
+
return sample?(rate) unless rate.nil?
|
30
|
+
return true if ignore_job?(job_name)
|
31
|
+
return sample?(@job_sample_rate) unless @job_sample_rate.nil?
|
32
|
+
elsif transaction.web?
|
33
|
+
uri = transaction.annotations[:uri]
|
34
|
+
rate = web_sample_rate(uri)
|
35
|
+
return sample?(rate) unless rate.nil?
|
36
|
+
return true if ignore_uri?(uri)
|
37
|
+
return sample?(@endpoint_sample_rate) unless @endpoint_sample_rate.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
# global sample check
|
41
|
+
if @global_sample_rate
|
42
|
+
return sample?(@global_sample_rate)
|
43
|
+
end
|
44
|
+
|
45
|
+
false # don't drop the request
|
46
|
+
end
|
47
|
+
|
48
|
+
def individual_sample_to_hash(sampling_config)
|
49
|
+
return nil if sampling_config.blank?
|
50
|
+
# config looks like ['/foo:50','/bar:100']. parse it into hash of string: integer
|
51
|
+
sample_hash = {}
|
52
|
+
sampling_config.each do |sample|
|
53
|
+
path, rate = sample.split(':')
|
54
|
+
sample_hash[path] = rate.to_i
|
55
|
+
end
|
56
|
+
sample_hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def ignore_uri?(uri)
|
60
|
+
return false if @ignore_endpoints.blank?
|
61
|
+
@ignore_endpoints.each do |prefix|
|
62
|
+
return true if uri.start_with?(prefix)
|
63
|
+
end
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
def web_sample_rate(uri)
|
68
|
+
return nil if @sample_endpoints.blank?
|
69
|
+
@sample_endpoints.each do |prefix, rate|
|
70
|
+
return rate if uri.start_with?(prefix)
|
71
|
+
end
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
def ignore_job?(job_name)
|
76
|
+
return false if @ignore_jobs.blank?
|
77
|
+
@ignore_jobs.include?(job_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
def job_sample_rate(job_name)
|
81
|
+
return nil if @sample_jobs.blank?
|
82
|
+
@sample_jobs.fetch(job_name, nil)
|
83
|
+
end
|
84
|
+
|
85
|
+
def sample?(rate)
|
86
|
+
rand * 100 > rate
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def logger
|
92
|
+
ScoutApm::Agent.instance.logger
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -303,7 +303,11 @@ module ScoutApm
|
|
303
303
|
restore_from_dump! if @agent_context.nil?
|
304
304
|
|
305
305
|
# Bail out early if the user asked us to ignore this uri
|
306
|
-
return if @agent_context.ignored_uris.ignore?(annotations[:uri])
|
306
|
+
# return if @agent_context.ignored_uris.ignore?(annotations[:uri])
|
307
|
+
if @agent_context.sampling.drop_request?(self)
|
308
|
+
logger.debug("Dropping request due to sampling")
|
309
|
+
return
|
310
|
+
end
|
307
311
|
|
308
312
|
apply_name_override
|
309
313
|
|
data/lib/scout_apm/version.rb
CHANGED
data/lib/scout_apm.rb
CHANGED
@@ -111,6 +111,7 @@ require 'scout_apm/instruments/samplers'
|
|
111
111
|
require 'scout_apm/app_server_load'
|
112
112
|
|
113
113
|
require 'scout_apm/ignored_uris.rb'
|
114
|
+
require 'scout_apm/sampling.rb'
|
114
115
|
require 'scout_apm/utils/active_record_metric_name'
|
115
116
|
require 'scout_apm/utils/backtrace_parser'
|
116
117
|
require 'scout_apm/utils/installed_gems'
|
data/test/test_helper.rb
CHANGED
@@ -38,6 +38,10 @@ class FakeConfigOverlay
|
|
38
38
|
@values[key]
|
39
39
|
end
|
40
40
|
|
41
|
+
def values
|
42
|
+
@values
|
43
|
+
end
|
44
|
+
|
41
45
|
def has_key?(key)
|
42
46
|
@values.has_key?(key)
|
43
47
|
end
|
@@ -169,3 +173,28 @@ end
|
|
169
173
|
class Minitest::Test
|
170
174
|
include CustomAsserts
|
171
175
|
end
|
176
|
+
|
177
|
+
class FakeTrackedRequest
|
178
|
+
def self.new_web_request(uri)
|
179
|
+
context = ScoutApm::Agent.instance.context
|
180
|
+
fake_store = ScoutApm::FakeStore.new
|
181
|
+
req = ScoutApm::TrackedRequest.new(context, fake_store)
|
182
|
+
|
183
|
+
first_layer = ScoutApm::Layer.new("Controller", "index")
|
184
|
+
req.start_layer(first_layer)
|
185
|
+
req.annotate_request(:uri => uri)
|
186
|
+
|
187
|
+
req
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.new_job_request(job_name)
|
191
|
+
context = ScoutApm::Agent.instance.context
|
192
|
+
fake_store = ScoutApm::FakeStore.new
|
193
|
+
req = ScoutApm::TrackedRequest.new(context, fake_store)
|
194
|
+
|
195
|
+
first_layer = ScoutApm::Layer.new("Job", job_name)
|
196
|
+
req.start_layer(first_layer)
|
197
|
+
|
198
|
+
req
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'scout_apm/sampling'
|
4
|
+
|
5
|
+
class SamplingTest < Minitest::Test
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@global_sample_config = FakeConfigOverlay.new(
|
9
|
+
{
|
10
|
+
'sample_rate' => 80,
|
11
|
+
}
|
12
|
+
)
|
13
|
+
|
14
|
+
@individual_config = FakeConfigOverlay.new(
|
15
|
+
{
|
16
|
+
'sample_endpoints' => ['/foo/bar:100', '/foo:50', '/bar/zap:80'],
|
17
|
+
'ignore_endpoints' => ['/baz'],
|
18
|
+
'sample_jobs' => ['joba:50'],
|
19
|
+
'ignore_jobs' => 'jobb,jobc',
|
20
|
+
}
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_individual_sample_to_hash
|
25
|
+
sampling = ScoutApm::Sampling.new(@individual_config)
|
26
|
+
assert_equal({'/foo/bar' => 100, '/foo' => 50, '/bar/zap' => 80}, sampling.individual_sample_to_hash(@individual_config.value('sample_endpoints')))
|
27
|
+
|
28
|
+
sampling = ScoutApm::Sampling.new(@global_sample_config)
|
29
|
+
assert_equal nil, sampling.individual_sample_to_hash(@global_sample_config.value('sample_endpoints'))
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_uri_ignore
|
33
|
+
sampling = ScoutApm::Sampling.new(@individual_config)
|
34
|
+
assert_equal true, sampling.ignore_uri?('/baz/bap')
|
35
|
+
assert_equal false, sampling.ignore_uri?('/foo/far')
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_uri_sample
|
39
|
+
sampling = ScoutApm::Sampling.new(@individual_config)
|
40
|
+
rate = sampling.web_sample_rate('/foo/far')
|
41
|
+
assert_equal 50, rate
|
42
|
+
|
43
|
+
rate = sampling.web_sample_rate('/bar')
|
44
|
+
assert_equal nil, rate
|
45
|
+
|
46
|
+
rate = sampling.web_sample_rate('/baz/bap')
|
47
|
+
assert_equal nil, rate
|
48
|
+
|
49
|
+
rate = sampling.web_sample_rate('/foo/bar/baz')
|
50
|
+
assert_equal 100, rate
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_job_ignore
|
54
|
+
sampling = ScoutApm::Sampling.new(@individual_config)
|
55
|
+
assert_equal true, sampling.ignore_job?('jobb')
|
56
|
+
assert_equal false, sampling.ignore_job?('joba')
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_job_sample
|
60
|
+
sampling = ScoutApm::Sampling.new(@individual_config)
|
61
|
+
assert_equal 50, sampling.job_sample_rate('joba')
|
62
|
+
assert_equal nil, sampling.job_sample_rate('jobb')
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_sample
|
66
|
+
sampling = ScoutApm::Sampling.new(@individual_config)
|
67
|
+
sampling.stub(:rand, 0.01) do
|
68
|
+
assert_equal(false, sampling.sample?(50))
|
69
|
+
end
|
70
|
+
sampling.stub(:rand, 0.99) do
|
71
|
+
assert_equal(true, sampling.sample?(50))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_old_ignore
|
76
|
+
config = FakeConfigOverlay.new({'ignore' => ['/foo', '/bar']})
|
77
|
+
sampling = ScoutApm::Sampling.new(config)
|
78
|
+
assert_equal true, sampling.ignore_uri?('/foo')
|
79
|
+
assert_equal true, sampling.ignore_uri?('/bar/bap')
|
80
|
+
assert_equal false, sampling.ignore_uri?('/baz')
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_web_request_individual_sampling
|
84
|
+
sampling = ScoutApm::Sampling.new(@individual_config)
|
85
|
+
|
86
|
+
# should be ignored
|
87
|
+
transaction = FakeTrackedRequest.new_web_request('/baz/bap')
|
88
|
+
assert_equal true, sampling.drop_request?(transaction)
|
89
|
+
|
90
|
+
# should be kept
|
91
|
+
transaction = FakeTrackedRequest.new_web_request('/faz/bap')
|
92
|
+
assert_equal false, sampling.drop_request?(transaction)
|
93
|
+
|
94
|
+
transaction = FakeTrackedRequest.new_web_request('/foo/far')
|
95
|
+
sampling.stub(:rand, 0.01) do
|
96
|
+
assert_equal false, sampling.drop_request?(transaction)
|
97
|
+
end
|
98
|
+
|
99
|
+
# passes individual sample but caught by global rate
|
100
|
+
sampling.stub(:rand, 0.99) do
|
101
|
+
assert_equal true, sampling.drop_request?(transaction)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_web_reqeust_general_sampling
|
106
|
+
config = FakeConfigOverlay.new(@individual_config.values.merge({'endpoint_sample_rate' => 80}))
|
107
|
+
sampling = ScoutApm::Sampling.new(config)
|
108
|
+
|
109
|
+
transaction = FakeTrackedRequest.new_web_request('/foo/far')
|
110
|
+
transaction2 = FakeTrackedRequest.new_web_request('/ooo/oar')
|
111
|
+
# /foo/far sampled at 50 specifically, /ooo/oar caught by general endpoint rate of 80
|
112
|
+
sampling.stub(:rand, 0.01) do
|
113
|
+
assert_equal false, sampling.drop_request?(transaction)
|
114
|
+
assert_equal false, sampling.drop_request?(transaction2)
|
115
|
+
end
|
116
|
+
|
117
|
+
sampling.stub(:rand, 0.70) do
|
118
|
+
assert_equal true, sampling.drop_request?(transaction)
|
119
|
+
assert_equal false, sampling.drop_request?(transaction2)
|
120
|
+
end
|
121
|
+
|
122
|
+
sampling.stub(:rand, 0.99) do
|
123
|
+
assert_equal true, sampling.drop_request?(transaction)
|
124
|
+
assert_equal true, sampling.drop_request?(transaction2)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_web_request_with_global_sampling
|
129
|
+
config = FakeConfigOverlay.new(@individual_config.values.merge({'sample_rate' => 20}))
|
130
|
+
sampling = ScoutApm::Sampling.new(config)
|
131
|
+
|
132
|
+
# caught by individual rate
|
133
|
+
transaction = FakeTrackedRequest.new_web_request('/foo/far')
|
134
|
+
sampling.stub(:rand, 0.01) do
|
135
|
+
assert_equal false, sampling.drop_request?(transaction)
|
136
|
+
end
|
137
|
+
|
138
|
+
# passes individual rate (50) but caught by global rate (20)
|
139
|
+
sampling.stub(:rand, 0.30) do
|
140
|
+
assert_equal false, sampling.drop_request?(transaction)
|
141
|
+
end
|
142
|
+
|
143
|
+
# passes individual rate
|
144
|
+
sampling.stub(:rand, 0.99) do
|
145
|
+
assert_equal true, sampling.drop_request?(transaction)
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_job_request_individual_sampling
|
151
|
+
sampling = ScoutApm::Sampling.new(@individual_config)
|
152
|
+
|
153
|
+
# should be ignored
|
154
|
+
transaction = FakeTrackedRequest.new_job_request('jobb')
|
155
|
+
assert_equal true, sampling.drop_request?(transaction)
|
156
|
+
|
157
|
+
# should be kept
|
158
|
+
transaction = FakeTrackedRequest.new_job_request('jobz')
|
159
|
+
assert_equal false, sampling.drop_request?(transaction)
|
160
|
+
|
161
|
+
# should be sampled if rand > 50
|
162
|
+
transaction = FakeTrackedRequest.new_job_request('joba')
|
163
|
+
sampling.stub(:rand, 0.01) do
|
164
|
+
assert_equal false, sampling.drop_request?(transaction)
|
165
|
+
end
|
166
|
+
|
167
|
+
sampling.stub(:rand, 0.99) do
|
168
|
+
assert_equal true, sampling.drop_request?(transaction)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_job_general_sampling
|
173
|
+
config = FakeConfigOverlay.new(@individual_config.values.merge({'job_sample_rate' => 80}))
|
174
|
+
sampling = ScoutApm::Sampling.new(config)
|
175
|
+
|
176
|
+
transaction = FakeTrackedRequest.new_job_request('joba')
|
177
|
+
transaction2 = FakeTrackedRequest.new_job_request('jobz')
|
178
|
+
# joba sampled at 50 specifically, jobz caught by general job rate of 80
|
179
|
+
sampling.stub(:rand, 0.01) do
|
180
|
+
assert_equal false, sampling.drop_request?(transaction)
|
181
|
+
assert_equal false, sampling.drop_request?(transaction2)
|
182
|
+
end
|
183
|
+
|
184
|
+
sampling.stub(:rand, 0.70) do
|
185
|
+
assert_equal true, sampling.drop_request?(transaction)
|
186
|
+
assert_equal false, sampling.drop_request?(transaction2)
|
187
|
+
end
|
188
|
+
|
189
|
+
sampling.stub(:rand, 0.99) do
|
190
|
+
assert_equal true, sampling.drop_request?(transaction)
|
191
|
+
assert_equal true, sampling.drop_request?(transaction2)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_job_request_global_sampling
|
196
|
+
config = FakeConfigOverlay.new(@individual_config.values.merge({'sample_rate' => 20}))
|
197
|
+
sampling = ScoutApm::Sampling.new(config)
|
198
|
+
|
199
|
+
# caught by individual rate
|
200
|
+
transaction = FakeTrackedRequest.new_job_request('joba')
|
201
|
+
sampling.stub(:rand, 0.01) do
|
202
|
+
assert_equal false, sampling.drop_request?(transaction)
|
203
|
+
end
|
204
|
+
|
205
|
+
# passes individual rate (50) but caught by global rate (20)
|
206
|
+
sampling.stub(:rand, 0.30) do
|
207
|
+
assert_equal false, sampling.drop_request?(transaction)
|
208
|
+
end
|
209
|
+
|
210
|
+
# passes individual rate
|
211
|
+
sampling.stub(:rand, 0.99) do
|
212
|
+
assert_equal true, sampling.drop_request?(transaction)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scout_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derek Haynes
|
8
8
|
- Andre Lewis
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-01-
|
11
|
+
date: 2025-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -365,6 +365,7 @@ files:
|
|
365
365
|
- lib/scout_apm/reporting.rb
|
366
366
|
- lib/scout_apm/request_histograms.rb
|
367
367
|
- lib/scout_apm/request_manager.rb
|
368
|
+
- lib/scout_apm/sampling.rb
|
368
369
|
- lib/scout_apm/scored_item_set.rb
|
369
370
|
- lib/scout_apm/serializers/app_server_load_serializer.rb
|
370
371
|
- lib/scout_apm/serializers/db_query_serializer_to_json.rb
|
@@ -471,6 +472,7 @@ files:
|
|
471
472
|
- test/unit/remote/route_test.rb
|
472
473
|
- test/unit/remote/server_test.rb
|
473
474
|
- test/unit/request_histograms_test.rb
|
475
|
+
- test/unit/sampling_test.rb
|
474
476
|
- test/unit/scored_item_set_test.rb
|
475
477
|
- test/unit/serializers/payload_serializer_test.rb
|
476
478
|
- test/unit/slow_request_policy_test.rb
|