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.
@@ -0,0 +1,395 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class CompiledMetricGaugeTest < 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_gauge_without_define
25
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge)
26
+
27
+ error = assert_raises(ArgumentError) do
28
+ metric.gauge(5)
29
+ end
30
+ assert_equal("Every CompiledMetric subclass needs to call `define` before first invocation of gauge.", error.message)
31
+ end
32
+
33
+ def test_define_counter_without_tags
34
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
35
+ define(name: "foo.bar")
36
+ end
37
+
38
+ metric.gauge(5)
39
+
40
+ datagram = @sink.datagrams.first
41
+ assert_equal("test.foo.bar", datagram.name)
42
+ assert_equal(5, datagram.value)
43
+ assert_equal(:g, datagram.type)
44
+ assert_nil(datagram.tags)
45
+ end
46
+
47
+ def test_define_counter_with_static_tags
48
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
49
+ define(
50
+ name: "foo.bar",
51
+ static_tags: { service: "web", env: "prod" },
52
+ )
53
+ end
54
+
55
+ metric.gauge(5)
56
+
57
+ datagram = @sink.datagrams.first
58
+ assert_equal("test.foo.bar", datagram.name)
59
+ assert_equal(5, datagram.value)
60
+ assert_equal(:g, datagram.type)
61
+ assert_equal(["env:prod", "service:web"], datagram.tags.sort)
62
+ end
63
+
64
+ def test_define_counter_with_dynamic_tags
65
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
66
+ define(
67
+ name: "foo.bar",
68
+ tags: { shop_id: Integer, user_id: Integer },
69
+ )
70
+ end
71
+
72
+ metric.gauge(1, shop_id: 123, user_id: 456)
73
+
74
+ datagram = @sink.datagrams.first
75
+ assert_equal("test.foo.bar", datagram.name)
76
+ assert_equal(1, datagram.value)
77
+ assert_equal(:g, datagram.type)
78
+ assert_equal(["shop_id:123", "user_id:456"], datagram.tags.sort)
79
+ end
80
+
81
+ def test_define_counter_with_mixed_tags
82
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
83
+ define(
84
+ name: "foo.bar",
85
+ static_tags: { service: "web" },
86
+ tags: { shop_id: Integer },
87
+ )
88
+ end
89
+
90
+ metric.gauge(3, shop_id: 999)
91
+
92
+ datagram = @sink.datagrams.first
93
+ assert_equal("test.foo.bar", datagram.name)
94
+ assert_equal(3, datagram.value)
95
+ assert_equal(["service:web", "shop_id:999"], datagram.tags.sort)
96
+ end
97
+
98
+ def test_define_counter_with_string_tags
99
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
100
+ define(
101
+ name: "foo.bar",
102
+ tags: { country: String, region: String },
103
+ )
104
+ end
105
+
106
+ metric.gauge(2, country: "US", region: "West")
107
+
108
+ datagram = @sink.datagrams.first
109
+ assert_equal("test.foo.bar", datagram.name)
110
+ assert_equal(2, datagram.value)
111
+ assert_equal(["country:US", "region:West"], datagram.tags.sort)
112
+ end
113
+
114
+ def test_define_counter_with_float_tags
115
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
116
+ define(
117
+ name: "foo.bar",
118
+ tags: { rate: Float },
119
+ )
120
+ end
121
+
122
+ metric.gauge(1, rate: 1.5)
123
+
124
+ datagram = @sink.datagrams.first
125
+ assert_equal("test.foo.bar", datagram.name)
126
+ assert_equal(1, datagram.value)
127
+ # Float formatting uses %f which outputs full precision
128
+ assert_equal(1, datagram.tags.size)
129
+ assert_match(/^rate:1\.5/, datagram.tags.first)
130
+ end
131
+
132
+ def test_define_counter_no_prefix
133
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
134
+ define(
135
+ name: "foo.bar",
136
+ no_prefix: true,
137
+ )
138
+ end
139
+
140
+ metric.gauge(1)
141
+
142
+ datagram = @sink.datagrams.first
143
+ assert_equal("foo.bar", datagram.name) # No "test." prefix
144
+ end
145
+
146
+ def test_multiple_gauge_same_tags
147
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
148
+ define(
149
+ name: "foo.bar",
150
+ tags: { shop_id: Integer },
151
+ )
152
+ end
153
+
154
+ metric.gauge(1, shop_id: 123)
155
+ metric.gauge(2, shop_id: 123)
156
+ metric.gauge(3, shop_id: 123)
157
+
158
+ assert_equal(3, @sink.datagrams.size)
159
+ @sink.datagrams.each do |datagram|
160
+ assert_equal(["shop_id:123"], datagram.tags)
161
+ end
162
+ end
163
+
164
+ def test_multiple_increments_different_tags
165
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
166
+ define(
167
+ name: "foo.bar",
168
+ tags: { shop_id: Integer },
169
+ )
170
+ end
171
+
172
+ metric.gauge(1, shop_id: 123)
173
+ metric.gauge(1, shop_id: 456)
174
+ metric.gauge(1, shop_id: 789)
175
+
176
+ assert_equal(3, @sink.datagrams.size)
177
+ assert_equal(["shop_id:123"], @sink.datagrams[0].tags)
178
+ assert_equal(["shop_id:456"], @sink.datagrams[1].tags)
179
+ assert_equal(["shop_id:789"], @sink.datagrams[2].tags)
180
+ end
181
+
182
+ def test_gauge_includes_default_tags_from_client
183
+ # Create a client with default tags
184
+ client = StatsD::Instrument::Client.new(
185
+ sink: @sink,
186
+ prefix: "test",
187
+ default_tags: ["env:production", "region:us-east"],
188
+ enable_aggregation: false,
189
+ )
190
+ StatsD.singleton_client = client
191
+
192
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
193
+ define(
194
+ name: "foo.bar",
195
+ static_tags: { service: "web" },
196
+ )
197
+ end
198
+
199
+ metric.gauge(1)
200
+
201
+ datagram = @sink.datagrams.first
202
+ assert_equal("test.foo.bar", datagram.name)
203
+ # Should include default tags from client + static tags
204
+ assert_equal(["env:production", "region:us-east", "service:web"], datagram.tags.sort)
205
+ end
206
+
207
+ def test_gauge_includes_default_tags_with_no_prefix
208
+ # Create a client with default tags
209
+ client = StatsD::Instrument::Client.new(
210
+ sink: @sink,
211
+ prefix: "test",
212
+ default_tags: ["env:production", "region:us-east"],
213
+ enable_aggregation: false,
214
+ )
215
+ StatsD.singleton_client = client
216
+
217
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
218
+ define(
219
+ name: "foo.bar",
220
+ static_tags: { service: "web" },
221
+ no_prefix: true,
222
+ )
223
+ end
224
+
225
+ metric.gauge(1)
226
+
227
+ datagram = @sink.datagrams.first
228
+ assert_equal("foo.bar", datagram.name) # No prefix
229
+ # Should include default tags even when no_prefix is true
230
+ assert_equal(["env:production", "region:us-east", "service:web"], datagram.tags.sort)
231
+ end
232
+
233
+ def test_gauge_does_not_support_blocks
234
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
235
+ define(
236
+ name: "foo.bar",
237
+ static_tags: { service: "web" },
238
+ tags: { shop_id: Integer },
239
+ )
240
+ end
241
+
242
+ block_called = false
243
+ metric.gauge(42, shop_id: 999) do
244
+ block_called = true
245
+ end
246
+
247
+ datagram = @sink.datagrams.first
248
+ assert_equal("test.foo.bar", datagram.name)
249
+ assert_equal(42, datagram.value)
250
+ refute(block_called)
251
+ assert_equal(["service:web", "shop_id:999"], datagram.tags.sort)
252
+ end
253
+ end
254
+
255
+ class CompiledMetricGaugeWithAggregationTest < Minitest::Test
256
+ def setup
257
+ super
258
+ @old_client = StatsD.singleton_client
259
+ @sink = StatsD::Instrument::CaptureSink.new(parent: StatsD::Instrument::NullSink.new)
260
+ @aggregator = StatsD::Instrument::Aggregator.new(
261
+ @sink,
262
+ StatsD::Instrument::DatagramBuilder,
263
+ "test",
264
+ [],
265
+ flush_interval: 0.1,
266
+ )
267
+ client = StatsD::Instrument::Client.new(
268
+ sink: @sink,
269
+ prefix: "test",
270
+ default_tags: [],
271
+ enable_aggregation: true,
272
+ )
273
+ client.instance_variable_set(:@aggregator, @aggregator)
274
+ StatsD.singleton_client = client
275
+ end
276
+
277
+ def teardown
278
+ super
279
+ @sink.clear
280
+ StatsD.singleton_client = @old_client
281
+ end
282
+
283
+ def test_aggregates_precompiled_metrics
284
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
285
+ define(
286
+ name: "foo.bar",
287
+ tags: { shop_id: Integer },
288
+ )
289
+ end
290
+
291
+ metric.gauge(1, shop_id: 123)
292
+ metric.gauge(2, shop_id: 123)
293
+ metric.gauge(3, shop_id: 123)
294
+
295
+ @aggregator.flush
296
+
297
+ assert_equal(1, @sink.datagrams.size)
298
+ datagram = @sink.datagrams.first
299
+ assert_equal(3, datagram.value)
300
+ assert_equal(["shop_id:123"], datagram.tags)
301
+ end
302
+
303
+ def test_aggregates_different_tag_combinations_separately
304
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
305
+ define(
306
+ name: "foo.bar",
307
+ tags: { shop_id: Integer },
308
+ )
309
+ end
310
+
311
+ metric.gauge(1, shop_id: 123)
312
+ metric.gauge(2, shop_id: 456)
313
+ metric.gauge(3, shop_id: 123)
314
+
315
+ @aggregator.flush
316
+
317
+ assert_equal(2, @sink.datagrams.size)
318
+
319
+ shop_123_datagram = @sink.datagrams.find { |d| d.tags.include?("shop_id:123") }
320
+ shop_456_datagram = @sink.datagrams.find { |d| d.tags.include?("shop_id:456") }
321
+
322
+ assert_equal(3, shop_123_datagram.value)
323
+ assert_equal(2, shop_456_datagram.value)
324
+ end
325
+
326
+ def test_aggregates_static_tag_metrics
327
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
328
+ define(
329
+ name: "foo.bar",
330
+ static_tags: { service: "web" },
331
+ )
332
+ end
333
+
334
+ metric.gauge(1)
335
+ metric.gauge(2)
336
+ metric.gauge(5)
337
+
338
+ @aggregator.flush
339
+
340
+ assert_equal(1, @sink.datagrams.size)
341
+ datagram = @sink.datagrams.first
342
+ assert_equal(5, datagram.value)
343
+ end
344
+
345
+ def test_sample_rate_equal_to_1_with_aggregation
346
+ # When aggregating with sample_rate, sampling happens before aggregation
347
+ # This test verifies that with sample_rate=1.0, all increments are aggregated
348
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
349
+ define(
350
+ name: "foo.bar",
351
+ static_tags: { service: "web" },
352
+ sample_rate: 1.0,
353
+ )
354
+ end
355
+
356
+ # With sample_rate=1.0, all increments should be aggregated
357
+ metric.gauge(5)
358
+ metric.gauge(3)
359
+
360
+ @aggregator.flush
361
+
362
+ assert_equal(1, @sink.datagrams.size)
363
+ datagram = @sink.datagrams.first
364
+ assert_equal("test.foo.bar", datagram.name)
365
+ assert_equal(3, datagram.value)
366
+ # Sample rate should be 1.0 when aggregating
367
+ assert_equal(1.0, datagram.sample_rate)
368
+ refute_includes(datagram.source, "|@")
369
+ end
370
+
371
+ def test_sample_rate_applied_with_aggregation
372
+ # When aggregating with sample_rate, sampling happens before aggregation
373
+ # This test verifies that with sample_rate=0.5, all gauges are aggregated
374
+ metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge) do
375
+ define(
376
+ name: "foo.bar",
377
+ static_tags: { service: "web" },
378
+ sample_rate: 0.5,
379
+ )
380
+ end
381
+
382
+ metric.gauge(5)
383
+ metric.gauge(3)
384
+
385
+ @aggregator.flush
386
+
387
+ assert_equal(1, @sink.datagrams.size)
388
+ datagram = @sink.datagrams.first
389
+ assert_equal("test.foo.bar", datagram.name)
390
+ assert_equal(3, datagram.value) # 5 + 3
391
+ # Sample rate should be 1.0 when aggregating
392
+ assert_equal(0.5, datagram.sample_rate)
393
+ assert_includes(datagram.source, "|@")
394
+ end
395
+ end