statsd-instrument 2.5.1 → 2.6.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +1 -1
  3. data/.rubocop.yml +11 -6
  4. data/.yardopts +5 -0
  5. data/CHANGELOG.md +75 -6
  6. data/README.md +54 -46
  7. data/benchmark/datagram-client +41 -0
  8. data/lib/statsd/instrument/assertions.rb +168 -57
  9. data/lib/statsd/instrument/backends/udp_backend.rb +20 -29
  10. data/lib/statsd/instrument/capture_sink.rb +27 -0
  11. data/lib/statsd/instrument/client.rb +313 -0
  12. data/lib/statsd/instrument/datagram.rb +75 -0
  13. data/lib/statsd/instrument/datagram_builder.rb +101 -0
  14. data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +71 -0
  15. data/lib/statsd/instrument/environment.rb +106 -29
  16. data/lib/statsd/instrument/log_sink.rb +24 -0
  17. data/lib/statsd/instrument/null_sink.rb +13 -0
  18. data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +39 -0
  19. data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +6 -10
  20. data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +37 -0
  21. data/lib/statsd/instrument/rubocop/metric_return_value.rb +7 -6
  22. data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +11 -20
  23. data/lib/statsd/instrument/rubocop/positional_arguments.rb +13 -13
  24. data/lib/statsd/instrument/rubocop/splat_arguments.rb +8 -14
  25. data/lib/statsd/instrument/rubocop.rb +64 -0
  26. data/lib/statsd/instrument/statsd_datagram_builder.rb +14 -0
  27. data/lib/statsd/instrument/strict.rb +112 -22
  28. data/lib/statsd/instrument/udp_sink.rb +62 -0
  29. data/lib/statsd/instrument/version.rb +1 -1
  30. data/lib/statsd/instrument.rb +191 -100
  31. data/test/assertions_test.rb +139 -176
  32. data/test/capture_sink_test.rb +44 -0
  33. data/test/client_test.rb +164 -0
  34. data/test/compatibility/dogstatsd_datagram_compatibility_test.rb +162 -0
  35. data/test/datagram_builder_test.rb +120 -0
  36. data/test/deprecations_test.rb +56 -10
  37. data/test/dogstatsd_datagram_builder_test.rb +32 -0
  38. data/test/environment_test.rb +73 -7
  39. data/test/log_sink_test.rb +37 -0
  40. data/test/null_sink_test.rb +13 -0
  41. data/test/rubocop/measure_as_dist_argument_test.rb +44 -0
  42. data/test/rubocop/metaprogramming_positional_arguments_test.rb +1 -1
  43. data/test/rubocop/metric_prefix_argument_test.rb +38 -0
  44. data/test/rubocop/metric_return_value_test.rb +1 -1
  45. data/test/rubocop/metric_value_keyword_argument_test.rb +1 -1
  46. data/test/rubocop/positional_arguments_test.rb +1 -1
  47. data/test/rubocop/splat_arguments_test.rb +1 -1
  48. data/test/statsd_datagram_builder_test.rb +22 -0
  49. data/test/statsd_instrumentation_test.rb +0 -24
  50. data/test/statsd_test.rb +0 -23
  51. data/test/test_helper.rb +0 -2
  52. data/test/udp_backend_test.rb +25 -8
  53. data/test/udp_sink_test.rb +85 -0
  54. 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, prefix: StatsD.prefix, no_prefix: false)
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?(Numeric)
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, prefix: StatsD.prefix, no_prefix: false)
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, prefix: StatsD.prefix, no_prefix: false)
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, prefix: StatsD.prefix, no_prefix: false)
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 measure(key, value = UNSPECIFIED, sample_rate: nil, tags: nil,
59
- prefix: StatsD.prefix, no_prefix: false, as_dist: false, &block)
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
- super
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, prefix: StatsD.prefix, no_prefix: false)
125
+ def statsd_distribution(method, name, sample_rate: nil, tags: nil, no_prefix: false)
110
126
  check_method_and_metric_name(method, name)
111
- super
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, prefix: StatsD.prefix, no_prefix: false)
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
- super
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, prefix: StatsD.prefix, no_prefix: false)
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
- super
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, prefix: StatsD.prefix, no_prefix: false)
204
+ def statsd_count(method, name, sample_rate: nil, tags: nil, no_prefix: false)
125
205
  check_method_and_metric_name(method, name)
126
- super
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module StatsD
4
4
  module Instrument
5
- VERSION = "2.5.1"
5
+ VERSION = "2.6.0"
6
6
  end
7
7
  end