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.
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