statsd-instrument 3.10.0 → 3.11.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/CHANGELOG.md +5 -0
- data/lib/statsd/instrument/compiled_metric.rb +27 -3
- data/lib/statsd/instrument/version.rb +1 -1
- data/test/compiled_metric/counter_test.rb +1 -1
- data/test/compiled_metric/distribution_test.rb +1 -1
- data/test/compiled_metric/gauge_test.rb +1 -1
- data/test/compiled_metric_test.rb +70 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f2b45a3e809ef0e5ca26b7faf52512632705f62120b3b73987a10282e19ed76
|
|
4
|
+
data.tar.gz: 7d7574e77adea4dc54fcf0355b83e1392623bcf2d4ecccd60a2cc101f62c84bb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb416ef206605fb8204acec2b0501e4722a2bd51d81fbc1083c4a4a96203a2838dd329355908ee83b64b7a3943f8f8af862cbf7d8433363d4452e68f14292f95
|
|
7
|
+
data.tar.gz: bf0597922ce2dbe8837ef29b0de1f7c4fa1ca0e9623dc4d15628661b49059577d31499c46164a14e98744c5e765d68b407a15f14153c212ec245f3d90ed691e7
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,11 @@ section below.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased changes
|
|
8
8
|
|
|
9
|
+
## Version 3.11.0
|
|
10
|
+
|
|
11
|
+
- [#418](https://github.com/Shopify/statsd-instrument/pull/418) - Prevent misuse of `CompiledMetric` definitions by requiring a subclass when defining one.
|
|
12
|
+
- [#417](https://github.com/Shopify/statsd-instrument/pull/417) - Add a `metric_name` method to `CompiledMetric`.
|
|
13
|
+
|
|
9
14
|
## Version 3.10.0
|
|
10
15
|
|
|
11
16
|
- [#416](https://github.com/Shopify/statsd-instrument/pull/416) - Fix missing `metric_prefix` in Aggregator finalizer, causing metrics to lose their prefix when flushed during GC.
|
|
@@ -18,6 +18,12 @@ module StatsD
|
|
|
18
18
|
# # Later, emit with minimal allocations:
|
|
19
19
|
# CheckoutMetric.increment(shop_id: 123, user_id: 456, value: 1)
|
|
20
20
|
class CompiledMetric
|
|
21
|
+
# Raised when a CompiledMetric subclass is defined incorrectly.
|
|
22
|
+
# This includes calling `define` on a base type class directly,
|
|
23
|
+
# calling `define` more than once, or using a metric before `define`
|
|
24
|
+
# has been called.
|
|
25
|
+
class DefinitionError < StandardError; end
|
|
26
|
+
|
|
21
27
|
# Default maximum number of unique tag combinations to cache before clearing
|
|
22
28
|
# the cache to prevent unbounded memory growth
|
|
23
29
|
DEFAULT_MAX_TAG_COMBINATION_CACHE_SIZE = 5000
|
|
@@ -33,6 +39,19 @@ module StatsD
|
|
|
33
39
|
# @param max_cache_size [Integer] Maximum tag combinations this metric supports, and will be retained in-memory. Cardinality beyond this number will fall back to the slow path and should be avoided.
|
|
34
40
|
# @return [Class] A new CompiledMetric subclass configured for this metric
|
|
35
41
|
def define(name:, static_tags: {}, tags: {}, no_prefix: false, sample_rate: nil, max_cache_size: DEFAULT_MAX_TAG_COMBINATION_CACHE_SIZE)
|
|
42
|
+
if equal?(CompiledMetric) || superclass.equal?(CompiledMetric)
|
|
43
|
+
raise DefinitionError,
|
|
44
|
+
"`define` must be called on a subclass, not on #{self.name} directly. " \
|
|
45
|
+
"Use `Class.new(#{self.name}) { define(...) }` or " \
|
|
46
|
+
"`class MyMetric < #{self.name}; define(...); end` instead."
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if defined?(@datagram_blueprint)
|
|
50
|
+
raise DefinitionError,
|
|
51
|
+
"`define` has already been called on #{self.name}. " \
|
|
52
|
+
"Each CompiledMetric subclass can only be defined once."
|
|
53
|
+
end
|
|
54
|
+
|
|
36
55
|
client = StatsD.singleton_client
|
|
37
56
|
|
|
38
57
|
# Build the datagram blueprint using the builder
|
|
@@ -51,7 +70,7 @@ module StatsD
|
|
|
51
70
|
# Create a new class for this specific metric
|
|
52
71
|
# Using classes instead of instances for better YJIT optimization
|
|
53
72
|
metric_class = tap do
|
|
54
|
-
@name = DatagramBlueprintBuilder.normalize_name(name)
|
|
73
|
+
@name = DatagramBlueprintBuilder.normalize_name(name).freeze
|
|
55
74
|
@datagram_blueprint = datagram_blueprint
|
|
56
75
|
@tag_combination_cache = {}
|
|
57
76
|
@max_cache_size = max_cache_size
|
|
@@ -101,10 +120,15 @@ module StatsD
|
|
|
101
120
|
@singleton_client.sink.sample?(sample_rate)
|
|
102
121
|
end
|
|
103
122
|
|
|
123
|
+
# @return [String, nil] The normalized metric name for this compiled metric class (`nil` if not yet defined).
|
|
124
|
+
def metric_name
|
|
125
|
+
@name
|
|
126
|
+
end
|
|
127
|
+
|
|
104
128
|
# @return [Float] The defined sample rate for a metric class.
|
|
105
129
|
# Will raise when `define` has not yet been called on the class.
|
|
106
130
|
def sample_rate
|
|
107
|
-
raise
|
|
131
|
+
raise DefinitionError, "Every CompiledMetric subclass needs to call `define` before accessing its sample_rate." unless defined?(@sample_rate)
|
|
108
132
|
|
|
109
133
|
@sample_rate
|
|
110
134
|
end
|
|
@@ -115,7 +139,7 @@ module StatsD
|
|
|
115
139
|
# Once `define` was called during the class creation, it will override the
|
|
116
140
|
# method implementation to emit the actual metric datagrams.
|
|
117
141
|
def require_define_to_be_called
|
|
118
|
-
raise
|
|
142
|
+
raise DefinitionError, "Every CompiledMetric subclass needs to call `define` before first invocation of #{method_name}."
|
|
119
143
|
end
|
|
120
144
|
|
|
121
145
|
def generate_block_handler
|
|
@@ -24,7 +24,7 @@ class CompiledMetricCounterTest < Minitest::Test
|
|
|
24
24
|
def test_counter_without_define
|
|
25
25
|
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter)
|
|
26
26
|
|
|
27
|
-
error = assert_raises(
|
|
27
|
+
error = assert_raises(StatsD::Instrument::CompiledMetric::DefinitionError) do
|
|
28
28
|
metric.increment(5)
|
|
29
29
|
end
|
|
30
30
|
assert_equal("Every CompiledMetric subclass needs to call `define` before first invocation of increment.", error.message)
|
|
@@ -24,7 +24,7 @@ class CompiledMetricDistributionTest < Minitest::Test
|
|
|
24
24
|
def test_distribution_without_define
|
|
25
25
|
metric = Class.new(StatsD::Instrument::CompiledMetric::Distribution)
|
|
26
26
|
|
|
27
|
-
error = assert_raises(
|
|
27
|
+
error = assert_raises(StatsD::Instrument::CompiledMetric::DefinitionError) do
|
|
28
28
|
metric.distribution(5)
|
|
29
29
|
end
|
|
30
30
|
assert_equal("Every CompiledMetric subclass needs to call `define` before first invocation of distribution.", error.message)
|
|
@@ -24,7 +24,7 @@ class CompiledMetricGaugeTest < Minitest::Test
|
|
|
24
24
|
def test_gauge_without_define
|
|
25
25
|
metric = Class.new(StatsD::Instrument::CompiledMetric::Gauge)
|
|
26
26
|
|
|
27
|
-
error = assert_raises(
|
|
27
|
+
error = assert_raises(StatsD::Instrument::CompiledMetric::DefinitionError) do
|
|
28
28
|
metric.gauge(5)
|
|
29
29
|
end
|
|
30
30
|
assert_equal("Every CompiledMetric subclass needs to call `define` before first invocation of gauge.", error.message)
|
|
@@ -439,9 +439,78 @@ class CompiledMetricDefinitionTest < Minitest::Test
|
|
|
439
439
|
def test_sample_rate_without_define
|
|
440
440
|
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter)
|
|
441
441
|
|
|
442
|
-
error = assert_raises(
|
|
442
|
+
error = assert_raises(StatsD::Instrument::CompiledMetric::DefinitionError) do
|
|
443
443
|
metric.sample_rate
|
|
444
444
|
end
|
|
445
445
|
assert_equal("Every CompiledMetric subclass needs to call `define` before accessing its sample_rate.", error.message)
|
|
446
446
|
end
|
|
447
|
+
|
|
448
|
+
def test_metric_name
|
|
449
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
450
|
+
define(
|
|
451
|
+
name: "foo.bar",
|
|
452
|
+
)
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
assert_equal("foo.bar", metric.metric_name)
|
|
456
|
+
assert_predicate(metric.metric_name, :frozen?)
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def test_metric_name_is_normalized
|
|
460
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
461
|
+
define(
|
|
462
|
+
name: "foo:bar|baz@qux",
|
|
463
|
+
)
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
assert_equal("foo_bar_baz_qux", metric.metric_name)
|
|
467
|
+
assert_predicate(metric.metric_name, :frozen?)
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def test_metric_name_without_define
|
|
471
|
+
metric = Class.new(StatsD::Instrument::CompiledMetric::Counter)
|
|
472
|
+
|
|
473
|
+
assert_nil(metric.metric_name)
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
def test_define_directly_on_counter_raises
|
|
477
|
+
error = assert_raises(StatsD::Instrument::CompiledMetric::DefinitionError) do
|
|
478
|
+
StatsD::Instrument::CompiledMetric::Counter.define(name: "bad_metric")
|
|
479
|
+
end
|
|
480
|
+
assert_includes(error.message, "`define` must be called on a subclass")
|
|
481
|
+
assert_includes(error.message, "Counter")
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
def test_define_directly_on_gauge_raises
|
|
485
|
+
error = assert_raises(StatsD::Instrument::CompiledMetric::DefinitionError) do
|
|
486
|
+
StatsD::Instrument::CompiledMetric::Gauge.define(name: "bad_metric")
|
|
487
|
+
end
|
|
488
|
+
assert_includes(error.message, "`define` must be called on a subclass")
|
|
489
|
+
assert_includes(error.message, "Gauge")
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def test_define_directly_on_distribution_raises
|
|
493
|
+
error = assert_raises(StatsD::Instrument::CompiledMetric::DefinitionError) do
|
|
494
|
+
StatsD::Instrument::CompiledMetric::Distribution.define(name: "bad_metric")
|
|
495
|
+
end
|
|
496
|
+
assert_includes(error.message, "`define` must be called on a subclass")
|
|
497
|
+
assert_includes(error.message, "Distribution")
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def test_define_directly_on_compiled_metric_raises
|
|
501
|
+
error = assert_raises(StatsD::Instrument::CompiledMetric::DefinitionError) do
|
|
502
|
+
StatsD::Instrument::CompiledMetric.define(name: "bad_metric")
|
|
503
|
+
end
|
|
504
|
+
assert_includes(error.message, "`define` must be called on a subclass")
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
def test_double_define_raises
|
|
508
|
+
error = assert_raises(StatsD::Instrument::CompiledMetric::DefinitionError) do
|
|
509
|
+
Class.new(StatsD::Instrument::CompiledMetric::Counter) do
|
|
510
|
+
define(name: "first_metric")
|
|
511
|
+
define(name: "second_metric")
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
assert_includes(error.message, "`define` has already been called")
|
|
515
|
+
end
|
|
447
516
|
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.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jesse Storimer
|
|
@@ -135,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
135
135
|
- !ruby/object:Gem::Version
|
|
136
136
|
version: '0'
|
|
137
137
|
requirements: []
|
|
138
|
-
rubygems_version: 4.0.
|
|
138
|
+
rubygems_version: 4.0.9
|
|
139
139
|
specification_version: 4
|
|
140
140
|
summary: A StatsD client for Ruby apps
|
|
141
141
|
test_files:
|