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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3dfd72cdc46892ac46a974e4e3947f81ac2ee6580bee96c0cc6ad845ce50ee7
4
- data.tar.gz: 560fb31452b8c5b526bf77723d121d561096c759640c0266ba894ffe71901d99
3
+ metadata.gz: 46df05a2d67c23f243d1eedfddd2eabdd8a6151ef8974e185ea7c8efbe01d076
4
+ data.tar.gz: 072bcd67b72f477c924dd647f2e3f87e586f3a97ff8dbbd727d5f5991c060f54
5
5
  SHA512:
6
- metadata.gz: 3958265f12804e22dbb4c75d3b03a6d4ad9f87ff989e5382f8d0b890d55c2c2505828cd4022fa7a965092bb7c0948a12baea760b0d15fd05f5ceef6d1a23f962
7
- data.tar.gz: cb45c1255f16f299e7a8cc67c93efd21fd7912682c4d4ed2030498be069022f2cf8811a987bbec0c22ff28150d57ff95222ed0771e0c2758bbba057b1805d4a8
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
@@ -91,6 +91,10 @@ module ScoutApm
91
91
  @logger ||= LoggerFactory.build(config, environment)
92
92
  end
93
93
 
94
+ def sampling
95
+ @sampling ||= ScoutApm::Sampling.new(config)
96
+ end
97
+
94
98
  def ignored_uris
95
99
  @ignored_uris ||= ScoutApm::IgnoredUris.new(config.value('ignore'))
96
100
  end
@@ -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' => true,
294
- 'detailed_middleware' => false,
295
- 'dev_trace' => false,
296
- 'direct_host' => 'https://apm.scoutapp.com',
297
- 'disabled_instruments' => [],
298
- 'enable_background_jobs' => true,
299
- 'host' => 'https://checkin.scoutapp.com',
300
- 'ignore' => [],
301
- 'log_level' => 'info',
302
- 'max_traces' => 10,
303
- 'profile' => true, # for scoutprof
304
- 'report_format' => 'json',
305
- 'scm_subdirectory' => '',
306
- 'uri_reporting' => 'full_path',
307
- 'remote_agent_host' => '127.0.0.1',
308
- 'remote_agent_port' => 7721, # picked at random
309
- 'database_metric_limit' => 5000, # The hard limit on db metrics
310
- 'database_metric_report_limit' => 1000,
311
- 'external_service_metric_limit' => 5000, # The hard limit on external service metrics
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' => 300,
314
- 'start_resque_server_instrument' => true, # still only starts if Resque is detected
315
- 'collect_remote_ip' => true,
316
- 'record_queue_time' => true,
317
- 'timeline_traces' => true,
318
- 'auto_instruments' => false,
319
- 'auto_instruments_ignore' => [],
320
- 'use_prepend' => false,
321
- 'alias_method_instruments' => [],
322
- 'prepend_instruments' => [],
323
- 'ssl_cert_file' => File.join( File.dirname(__FILE__), *%w[.. .. data cacert.pem] ),
324
- 'errors_enabled' => false,
325
- 'errors_ignored_exceptions' => %w(ActiveRecord::RecordNotFound ActionController::RoutingError),
326
- 'errors_filtered_params' => %w(password s3-key),
327
- 'errors_host' => 'https://errors.scoutapm.com',
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
 
@@ -1,3 +1,3 @@
1
1
  module ScoutApm
2
- VERSION = "5.5.0"
2
+ VERSION = "5.6.0"
3
3
  end
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.5.0
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-02 00:00:00.000000000 Z
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