statsd-instrument 3.9.9 → 3.10.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 +4 -4
- data/.github/workflows/tests.yml +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +21 -0
- data/lib/statsd/instrument/aggregator.rb +166 -45
- data/lib/statsd/instrument/client.rb +85 -4
- data/lib/statsd/instrument/compiled_metric.rb +465 -0
- data/lib/statsd/instrument/version.rb +1 -1
- data/lib/statsd/instrument.rb +2 -1
- data/test/aggregator_test.rb +158 -6
- data/test/client_test.rb +75 -4
- data/test/compiled_metric/counter_test.rb +396 -0
- data/test/compiled_metric/distribution_test.rb +503 -0
- data/test/compiled_metric/gauge_test.rb +395 -0
- data/test/compiled_metric_test.rb +447 -0
- data/test/dispatcher_stats_test.rb +6 -6
- data/test/integration_test.rb +52 -0
- metadata +12 -3
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class CompiledMetricDefinitionTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
super
|
|
8
|
+
@old_client = StatsD.singleton_client
|
|
9
|
+
@sink = StatsD::Instrument::CaptureSink.new(parent: StatsD::Instrument::NullSink.new)
|
|
10
|
+
StatsD.singleton_client = StatsD::Instrument::Client.new(
|
|
11
|
+
sink: @sink,
|
|
12
|
+
prefix: "test",
|
|
13
|
+
default_tags: [],
|
|
14
|
+
enable_aggregation: false,
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def teardown
|
|
19
|
+
super
|
|
20
|
+
@sink.clear
|
|
21
|
+
StatsD.singleton_client = @old_client
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_sanitizes_tag_names
|
|
25
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
26
|
+
define(
|
|
27
|
+
name: "foo.bar",
|
|
28
|
+
static_tags: { "tag|with|pipes" => "value", "tag,with,commas" => "value2" },
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
metric.increment(1)
|
|
33
|
+
|
|
34
|
+
datagram = @sink.datagrams.first
|
|
35
|
+
# Pipes and commas should be removed from tag names
|
|
36
|
+
assert(datagram.tags[0], "tag_with_pipes:value")
|
|
37
|
+
assert(datagram.tags[1], "tag_with_commas:value2")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_sanitizes_tag_values_in_static_tags
|
|
41
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
42
|
+
define(
|
|
43
|
+
name: "foo.bar",
|
|
44
|
+
static_tags: { service: "web|api" },
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
metric.increment(1)
|
|
49
|
+
|
|
50
|
+
datagram = @sink.datagrams.first
|
|
51
|
+
# Pipes should be removed from tag values
|
|
52
|
+
assert_equal(["service:webapi"], datagram.tags)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_sanitizes_dynamic_string_tag_values
|
|
56
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
57
|
+
define(
|
|
58
|
+
name: "foo.bar",
|
|
59
|
+
tags: { endpoint: String },
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
metric.increment(1, endpoint: "/api|v1,endpoint")
|
|
64
|
+
|
|
65
|
+
datagram = @sink.datagrams.first
|
|
66
|
+
# Pipes and commas should be removed
|
|
67
|
+
assert_equal(["endpoint:/apiv1endpoint"], datagram.tags)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_normalizes_metric_name
|
|
71
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
72
|
+
define(
|
|
73
|
+
name: "foo:bar|baz@qux",
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
metric.increment(1)
|
|
77
|
+
|
|
78
|
+
datagram = @sink.datagrams.first
|
|
79
|
+
# Special characters should be converted to underscores
|
|
80
|
+
assert_equal("test.foo_bar_baz_qux", datagram.name)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_raises_on_unsupported_tag_type
|
|
84
|
+
assert_raises(ArgumentError) do
|
|
85
|
+
Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
86
|
+
define(
|
|
87
|
+
name: "foo.bar",
|
|
88
|
+
tags: { invalid: Array },
|
|
89
|
+
)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def test_sample_rate_parameter
|
|
95
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
96
|
+
define(
|
|
97
|
+
name: "foo.bar",
|
|
98
|
+
static_tags: { service: "web" },
|
|
99
|
+
|
|
100
|
+
sample_rate: 0.5,
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
metric.increment(1)
|
|
105
|
+
|
|
106
|
+
assert_equal(1, @sink.datagrams.size)
|
|
107
|
+
assert_equal(0.5, @sink.datagrams.first.sample_rate)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_default_sample_rate_from_client
|
|
111
|
+
# Create a client with default sample rate
|
|
112
|
+
client = StatsD::Instrument::Client.new(
|
|
113
|
+
sink: @sink,
|
|
114
|
+
prefix: "test",
|
|
115
|
+
default_tags: [],
|
|
116
|
+
enable_aggregation: false,
|
|
117
|
+
default_sample_rate: 0.6,
|
|
118
|
+
)
|
|
119
|
+
StatsD.singleton_client = client
|
|
120
|
+
|
|
121
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
122
|
+
define(
|
|
123
|
+
name: "foo.bar",
|
|
124
|
+
)
|
|
125
|
+
end
|
|
126
|
+
metric.increment(1)
|
|
127
|
+
assert_equal(1, @sink.datagrams.size)
|
|
128
|
+
assert_equal(0.6, @sink.datagrams.first.sample_rate)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def test_sample_rate_default_to_1
|
|
132
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
133
|
+
define(
|
|
134
|
+
name: "foo.bar",
|
|
135
|
+
static_tags: { service: "web" },
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
metric.increment(5)
|
|
140
|
+
|
|
141
|
+
assert_equal(1, @sink.datagrams.size)
|
|
142
|
+
assert_equal(1.0, @sink.datagrams.first.sample_rate)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def test_sample_rate_omitted_when_1
|
|
146
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
147
|
+
define(
|
|
148
|
+
name: "foo.bar",
|
|
149
|
+
sample_rate: 1.0,
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# With sample rate = 1.0, it should be omitted from the datagram
|
|
154
|
+
metric.increment(3)
|
|
155
|
+
|
|
156
|
+
assert_equal(1, @sink.datagrams.size)
|
|
157
|
+
datagram = @sink.datagrams.first
|
|
158
|
+
# Sample rate defaults to 1.0 when not present in datagram
|
|
159
|
+
assert_equal(1.0, datagram.sample_rate)
|
|
160
|
+
# Verify the source doesn't contain |@1.0
|
|
161
|
+
refute_includes(datagram.source, "|@")
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def test_normalizes_symbol_tag_values
|
|
165
|
+
# Test with tag value that's a symbol (should hit the else clause)
|
|
166
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
167
|
+
define(
|
|
168
|
+
name: "foo.bar",
|
|
169
|
+
tags: { status: String },
|
|
170
|
+
)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Pass a symbol as a tag value (not a common case but should be handled)
|
|
174
|
+
# This will be converted to string
|
|
175
|
+
metric.increment(1, status: :active)
|
|
176
|
+
|
|
177
|
+
datagram = @sink.datagrams.first
|
|
178
|
+
assert_equal(["status:active"], datagram.tags)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def test_supports_boolean_dynamic_tags
|
|
182
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
183
|
+
define(
|
|
184
|
+
name: "foo.bar",
|
|
185
|
+
tags: { enabled: :Boolean },
|
|
186
|
+
)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
metric.increment(1, enabled: true)
|
|
190
|
+
assert_equal(["enabled:true"], @sink.datagrams.first.tags)
|
|
191
|
+
|
|
192
|
+
@sink.clear
|
|
193
|
+
|
|
194
|
+
metric.increment(1, enabled: false)
|
|
195
|
+
assert_equal(["enabled:false"], @sink.datagrams.first.tags)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def test_supports_symbol_dynamic_tags
|
|
199
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
200
|
+
define(
|
|
201
|
+
name: "foo.bar",
|
|
202
|
+
tags: { status: Symbol },
|
|
203
|
+
)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
metric.increment(1, status: :active)
|
|
207
|
+
assert_equal(["status:active"], @sink.datagrams.first.tags)
|
|
208
|
+
|
|
209
|
+
@sink.clear
|
|
210
|
+
|
|
211
|
+
metric.increment(1, status: :inactive)
|
|
212
|
+
assert_equal(["status:inactive"], @sink.datagrams.first.tags)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def test_sanitizes_symbol_dynamic_tag_values
|
|
216
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
217
|
+
define(
|
|
218
|
+
name: "foo.bar",
|
|
219
|
+
tags: { status: Symbol },
|
|
220
|
+
)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
metric.increment(1, status: :"active|with,special")
|
|
224
|
+
assert_equal(["status:activewithspecial"], @sink.datagrams.first.tags)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def test_handles_nil_tag_values
|
|
228
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
229
|
+
define(
|
|
230
|
+
name: "foo.bar",
|
|
231
|
+
tags: { shop_id: Integer, name: String, rate: Float },
|
|
232
|
+
)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
metric.increment(1, shop_id: nil, name: nil, rate: nil)
|
|
236
|
+
|
|
237
|
+
assert_equal(1, @sink.datagrams.size)
|
|
238
|
+
assert_equal(["name:", "rate:", "shop_id:"], @sink.datagrams.first.tags.sort)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def test_emits_metric_when_cache_exceeded
|
|
242
|
+
# Create a metric with a very small cache size
|
|
243
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
244
|
+
define(
|
|
245
|
+
name: "foo.bar",
|
|
246
|
+
tags: { shop_id: Integer },
|
|
247
|
+
|
|
248
|
+
max_cache_size: 2,
|
|
249
|
+
)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Clear any existing datagrams
|
|
253
|
+
@sink.clear
|
|
254
|
+
|
|
255
|
+
# Fill the cache (2 entries)
|
|
256
|
+
metric.increment(1, shop_id: 1)
|
|
257
|
+
metric.increment(1, shop_id: 2)
|
|
258
|
+
|
|
259
|
+
# Third entry brings us to max_cache_size. it should trigger cache exceeded (cache.size = 3 >= 2)
|
|
260
|
+
metric.increment(1, shop_id: 3)
|
|
261
|
+
|
|
262
|
+
# Find the cache exceeded metric (includes prefix)
|
|
263
|
+
cache_exceeded_metric = @sink.datagrams.find do |datagram|
|
|
264
|
+
datagram.name == "test.statsd_instrument.compiled_metric.cache_exceeded_total"
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
assert_equal(4, @sink.datagrams.size)
|
|
268
|
+
refute_nil(cache_exceeded_metric, "Expected cache exceeded metric to be emitted")
|
|
269
|
+
assert_equal(1, cache_exceeded_metric.value)
|
|
270
|
+
assert_includes(cache_exceeded_metric.tags, "metric_name:foo.bar")
|
|
271
|
+
assert_includes(cache_exceeded_metric.tags, "max_size:2")
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def test_emits_metric_on_hash_collision
|
|
275
|
+
# Create a metric with a single tag
|
|
276
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
277
|
+
define(
|
|
278
|
+
name: "foo.bar",
|
|
279
|
+
tags: { shop_id: Integer },
|
|
280
|
+
)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# First call with shop_id=1 to populate cache
|
|
284
|
+
metric.increment(1, shop_id: 1)
|
|
285
|
+
|
|
286
|
+
cache = metric.instance_variable_get(:@tag_combination_cache)
|
|
287
|
+
|
|
288
|
+
# Compute cache keys using the rotate-left + XOR formula (32-bit bounded)
|
|
289
|
+
# For single tag, it's the hash value masked to 32 bits
|
|
290
|
+
cache_key_for_1 = 1.hash & 0xFFFFFFFF
|
|
291
|
+
cache_key_for_2 = 2.hash & 0xFFFFFFFF
|
|
292
|
+
|
|
293
|
+
# Store the cached datagram under the collision key
|
|
294
|
+
cached_datagram = cache[cache_key_for_1]
|
|
295
|
+
cache[cache_key_for_2] = cached_datagram
|
|
296
|
+
|
|
297
|
+
# Clear datagrams before the collision test
|
|
298
|
+
@sink.clear
|
|
299
|
+
|
|
300
|
+
# Now increment with the collision shop_id - this should detect the collision
|
|
301
|
+
# because the tag_values won't match
|
|
302
|
+
metric.increment(1, shop_id: 2)
|
|
303
|
+
|
|
304
|
+
# Find the hash collision metric (includes prefix)
|
|
305
|
+
hash_collision_metric = @sink.datagrams.find do |datagram|
|
|
306
|
+
datagram.name == "test.statsd_instrument.compiled_metric.hash_collision_detected"
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
refute_nil(hash_collision_metric, "Expected hash collision metric to be emitted")
|
|
310
|
+
assert_equal(1, hash_collision_metric.value)
|
|
311
|
+
# The metric name uses the normalized name (only : | @ are replaced, not .)
|
|
312
|
+
assert_includes(hash_collision_metric.tags, "metric_name:foo.bar")
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def test_handles_default_tags_as_array
|
|
316
|
+
StatsD.singleton_client = StatsD::Instrument::Client.new(
|
|
317
|
+
sink: @sink,
|
|
318
|
+
prefix: "test",
|
|
319
|
+
default_tags: ["env:production", "region:us-east"],
|
|
320
|
+
enable_aggregation: false,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
324
|
+
define(
|
|
325
|
+
name: "foo.bar",
|
|
326
|
+
static_tags: { service: "web" },
|
|
327
|
+
)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
metric.increment(1)
|
|
331
|
+
|
|
332
|
+
datagram = @sink.datagrams.first
|
|
333
|
+
assert_equal(["env:production", "region:us-east", "service:web"], datagram.tags.sort)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def test_handles_default_tags_as_hash
|
|
337
|
+
StatsD.singleton_client = StatsD::Instrument::Client.new(
|
|
338
|
+
sink: @sink,
|
|
339
|
+
prefix: "test",
|
|
340
|
+
default_tags: { env: "production", region: "us-east" },
|
|
341
|
+
enable_aggregation: false,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
345
|
+
define(
|
|
346
|
+
name: "foo.bar",
|
|
347
|
+
static_tags: { service: "web" },
|
|
348
|
+
)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
metric.increment(1)
|
|
352
|
+
|
|
353
|
+
datagram = @sink.datagrams.first
|
|
354
|
+
assert_equal(["env:production", "region:us-east", "service:web"], datagram.tags.sort)
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def test_handles_default_tags_as_string
|
|
358
|
+
StatsD.singleton_client = StatsD::Instrument::Client.new(
|
|
359
|
+
sink: @sink,
|
|
360
|
+
prefix: "test",
|
|
361
|
+
default_tags: "env:production",
|
|
362
|
+
enable_aggregation: false,
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
366
|
+
define(
|
|
367
|
+
name: "foo.bar",
|
|
368
|
+
static_tags: { service: "web" },
|
|
369
|
+
)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
metric.increment(1)
|
|
373
|
+
|
|
374
|
+
datagram = @sink.datagrams.first
|
|
375
|
+
assert_equal(["env:production", "service:web"], datagram.tags.sort)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def test_allows_value_as_tag_name
|
|
379
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
380
|
+
define(
|
|
381
|
+
name: "foo.bar",
|
|
382
|
+
tags: { value: String, status: String },
|
|
383
|
+
)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
metric.increment(1, value: "product", status: "active")
|
|
387
|
+
|
|
388
|
+
datagram = @sink.datagrams.first
|
|
389
|
+
assert_equal(["status:active", "value:product"], datagram.tags.sort)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def test_cache_key_is_order_dependent
|
|
393
|
+
# Verify that swapped tag values produce different cache entries
|
|
394
|
+
# This tests the fix for XOR-based cache keys which are commutative
|
|
395
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
396
|
+
define(
|
|
397
|
+
name: "foo.bar",
|
|
398
|
+
tags: { tag_a: :Boolean, tag_b: :Boolean },
|
|
399
|
+
)
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# Emit with (true, false)
|
|
403
|
+
metric.increment(1, tag_a: true, tag_b: false)
|
|
404
|
+
|
|
405
|
+
# Emit with (false, true) - swapped values
|
|
406
|
+
metric.increment(1, tag_a: false, tag_b: true)
|
|
407
|
+
|
|
408
|
+
# Both should be cached separately
|
|
409
|
+
cache = metric.instance_variable_get(:@tag_combination_cache)
|
|
410
|
+
assert_equal(2, cache.size, "Swapped tag values should produce different cache entries")
|
|
411
|
+
|
|
412
|
+
# Verify the datagrams have different tags
|
|
413
|
+
assert_equal(2, @sink.datagrams.size)
|
|
414
|
+
assert_equal(["tag_a:true", "tag_b:false"], @sink.datagrams[0].tags)
|
|
415
|
+
assert_equal(["tag_a:false", "tag_b:true"], @sink.datagrams[1].tags)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def test_sample_rate
|
|
419
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
420
|
+
define(
|
|
421
|
+
name: "foo.bar",
|
|
422
|
+
sample_rate: 0.1337,
|
|
423
|
+
)
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
assert_equal(0.1337, metric.sample_rate)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def test_sample_rate_with_define_without_sample_rate
|
|
430
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
431
|
+
define(
|
|
432
|
+
name: "foo.bar.withouth_sample_rate",
|
|
433
|
+
)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
assert_equal(StatsD.singleton_client.default_sample_rate, metric.sample_rate)
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def test_sample_rate_without_define
|
|
440
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter)
|
|
441
|
+
|
|
442
|
+
error = assert_raises(ArgumentError) do
|
|
443
|
+
metric.sample_rate
|
|
444
|
+
end
|
|
445
|
+
assert_equal("Every CompiledMetric subclass needs to call `define` before accessing its sample_rate.", error.message)
|
|
446
|
+
end
|
|
447
|
+
end
|
|
@@ -48,21 +48,21 @@ class DispatcherStatsTest < Minitest::Test
|
|
|
48
48
|
end
|
|
49
49
|
assert_equal(batches.length, stats.instance_variable_get(:@batched_sends))
|
|
50
50
|
assert_equal(
|
|
51
|
-
batches.map
|
|
51
|
+
batches.map do |b|
|
|
52
52
|
b[:buffer_len]
|
|
53
|
-
|
|
53
|
+
end.sum / batches.length,
|
|
54
54
|
stats.instance_variable_get(:@avg_buffer_length),
|
|
55
55
|
)
|
|
56
56
|
assert_equal(
|
|
57
|
-
batches.map
|
|
57
|
+
batches.map do |b|
|
|
58
58
|
b[:packet_size]
|
|
59
|
-
|
|
59
|
+
end.sum / batches.length,
|
|
60
60
|
stats.instance_variable_get(:@avg_batched_packet_size),
|
|
61
61
|
)
|
|
62
62
|
assert_equal(
|
|
63
|
-
batches.map
|
|
63
|
+
batches.map do |b|
|
|
64
64
|
b[:batch_len]
|
|
65
|
-
|
|
65
|
+
end.sum / batches.length,
|
|
66
66
|
stats.instance_variable_get(:@avg_batch_length),
|
|
67
67
|
)
|
|
68
68
|
end
|
data/test/integration_test.rb
CHANGED
|
@@ -103,4 +103,56 @@ class IntegrationTest < Minitest::Test
|
|
|
103
103
|
assert_match(/counter:\d+|c/, packets.find { |packet| packet.start_with?("counter:") })
|
|
104
104
|
assert_match(/test_distribution:\d+:3|d/, packets.find { |packet| packet.start_with?("test_distribution:") })
|
|
105
105
|
end
|
|
106
|
+
|
|
107
|
+
def test_signal_trap_with_aggregation_fallback
|
|
108
|
+
skip("#{RUBY_ENGINE} not supported for this test. Reason: signal handling") if RUBY_ENGINE != "ruby"
|
|
109
|
+
|
|
110
|
+
client = StatsD::Instrument::Environment.new(
|
|
111
|
+
"STATSD_ADDR" => "#{@server.addr[2]}:#{@server.addr[1]}",
|
|
112
|
+
"STATSD_IMPLEMENTATION" => "dogstatsd",
|
|
113
|
+
"STATSD_ENV" => "production",
|
|
114
|
+
"STATSD_ENABLE_AGGREGATION" => "true",
|
|
115
|
+
"STATSD_AGGREGATION_INTERVAL" => "5.0",
|
|
116
|
+
).client
|
|
117
|
+
|
|
118
|
+
signal_received = false
|
|
119
|
+
|
|
120
|
+
old_trap = Signal.trap("USR1") do
|
|
121
|
+
signal_received = true
|
|
122
|
+
# These should fall back to direct writes
|
|
123
|
+
client.increment("trap_metric", 5)
|
|
124
|
+
client.gauge("trap_gauge", 42)
|
|
125
|
+
client.distribution("trap_distribution", 100)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
Process.kill("USR1", Process.pid)
|
|
129
|
+
|
|
130
|
+
sleep(0.1)
|
|
131
|
+
|
|
132
|
+
assert(signal_received, "Signal should have been received")
|
|
133
|
+
|
|
134
|
+
packets = []
|
|
135
|
+
while IO.select([@server], nil, nil, 0.1)
|
|
136
|
+
packet = @server.recvfrom(300).first
|
|
137
|
+
packets.concat(packet.split("\n"))
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# When aggregation is disabled due to trap context, metrics might be batched
|
|
141
|
+
assert(packets.size >= 3, "Expected at least 3 metrics, got #{packets.size}: #{packets.inspect}")
|
|
142
|
+
|
|
143
|
+
assert(
|
|
144
|
+
packets.any? { |p| p == "trap_metric:5|c" },
|
|
145
|
+
"Expected counter metric, got: #{packets.inspect}",
|
|
146
|
+
)
|
|
147
|
+
assert(
|
|
148
|
+
packets.any? { |p| p == "trap_gauge:42|g" },
|
|
149
|
+
"Expected gauge metric, got: #{packets.inspect}",
|
|
150
|
+
)
|
|
151
|
+
assert(
|
|
152
|
+
packets.any? { |p| p == "trap_distribution:100|d" },
|
|
153
|
+
"Expected distribution metric, got: #{packets.inspect}",
|
|
154
|
+
)
|
|
155
|
+
ensure
|
|
156
|
+
Signal.trap("USR1", old_trap || "DEFAULT")
|
|
157
|
+
end
|
|
106
158
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: statsd-instrument
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jesse Storimer
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
- Willem van Bergen
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
13
|
dependencies: []
|
|
14
14
|
description: A StatsD client for Ruby apps. Provides metaprogramming methods to inject
|
|
15
15
|
StatsD instrumentation into your code.
|
|
@@ -49,6 +49,7 @@ files:
|
|
|
49
49
|
- lib/statsd/instrument/batched_sink.rb
|
|
50
50
|
- lib/statsd/instrument/capture_sink.rb
|
|
51
51
|
- lib/statsd/instrument/client.rb
|
|
52
|
+
- lib/statsd/instrument/compiled_metric.rb
|
|
52
53
|
- lib/statsd/instrument/connection_behavior.rb
|
|
53
54
|
- lib/statsd/instrument/datagram.rb
|
|
54
55
|
- lib/statsd/instrument/datagram_builder.rb
|
|
@@ -86,6 +87,10 @@ files:
|
|
|
86
87
|
- test/capture_sink_test.rb
|
|
87
88
|
- test/changelog_test.rb
|
|
88
89
|
- test/client_test.rb
|
|
90
|
+
- test/compiled_metric/counter_test.rb
|
|
91
|
+
- test/compiled_metric/distribution_test.rb
|
|
92
|
+
- test/compiled_metric/gauge_test.rb
|
|
93
|
+
- test/compiled_metric_test.rb
|
|
89
94
|
- test/datagram_builder_test.rb
|
|
90
95
|
- test/datagram_test.rb
|
|
91
96
|
- test/dispatcher_stats_test.rb
|
|
@@ -130,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
130
135
|
- !ruby/object:Gem::Version
|
|
131
136
|
version: '0'
|
|
132
137
|
requirements: []
|
|
133
|
-
rubygems_version:
|
|
138
|
+
rubygems_version: 4.0.8
|
|
134
139
|
specification_version: 4
|
|
135
140
|
summary: A StatsD client for Ruby apps
|
|
136
141
|
test_files:
|
|
@@ -142,6 +147,10 @@ test_files:
|
|
|
142
147
|
- test/capture_sink_test.rb
|
|
143
148
|
- test/changelog_test.rb
|
|
144
149
|
- test/client_test.rb
|
|
150
|
+
- test/compiled_metric/counter_test.rb
|
|
151
|
+
- test/compiled_metric/distribution_test.rb
|
|
152
|
+
- test/compiled_metric/gauge_test.rb
|
|
153
|
+
- test/compiled_metric_test.rb
|
|
145
154
|
- test/datagram_builder_test.rb
|
|
146
155
|
- test/datagram_test.rb
|
|
147
156
|
- test/dispatcher_stats_test.rb
|