statsd-instrument 2.5.1 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +1 -1
- data/.rubocop.yml +11 -6
- data/.yardopts +5 -0
- data/CHANGELOG.md +75 -6
- data/README.md +54 -46
- data/benchmark/datagram-client +41 -0
- data/lib/statsd/instrument/assertions.rb +168 -57
- data/lib/statsd/instrument/backends/udp_backend.rb +20 -29
- data/lib/statsd/instrument/capture_sink.rb +27 -0
- data/lib/statsd/instrument/client.rb +313 -0
- data/lib/statsd/instrument/datagram.rb +75 -0
- data/lib/statsd/instrument/datagram_builder.rb +101 -0
- data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +71 -0
- data/lib/statsd/instrument/environment.rb +106 -29
- data/lib/statsd/instrument/log_sink.rb +24 -0
- data/lib/statsd/instrument/null_sink.rb +13 -0
- data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +39 -0
- data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +6 -10
- data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +37 -0
- data/lib/statsd/instrument/rubocop/metric_return_value.rb +7 -6
- data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +11 -20
- data/lib/statsd/instrument/rubocop/positional_arguments.rb +13 -13
- data/lib/statsd/instrument/rubocop/splat_arguments.rb +8 -14
- data/lib/statsd/instrument/rubocop.rb +64 -0
- data/lib/statsd/instrument/statsd_datagram_builder.rb +14 -0
- data/lib/statsd/instrument/strict.rb +112 -22
- data/lib/statsd/instrument/udp_sink.rb +62 -0
- data/lib/statsd/instrument/version.rb +1 -1
- data/lib/statsd/instrument.rb +191 -100
- data/test/assertions_test.rb +139 -176
- data/test/capture_sink_test.rb +44 -0
- data/test/client_test.rb +164 -0
- data/test/compatibility/dogstatsd_datagram_compatibility_test.rb +162 -0
- data/test/datagram_builder_test.rb +120 -0
- data/test/deprecations_test.rb +56 -10
- data/test/dogstatsd_datagram_builder_test.rb +32 -0
- data/test/environment_test.rb +73 -7
- data/test/log_sink_test.rb +37 -0
- data/test/null_sink_test.rb +13 -0
- data/test/rubocop/measure_as_dist_argument_test.rb +44 -0
- data/test/rubocop/metaprogramming_positional_arguments_test.rb +1 -1
- data/test/rubocop/metric_prefix_argument_test.rb +38 -0
- data/test/rubocop/metric_return_value_test.rb +1 -1
- data/test/rubocop/metric_value_keyword_argument_test.rb +1 -1
- data/test/rubocop/positional_arguments_test.rb +1 -1
- data/test/rubocop/splat_arguments_test.rb +1 -1
- data/test/statsd_datagram_builder_test.rb +22 -0
- data/test/statsd_instrumentation_test.rb +0 -24
- data/test/statsd_test.rb +0 -23
- data/test/test_helper.rb +0 -2
- data/test/udp_backend_test.rb +25 -8
- data/test/udp_sink_test.rb +85 -0
- metadata +38 -2
@@ -24,15 +24,15 @@ module StatsD
|
|
24
24
|
#
|
25
25
|
# This monkeypatch is not meant to be used in production.
|
26
26
|
module Strict
|
27
|
-
def increment(key, value = 1, sample_rate: nil, tags: nil,
|
27
|
+
def increment(key, value = 1, sample_rate: nil, tags: nil, no_prefix: false)
|
28
28
|
raise ArgumentError, "StatsD.increment does not accept a block" if block_given?
|
29
|
-
raise ArgumentError, "The value argument should be an integer, got #{value.inspect}" unless value.is_a?(
|
29
|
+
raise ArgumentError, "The value argument should be an integer, got #{value.inspect}" unless value.is_a?(Integer)
|
30
30
|
check_tags_and_sample_rate(sample_rate, tags)
|
31
31
|
|
32
32
|
super
|
33
33
|
end
|
34
34
|
|
35
|
-
def gauge(key, value, sample_rate: nil, tags: nil,
|
35
|
+
def gauge(key, value, sample_rate: nil, tags: nil, no_prefix: false)
|
36
36
|
raise ArgumentError, "StatsD.increment does not accept a block" if block_given?
|
37
37
|
raise ArgumentError, "The value argument should be an integer, got #{value.inspect}" unless value.is_a?(Numeric)
|
38
38
|
check_tags_and_sample_rate(sample_rate, tags)
|
@@ -40,7 +40,7 @@ module StatsD
|
|
40
40
|
super
|
41
41
|
end
|
42
42
|
|
43
|
-
def histogram(key, value, sample_rate: nil, tags: nil,
|
43
|
+
def histogram(key, value, sample_rate: nil, tags: nil, no_prefix: false)
|
44
44
|
raise ArgumentError, "StatsD.increment does not accept a block" if block_given?
|
45
45
|
raise ArgumentError, "The value argument should be an integer, got #{value.inspect}" unless value.is_a?(Numeric)
|
46
46
|
check_tags_and_sample_rate(sample_rate, tags)
|
@@ -48,25 +48,33 @@ module StatsD
|
|
48
48
|
super
|
49
49
|
end
|
50
50
|
|
51
|
-
def set(key, value, sample_rate: nil, tags: nil,
|
51
|
+
def set(key, value, sample_rate: nil, tags: nil, no_prefix: false)
|
52
52
|
raise ArgumentError, "StatsD.set does not accept a block" if block_given?
|
53
53
|
check_tags_and_sample_rate(sample_rate, tags)
|
54
54
|
|
55
55
|
super
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
59
|
-
|
58
|
+
def service_check(name, status, tags: nil, no_prefix: false,
|
59
|
+
hostname: nil, timestamp: nil, message: nil)
|
60
|
+
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
def event(title, text, tags: nil, no_prefix: false,
|
65
|
+
hostname: nil, timestamp: nil, aggregation_key: nil, priority: nil, source_type_name: nil, alert_type: nil)
|
66
|
+
|
67
|
+
super
|
68
|
+
end
|
60
69
|
|
70
|
+
def measure(key, value = UNSPECIFIED, sample_rate: nil, tags: nil, no_prefix: false, &block)
|
61
71
|
check_block_or_numeric_value(value, &block)
|
62
72
|
check_tags_and_sample_rate(sample_rate, tags)
|
63
73
|
|
64
74
|
super
|
65
75
|
end
|
66
76
|
|
67
|
-
def distribution(key, value = UNSPECIFIED, sample_rate: nil, tags: nil,
|
68
|
-
prefix: StatsD.prefix, no_prefix: false, &block)
|
69
|
-
|
77
|
+
def distribution(key, value = UNSPECIFIED, sample_rate: nil, tags: nil, no_prefix: false, &block)
|
70
78
|
check_block_or_numeric_value(value, &block)
|
71
79
|
check_tags_and_sample_rate(sample_rate, tags)
|
72
80
|
|
@@ -99,31 +107,113 @@ module StatsD
|
|
99
107
|
end
|
100
108
|
|
101
109
|
module StrictMetaprogramming
|
102
|
-
def statsd_measure(method, name, sample_rate: nil, tags: nil,
|
103
|
-
prefix: StatsD.prefix, no_prefix: false, as_dist: false)
|
104
|
-
|
110
|
+
def statsd_measure(method, name, sample_rate: nil, tags: nil, no_prefix: false)
|
105
111
|
check_method_and_metric_name(method, name)
|
106
|
-
|
112
|
+
|
113
|
+
# Unfortunately, we have to inline the new method implementation ebcause we have to fix the
|
114
|
+
# Stats.measure call to not use the `as_dist` and `prefix` arguments.
|
115
|
+
add_to_method(method, name, :measure) do
|
116
|
+
define_method(method) do |*args, &block|
|
117
|
+
key = StatsD::Instrument.generate_metric_name(name, self, *args)
|
118
|
+
StatsD.measure(key, sample_rate: sample_rate, tags: tags, no_prefix: no_prefix) do
|
119
|
+
super(*args, &block)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
107
123
|
end
|
108
124
|
|
109
|
-
def statsd_distribution(method, name, sample_rate: nil, tags: nil,
|
125
|
+
def statsd_distribution(method, name, sample_rate: nil, tags: nil, no_prefix: false)
|
110
126
|
check_method_and_metric_name(method, name)
|
111
|
-
|
127
|
+
|
128
|
+
# Unfortunately, we have to inline the new method implementation ebcause we have to fix the
|
129
|
+
# Stats.distribution call to not use the `prefix` argument.
|
130
|
+
|
131
|
+
add_to_method(method, name, :distribution) do
|
132
|
+
define_method(method) do |*args, &block|
|
133
|
+
key = StatsD::Instrument.generate_metric_name(name, self, *args)
|
134
|
+
StatsD.distribution(key, sample_rate: sample_rate, tags: tags, no_prefix: no_prefix) do
|
135
|
+
super(*args, &block)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
112
139
|
end
|
113
140
|
|
114
|
-
def statsd_count_success(method, name, sample_rate: nil, tags: nil,
|
141
|
+
def statsd_count_success(method, name, sample_rate: nil, tags: nil, no_prefix: false)
|
115
142
|
check_method_and_metric_name(method, name)
|
116
|
-
|
143
|
+
|
144
|
+
# Unfortunately, we have to inline the new method implementation ebcause we have to fix the
|
145
|
+
# Stats.increment call to not use the `prefix` argument.
|
146
|
+
|
147
|
+
add_to_method(method, name, :count_success) do
|
148
|
+
define_method(method) do |*args, &block|
|
149
|
+
begin
|
150
|
+
truthiness = result = super(*args, &block)
|
151
|
+
rescue
|
152
|
+
truthiness = false
|
153
|
+
raise
|
154
|
+
else
|
155
|
+
if block_given?
|
156
|
+
begin
|
157
|
+
truthiness = yield(result)
|
158
|
+
rescue
|
159
|
+
truthiness = false
|
160
|
+
end
|
161
|
+
end
|
162
|
+
result
|
163
|
+
ensure
|
164
|
+
suffix = truthiness == false ? 'failure' : 'success'
|
165
|
+
key = "#{StatsD::Instrument.generate_metric_name(name, self, *args)}.#{suffix}"
|
166
|
+
StatsD.increment(key, sample_rate: sample_rate, tags: tags, no_prefix: no_prefix)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
117
170
|
end
|
118
171
|
|
119
|
-
def statsd_count_if(method, name, sample_rate: nil, tags: nil,
|
172
|
+
def statsd_count_if(method, name, sample_rate: nil, tags: nil, no_prefix: false)
|
120
173
|
check_method_and_metric_name(method, name)
|
121
|
-
|
174
|
+
|
175
|
+
# Unfortunately, we have to inline the new method implementation ebcause we have to fix the
|
176
|
+
# Stats.increment call to not use the `prefix` argument.
|
177
|
+
|
178
|
+
add_to_method(method, name, :count_if) do
|
179
|
+
define_method(method) do |*args, &block|
|
180
|
+
begin
|
181
|
+
truthiness = result = super(*args, &block)
|
182
|
+
rescue
|
183
|
+
truthiness = false
|
184
|
+
raise
|
185
|
+
else
|
186
|
+
if block_given?
|
187
|
+
begin
|
188
|
+
truthiness = yield(result)
|
189
|
+
rescue
|
190
|
+
truthiness = false
|
191
|
+
end
|
192
|
+
end
|
193
|
+
result
|
194
|
+
ensure
|
195
|
+
if truthiness
|
196
|
+
key = StatsD::Instrument.generate_metric_name(name, self, *args)
|
197
|
+
StatsD.increment(key, sample_rate: sample_rate, tags: tags, no_prefix: no_prefix)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
122
202
|
end
|
123
203
|
|
124
|
-
def statsd_count(method, name, sample_rate: nil, tags: nil,
|
204
|
+
def statsd_count(method, name, sample_rate: nil, tags: nil, no_prefix: false)
|
125
205
|
check_method_and_metric_name(method, name)
|
126
|
-
|
206
|
+
|
207
|
+
# Unfortunately, we have to inline the new method implementation ebcause we have to fix the
|
208
|
+
# Stats.increment call to not use the `prefix` argument.
|
209
|
+
|
210
|
+
add_to_method(method, name, :count) do
|
211
|
+
define_method(method) do |*args, &block|
|
212
|
+
key = StatsD::Instrument.generate_metric_name(name, self, *args)
|
213
|
+
StatsD.increment(key, sample_rate: sample_rate, tags: tags, no_prefix: no_prefix)
|
214
|
+
super(*args, &block)
|
215
|
+
end
|
216
|
+
end
|
127
217
|
end
|
128
218
|
|
129
219
|
private
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @note This class is part of the new Client implementation that is intended
|
4
|
+
# to become the new default in the next major release of this library.
|
5
|
+
class StatsD::Instrument::UDPSink
|
6
|
+
def self.for_addr(addr)
|
7
|
+
host, port_as_string = addr.split(':', 2)
|
8
|
+
new(host, Integer(port_as_string))
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :host, :port
|
12
|
+
|
13
|
+
def initialize(host, port)
|
14
|
+
@host = host
|
15
|
+
@port = port
|
16
|
+
@mutex = Mutex.new
|
17
|
+
@socket = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def sample?(sample_rate)
|
21
|
+
sample_rate == 1 || rand < sample_rate
|
22
|
+
end
|
23
|
+
|
24
|
+
def <<(datagram)
|
25
|
+
with_socket { |socket| socket.send(datagram, 0) > 0 }
|
26
|
+
self
|
27
|
+
|
28
|
+
rescue ThreadError
|
29
|
+
# In cases where a TERM or KILL signal has been sent, and we send stats as
|
30
|
+
# part of a signal handler, locks cannot be acquired, so we do our best
|
31
|
+
# to try and send the datagram without a lock.
|
32
|
+
socket.send(datagram, 0) > 0
|
33
|
+
|
34
|
+
rescue SocketError, IOError, SystemCallError
|
35
|
+
# TODO: log?
|
36
|
+
invalidate_socket
|
37
|
+
end
|
38
|
+
|
39
|
+
def addr
|
40
|
+
"#{host}:#{port}"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def with_socket
|
46
|
+
@mutex.synchronize { yield(socket) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def socket
|
50
|
+
if @socket.nil?
|
51
|
+
@socket = UDPSocket.new
|
52
|
+
@socket.connect(@host, @port)
|
53
|
+
end
|
54
|
+
@socket
|
55
|
+
end
|
56
|
+
|
57
|
+
def invalidate_socket
|
58
|
+
@mutex.synchronize do
|
59
|
+
@socket = nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|