statsd-instrument 2.3.2 → 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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/workflows/benchmark.yml +32 -0
  4. data/.github/workflows/ci.yml +47 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +1027 -0
  7. data/.rubocop.yml +50 -0
  8. data/.yardopts +5 -0
  9. data/CHANGELOG.md +288 -2
  10. data/CONTRIBUTING.md +28 -6
  11. data/Gemfile +5 -0
  12. data/README.md +54 -46
  13. data/Rakefile +4 -2
  14. data/benchmark/README.md +29 -0
  15. data/benchmark/datagram-client +41 -0
  16. data/benchmark/send-metrics-to-dev-null-log +47 -0
  17. data/benchmark/send-metrics-to-local-udp-receiver +57 -0
  18. data/lib/statsd/instrument/assertions.rb +179 -30
  19. data/lib/statsd/instrument/backend.rb +3 -2
  20. data/lib/statsd/instrument/backends/capture_backend.rb +4 -1
  21. data/lib/statsd/instrument/backends/logger_backend.rb +3 -3
  22. data/lib/statsd/instrument/backends/null_backend.rb +2 -0
  23. data/lib/statsd/instrument/backends/udp_backend.rb +39 -45
  24. data/lib/statsd/instrument/capture_sink.rb +27 -0
  25. data/lib/statsd/instrument/client.rb +313 -0
  26. data/lib/statsd/instrument/datagram.rb +75 -0
  27. data/lib/statsd/instrument/datagram_builder.rb +101 -0
  28. data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +71 -0
  29. data/lib/statsd/instrument/environment.rb +108 -29
  30. data/lib/statsd/instrument/helpers.rb +16 -8
  31. data/lib/statsd/instrument/log_sink.rb +24 -0
  32. data/lib/statsd/instrument/matchers.rb +14 -11
  33. data/lib/statsd/instrument/metric.rb +72 -45
  34. data/lib/statsd/instrument/metric_expectation.rb +32 -18
  35. data/lib/statsd/instrument/null_sink.rb +13 -0
  36. data/lib/statsd/instrument/railtie.rb +2 -1
  37. data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +39 -0
  38. data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +42 -0
  39. data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +37 -0
  40. data/lib/statsd/instrument/rubocop/metric_return_value.rb +32 -0
  41. data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +36 -0
  42. data/lib/statsd/instrument/rubocop/positional_arguments.rb +99 -0
  43. data/lib/statsd/instrument/rubocop/splat_arguments.rb +31 -0
  44. data/lib/statsd/instrument/rubocop.rb +64 -0
  45. data/lib/statsd/instrument/statsd_datagram_builder.rb +14 -0
  46. data/lib/statsd/instrument/strict.rb +235 -0
  47. data/lib/statsd/instrument/udp_sink.rb +62 -0
  48. data/lib/statsd/instrument/version.rb +3 -1
  49. data/lib/statsd/instrument.rb +340 -163
  50. data/lib/statsd-instrument.rb +2 -0
  51. data/statsd-instrument.gemspec +13 -10
  52. data/test/assertions_test.rb +167 -156
  53. data/test/benchmark/clock_gettime.rb +27 -0
  54. data/test/benchmark/default_tags.rb +47 -0
  55. data/test/benchmark/metrics.rb +9 -8
  56. data/test/benchmark/tags.rb +5 -3
  57. data/test/capture_backend_test.rb +4 -2
  58. data/test/capture_sink_test.rb +44 -0
  59. data/test/client_test.rb +164 -0
  60. data/test/compatibility/dogstatsd_datagram_compatibility_test.rb +162 -0
  61. data/test/datagram_builder_test.rb +120 -0
  62. data/test/deprecations_test.rb +132 -0
  63. data/test/dogstatsd_datagram_builder_test.rb +32 -0
  64. data/test/environment_test.rb +75 -8
  65. data/test/helpers/rubocop_helper.rb +47 -0
  66. data/test/helpers_test.rb +2 -1
  67. data/test/integration_test.rb +31 -7
  68. data/test/log_sink_test.rb +37 -0
  69. data/test/logger_backend_test.rb +10 -8
  70. data/test/matchers_test.rb +42 -28
  71. data/test/metric_test.rb +18 -22
  72. data/test/null_sink_test.rb +13 -0
  73. data/test/rubocop/measure_as_dist_argument_test.rb +44 -0
  74. data/test/rubocop/metaprogramming_positional_arguments_test.rb +58 -0
  75. data/test/rubocop/metric_prefix_argument_test.rb +38 -0
  76. data/test/rubocop/metric_return_value_test.rb +78 -0
  77. data/test/rubocop/metric_value_keyword_argument_test.rb +39 -0
  78. data/test/rubocop/positional_arguments_test.rb +110 -0
  79. data/test/rubocop/splat_arguments_test.rb +27 -0
  80. data/test/statsd_datagram_builder_test.rb +22 -0
  81. data/test/statsd_instrumentation_test.rb +109 -100
  82. data/test/statsd_test.rb +113 -79
  83. data/test/test_helper.rb +12 -1
  84. data/test/udp_backend_test.rb +38 -36
  85. data/test/udp_sink_test.rb +85 -0
  86. metadata +85 -5
  87. data/.travis.yml +0 -12
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'socket'
2
4
  require 'logger'
3
5
 
@@ -27,10 +29,13 @@ require 'logger'
27
29
  # @!attribute logger
28
30
  # The logger to use in case of any errors. The logger is also used as default logger
29
31
  # for the LoggerBackend (although this can be overwritten).
30
- #
31
32
  # @see StatsD::Instrument::Backends::LoggerBackend
32
33
  # @return [Logger]
33
34
  #
35
+ # @!attribute default_tags
36
+ # The tags to apply to all metrics.
37
+ # @return [Array<String>, Hash<String, String>, nil] The default tags, or <tt>nil</tt> when no default tags is used
38
+ #
34
39
  # @see StatsD::Instrument <tt>StatsD::Instrument</tt> contains module to instrument
35
40
  # existing methods with StatsD metrics.
36
41
  module StatsD
@@ -56,20 +61,26 @@ module StatsD
56
61
  metric_name.respond_to?(:call) ? metric_name.call(callee, args).gsub('::', '.') : metric_name.gsub('::', '.')
57
62
  end
58
63
 
59
- if Process.respond_to?(:clock_gettime)
60
- # @private
61
- def self.duration
62
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
63
- yield
64
- Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
65
- end
66
- else
67
- # @private
68
- def self.duration
69
- start = Time.now
70
- yield
71
- Time.now - start
72
- end
64
+ # Even though this method is considered private, and is no longer used internally,
65
+ # applications in the wild rely on it. As a result, we cannot remove this method
66
+ # until the next major version.
67
+ #
68
+ # @deprecated Use Process.clock_gettime(Process::CLOCK_MONOTONIC) instead.
69
+ def self.current_timestamp
70
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
71
+ end
72
+
73
+ # Even though this method is considered private, and is no longer used internally,
74
+ # applications in the wild rely on it. As a result, we cannot remove this method
75
+ # until the next major version.
76
+ #
77
+ # @deprecated You can implement similar functionality yourself using
78
+ # `Process.clock_gettime(Process::CLOCK_MONOTONIC)`. Think about what will
79
+ # happen if an exception happens during the block execution though.
80
+ def self.duration
81
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
82
+ yield
83
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
73
84
  end
74
85
 
75
86
  # Adds execution duration instrumentation to a method as a timing.
@@ -79,10 +90,18 @@ module StatsD
79
90
  # callable to dynamically generate a metric name
80
91
  # @param metric_options (see StatsD#measure)
81
92
  # @return [void]
82
- def statsd_measure(method, name, *metric_options)
93
+ def statsd_measure(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil, as_dist: false,
94
+ sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg, prefix: nil, no_prefix: false)
95
+
83
96
  add_to_method(method, name, :measure) do
84
97
  define_method(method) do |*args, &block|
85
- StatsD.measure(StatsD::Instrument.generate_metric_name(name, self, *args), *metric_options) { super(*args, &block) }
98
+ key = StatsD::Instrument.generate_metric_name(name, self, *args)
99
+ prefix ||= StatsD.prefix
100
+ StatsD.measure( # rubocop:disable StatsD/MeasureAsDistArgument, StatsD/MetricPrefixArgument
101
+ key, sample_rate: sample_rate, tags: tags, prefix: prefix, no_prefix: no_prefix, as_dist: as_dist
102
+ ) do
103
+ super(*args, &block)
104
+ end
86
105
  end
87
106
  end
88
107
  end
@@ -95,10 +114,18 @@ module StatsD
95
114
  # @param metric_options (see StatsD#measure)
96
115
  # @return [void]
97
116
  # @note Supported by the datadog implementation only (in beta)
98
- def statsd_distribution(method, name, *metric_options)
117
+ def statsd_distribution(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
118
+ sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg, prefix: nil, no_prefix: false)
119
+
99
120
  add_to_method(method, name, :distribution) do
100
121
  define_method(method) do |*args, &block|
101
- StatsD.distribution(StatsD::Instrument.generate_metric_name(name, self, *args), *metric_options) { super(*args, &block) }
122
+ key = StatsD::Instrument.generate_metric_name(name, self, *args)
123
+ prefix ||= StatsD.prefix
124
+ StatsD.distribution( # rubocop:disable StatsD/MetricPrefixArgument
125
+ key, sample_rate: sample_rate, tags: tags, prefix: prefix, no_prefix: no_prefix
126
+ ) do
127
+ super(*args, &block)
128
+ end
102
129
  end
103
130
  end
104
131
  end
@@ -118,7 +145,9 @@ module StatsD
118
145
  # @yieldreturn [Boolean] Return true iff the return value is consisered a success, false otherwise.
119
146
  # @return [void]
120
147
  # @see #statsd_count_if
121
- def statsd_count_success(method, name, *metric_options)
148
+ def statsd_count_success(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
149
+ sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg, prefix: nil, no_prefix: false)
150
+
122
151
  add_to_method(method, name, :count_success) do
123
152
  define_method(method) do |*args, &block|
124
153
  begin
@@ -127,30 +156,40 @@ module StatsD
127
156
  truthiness = false
128
157
  raise
129
158
  else
130
- truthiness = (yield(result) rescue false) if block_given?
159
+ if block_given?
160
+ begin
161
+ truthiness = yield(result)
162
+ rescue
163
+ truthiness = false
164
+ end
165
+ end
131
166
  result
132
167
  ensure
133
168
  suffix = truthiness == false ? 'failure' : 'success'
134
- StatsD.increment("#{StatsD::Instrument.generate_metric_name(name, self, *args)}.#{suffix}", 1, *metric_options)
169
+ key = "#{StatsD::Instrument.generate_metric_name(name, self, *args)}.#{suffix}"
170
+ prefix ||= StatsD.prefix
171
+ StatsD.increment(key, prefix: prefix, # rubocop:disable StatsD/MetricPrefixArgument
172
+ sample_rate: sample_rate, tags: tags, no_prefix: no_prefix)
135
173
  end
136
174
  end
137
175
  end
138
176
  end
139
177
 
140
- # Adds success and failure counter instrumentation to a method.
178
+ # Adds success counter instrumentation to a method.
141
179
  #
142
180
  # A method call will be considered successful if it does not raise an exception, and the result is true-y.
143
- # Only for successful calls, the metric will be icnremented
181
+ # Only for successful calls, the metric will be incremented.
144
182
  #
145
183
  # @param method (see #statsd_measure)
146
184
  # @param name (see #statsd_measure)
147
- # @param metric_options (see #statsd_measure)
148
185
  # @yield (see #statsd_count_success)
149
186
  # @yieldparam result (see #statsd_count_success)
150
187
  # @yieldreturn (see #statsd_count_success)
151
188
  # @return [void]
152
189
  # @see #statsd_count_success
153
- def statsd_count_if(method, name, *metric_options)
190
+ def statsd_count_if(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
191
+ sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg, prefix: nil, no_prefix: false)
192
+
154
193
  add_to_method(method, name, :count_if) do
155
194
  define_method(method) do |*args, &block|
156
195
  begin
@@ -159,10 +198,21 @@ module StatsD
159
198
  truthiness = false
160
199
  raise
161
200
  else
162
- truthiness = (yield(result) rescue false) if block_given?
201
+ if block_given?
202
+ begin
203
+ truthiness = yield(result)
204
+ rescue
205
+ truthiness = false
206
+ end
207
+ end
163
208
  result
164
209
  ensure
165
- StatsD.increment(StatsD::Instrument.generate_metric_name(name, self, *args), *metric_options) if truthiness
210
+ if truthiness
211
+ key = StatsD::Instrument.generate_metric_name(name, self, *args)
212
+ prefix ||= StatsD.prefix
213
+ StatsD.increment(key, prefix: prefix, # rubocop:disable StatsD/MetricPrefixArgument
214
+ sample_rate: sample_rate, tags: tags, no_prefix: no_prefix)
215
+ end
166
216
  end
167
217
  end
168
218
  end
@@ -177,10 +227,15 @@ module StatsD
177
227
  # @param name (see #statsd_measure)
178
228
  # @param metric_options (see #statsd_measure)
179
229
  # @return [void]
180
- def statsd_count(method, name, *metric_options)
230
+ def statsd_count(method, name, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
231
+ sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg, prefix: nil, no_prefix: false)
232
+
181
233
  add_to_method(method, name, :count) do
182
234
  define_method(method) do |*args, &block|
183
- StatsD.increment(StatsD::Instrument.generate_metric_name(name, self, *args), 1, *metric_options)
235
+ key = StatsD::Instrument.generate_metric_name(name, self, *args)
236
+ prefix ||= StatsD.prefix
237
+ StatsD.increment(key, prefix: prefix, # rubocop:disable StatsD/MetricPrefixArgument
238
+ sample_rate: sample_rate, tags: tags, no_prefix: no_prefix)
184
239
  super(*args, &block)
185
240
  end
186
241
  end
@@ -248,8 +303,13 @@ module StatsD
248
303
  def add_to_method(method, name, action, &block)
249
304
  instrumentation_module = statsd_instrumentation_for(method, name, action)
250
305
 
251
- raise ArgumentError, "already instrumented #{method} for #{self.name}" if instrumentation_module.method_defined?(method)
252
- raise ArgumentError, "could not find method #{method} for #{self.name}" unless method_defined?(method) || private_method_defined?(method)
306
+ if instrumentation_module.method_defined?(method)
307
+ raise ArgumentError, "Already instrumented #{method} for #{self.name}"
308
+ end
309
+
310
+ unless method_defined?(method) || private_method_defined?(method)
311
+ raise ArgumentError, "could not find method #{method} for #{self.name}"
312
+ end
253
313
 
254
314
  method_scope = method_visibility(method)
255
315
 
@@ -263,10 +323,9 @@ module StatsD
263
323
  end
264
324
 
265
325
  def method_visibility(method)
266
- case
267
- when private_method_defined?(method)
326
+ if private_method_defined?(method)
268
327
  :private
269
- when protected_method_defined?(method)
328
+ elsif protected_method_defined?(method)
270
329
  :protected
271
330
  else
272
331
  :public
@@ -275,49 +334,75 @@ module StatsD
275
334
  end
276
335
 
277
336
  attr_accessor :logger, :default_sample_rate, :prefix
278
- attr_writer :backend
337
+ attr_writer :backend, :client
338
+ attr_reader :default_tags
339
+
340
+ def default_tags=(tags)
341
+ @default_tags = StatsD::Instrument::Metric.normalize_tags(tags)
342
+ end
279
343
 
280
344
  def backend
281
345
  @backend ||= StatsD::Instrument::Environment.default_backend
282
346
  end
283
347
 
284
- # Emits a duration metric.
348
+ def client
349
+ @client ||= begin
350
+ require 'statsd/instrument/client'
351
+ StatsD::Instrument::Environment.from_env.default_client
352
+ end
353
+ end
354
+
355
+ # @!method measure(name, value = nil, sample_rate: nil, tags: nil, &block)
356
+ #
357
+ # Emits a timing metric
358
+ #
359
+ # @param [String] key The name of the metric.
360
+ # @param sample_rate (see #increment)
361
+ # @param tags (see #increment)
285
362
  #
286
- # @overload measure(key, value, metric_options = {})
287
- # Emits a measure metric, by providing a duration in milliseconds.
288
- # @param key [String] The name of the metric.
289
- # @param value [Float] The measured duration in milliseconds
290
- # @param metric_options [Hash] Options for the metric
291
- # the key :as_dist will submit the value as a distribution instead of a timing
292
- # (only supported by DataDog's implementation)
293
- # @return [StatsD::Instrument::Metric] The metric that was sent to the backend.
363
+ # @example Providing a value directly
364
+ # start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
365
+ # do_something
366
+ # stop = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
367
+ # http_response = StatsD.measure('HTTP.call.duration', stop - start)
294
368
  #
295
- # @overload measure(key, metric_options = {}, &block)
296
- # Emits a measure metric, after measuring the execution duration of the
369
+ # @example Providing a block to measure the duration of its execution
370
+ # http_response = StatsD.measure('HTTP.call.duration') do
371
+ # Net::HTTP.get(url)
372
+ # end
373
+ #
374
+ # @overload measure(key, value, sample_rate: nil, tags: nil)
375
+ # Emits a timing metric, by providing a duration in milliseconds.
376
+ #
377
+ # @param [Float] value The measured duration in milliseconds
378
+ # @return [void]
379
+ #
380
+ # @overload measure(key, sample_rate: nil, tags: nil, &block)
381
+ # Emits a timing metric, after measuring the execution duration of the
297
382
  # block passed to this method.
298
- # @param key [String] The name of the metric.
299
- # @param metric_options [Hash] Options for the metric
300
- # the key :as_dist sets the metric type to a 'distribution' instead of a 'timing'
301
- # (only supported by DataDog's implementation)
302
- # @yield The method will yield the block that was passed to this method to measure its duration.
303
- # @return The value that was returns by the block passed to this method.
304
- #
305
- # @example
306
- # http_response = StatsD.measure('HTTP.call.duration') do
307
- # HTTP.get(url)
308
- # end
309
- def measure(key, value = nil, *metric_options, &block)
310
- value, metric_options = parse_options(value, metric_options)
311
- type = (!metric_options.empty? && metric_options.first[:as_dist] ? :d : :ms)
312
-
313
- result = nil
314
- value = 1000 * StatsD::Instrument.duration { result = block.call } if block_given?
315
- metric = collect_metric(type, key, value, metric_options)
316
- result = metric unless block_given?
317
- result
383
+ #
384
+ # @yield `StatsD.measure` will yield the block and measure the duration. After the block
385
+ # returns, the duration in millisecond will be emitted as metric.
386
+ # @return The value that was returned by the block passed through.
387
+ def measure(
388
+ key, value_arg = nil, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
389
+ value: value_arg, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
390
+ prefix: StatsD.prefix, no_prefix: false, as_dist: false,
391
+ &block
392
+ )
393
+ # TODO: in the next version, hardcode this to :ms when the as_dist argument is dropped.
394
+ type = as_dist ? :d : :ms
395
+ prefix = nil if no_prefix
396
+ if block_given?
397
+ measure_latency(type, key, sample_rate: sample_rate, tags: tags, prefix: prefix, &block)
398
+ else
399
+ collect_metric(type, key, value, sample_rate: sample_rate, tags: tags, prefix: prefix)
400
+ end
318
401
  end
319
402
 
403
+ # @!method increment(name, value = 1, sample_rate: nil, tags: nil)
320
404
  # Emits a counter metric.
405
+ #
321
406
  # @param key [String] The name of the metric.
322
407
  # @param value [Integer] The value to increment the counter by.
323
408
  #
@@ -326,135 +411,226 @@ module StatsD
326
411
  # The sample rate is part of the packet that is being sent to the server, and the server
327
412
  # should know how to handle it.
328
413
  #
329
- # @param metric_options [Hash] (default: {}) Metric options
330
- # @return (see #collect_metric)
331
- def increment(key, value = 1, *metric_options)
332
- collect_metric(:c, key, value, metric_options)
414
+ # @param sample_rate [Float] (default: `StatsD.default_sample_rate`) The rate at which to sample
415
+ # this metric call. This value should be between 0 and 1. This value can be used to reduce
416
+ # the amount of network I/O (and CPU cycles) used for very frequent metrics.
417
+ #
418
+ # - A value of `0.1` means that only 1 out of 10 calls will be emitted; the other 9 will
419
+ # be short-circuited.
420
+ # - When set to `1`, every metric will be emitted.
421
+ # - If this parameter is not set, the default sample rate for this client will be used.
422
+ # @param tags [Array<String>, Hash<Symbol, String>] The tags to associate with this measurement.
423
+ # They can be provided as an array of strings, or a hash of key/value pairs.
424
+ # _Note:_ Tags are not supported by all implementations.
425
+ # @return [void]
426
+ def increment(
427
+ key, value_arg = 1, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
428
+ value: value_arg, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
429
+ prefix: StatsD.prefix, no_prefix: false
430
+ )
431
+ prefix = nil if no_prefix
432
+ collect_metric(:c, key, value, sample_rate: sample_rate, tags: tags, prefix: prefix)
333
433
  end
334
434
 
435
+ # @!method gauge(name, value, sample_rate: nil, tags: nil)
436
+ #
335
437
  # Emits a gauge metric.
336
- # @param key [String] The name of the metric.
438
+ #
439
+ # @param key The name of the metric.
337
440
  # @param value [Numeric] The current value to record.
338
- # @param metric_options [Hash] (default: {}) Metric options
339
- # @return (see #collect_metric)
340
- def gauge(key, value, *metric_options)
341
- collect_metric(:g, key, value, metric_options)
441
+ # @param sample_rate (see #increment)
442
+ # @param tags (see #increment)
443
+ # @return [void]
444
+ def gauge(
445
+ key, value_arg = nil, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
446
+ value: value_arg, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
447
+ prefix: StatsD.prefix, no_prefix: false
448
+ )
449
+ prefix = nil if no_prefix
450
+ collect_metric(:g, key, value, sample_rate: sample_rate, tags: tags, prefix: prefix)
342
451
  end
343
452
 
344
- # Emits a histogram metric.
453
+ # @!method set(name, value, sample_rate: nil, tags: nil)
454
+ #
455
+ # Emits a set metric, which counts the number of distinct values that have occurred.
456
+ #
457
+ # @example Couning the number of unique visitors
458
+ # StatsD.set('visitors.unique', Current.user.id)
459
+ #
345
460
  # @param key [String] The name of the metric.
346
461
  # @param value [Numeric] The value to record.
347
- # @param metric_options [Hash] (default: {}) Metric options
462
+ # @param sample_rate (see #increment)
463
+ # @param tags (see #increment)
464
+ # @return [void]
465
+ def set(
466
+ key, value_arg = nil, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
467
+ value: value_arg, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
468
+ prefix: StatsD.prefix, no_prefix: false
469
+ )
470
+ prefix = nil if no_prefix
471
+ collect_metric(:s, key, value, sample_rate: sample_rate, tags: tags, prefix: prefix)
472
+ end
473
+
474
+ # @!method histogram(name, value, sample_rate: nil, tags: nil)
475
+ #
476
+ # Emits a histogram metric.
477
+ #
478
+ # @param key The name of the metric.
479
+ # @param value [Numeric] The value to record.
480
+ # @param sample_rate (see #increment)
481
+ # @param tags (see #increment)
348
482
  # @return (see #collect_metric)
349
483
  # @note Supported by the datadog implementation only.
350
- def histogram(key, value, *metric_options)
351
- collect_metric(:h, key, value, metric_options)
484
+ def histogram(
485
+ key, value_arg = nil, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
486
+ value: value_arg, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
487
+ prefix: StatsD.prefix, no_prefix: false
488
+ )
489
+ prefix = nil if no_prefix
490
+ collect_metric(:h, key, value, sample_rate: sample_rate, tags: tags, prefix: prefix)
352
491
  end
353
492
 
493
+ # @!method distribution(name, value = nil, sample_rate: nil, tags: nil, &block)
494
+ #
354
495
  # Emits a distribution metric.
355
- # @param key [String] The name of the metric.
356
- # @param value [Numeric] The value to record.
357
- # @param metric_options [Hash] (default: {}) Metric options
358
- # @return (see #collect_metric)
359
- # @note Supported by the datadog implementation only (in beta)
496
+ #
497
+ # @param [String] key The name of the metric.
498
+ # @param sample_rate (see #increment)
499
+ # @param tags (see #increment)
500
+ #
501
+ # @note Supported by the datadog implementation only.
502
+ # @example
503
+ # http_response = StatsD.distribution('HTTP.call.duration') do
504
+ # Net::HTTP.get(url)
505
+ # end
506
+ #
507
+ # @overload distribution(name, value, sample_rate: nil, tags: nil)
508
+ #
509
+ # Emits a distribution metric, given a provided value to record.
510
+ #
511
+ # @param [Numeric] value The value to record.
512
+ # @return [void]
360
513
  #
361
514
  # @overload distribution(key, metric_options = {}, &block)
362
- # Emits a distribution metric, after measuring the execution duration of the
363
- # block passed to this method.
364
- # @param key [String] The name of the metric.
365
- # @param metric_options [Hash] Options for the metric
366
- # @yield The method will yield the block that was passed to this method to measure its duration.
367
- # @return The value that was returns by the block passed to this method.
368
- # @note Supported by the datadog implementation only.
369
- #
370
- # @example
371
- # http_response = StatsD.distribution('HTTP.call.duration') do
372
- # HTTP.get(url)
373
- # end
374
- def distribution(key, value=nil, *metric_options, &block)
375
- value, metric_options = parse_options(value, metric_options)
376
- result = nil
377
- value = 1000 * StatsD::Instrument.duration { result = block.call } if block_given?
378
- metric = collect_metric(:d, key, value, metric_options)
379
- result = metric unless block_given?
380
- result
515
+ #
516
+ # Emits a distribution metric for the duration of the provided block, in milliseconds.
517
+ #
518
+ # @yield `StatsD.distribution` will yield the block and measure the duration. After
519
+ # the block returns, the duration in millisecond will be emitted as metric.
520
+ # @return The value that was returned by the block passed through.
521
+ def distribution(
522
+ key, value_arg = nil, deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
523
+ value: value_arg, sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
524
+ prefix: StatsD.prefix, no_prefix: false,
525
+ &block
526
+ )
527
+ prefix = nil if no_prefix
528
+ if block_given?
529
+ measure_latency(:d, key, sample_rate: sample_rate, tags: tags, prefix: prefix, &block)
530
+ else
531
+ collect_metric(:d, key, value, sample_rate: sample_rate, tags: tags, prefix: prefix)
532
+ end
381
533
  end
382
534
 
535
+ # @!method key_value(name, value)
536
+ #
383
537
  # Emits a key/value metric.
538
+ #
384
539
  # @param key [String] The name of the metric.
385
540
  # @param value [Numeric] The value to record.
386
- # @param metric_options [Hash] (default: {}) Metric options
387
- # @return (see #collect_metric)
541
+ # @return [void]
542
+ #
388
543
  # @note Supported by the statsite implementation only.
389
- def key_value(key, value, *metric_options)
390
- collect_metric(:kv, key, value, metric_options)
391
- end
392
-
393
- # Emits a set metric.
394
- # @param key [String] The name of the metric.
395
- # @param value [Numeric] The value to record.
396
- # @param metric_options [Hash] (default: {}) Metric options
397
- # @return (see #collect_metric)
398
- # @note Supported by the datadog implementation only.
399
- def set(key, value, *metric_options)
400
- collect_metric(:s, key, value, metric_options)
544
+ def key_value(
545
+ key, value_arg = nil, deprecated_sample_rate_arg = nil,
546
+ value: value_arg, sample_rate: deprecated_sample_rate_arg, no_prefix: false
547
+ )
548
+ prefix = nil if no_prefix
549
+ collect_metric(:kv, key, value, sample_rate: sample_rate, prefix: prefix)
401
550
  end
402
551
 
403
- # Emits an event metric.
404
- # @param title [String] Title of the event.
405
- # @param text [String] Body of the event.
406
- # @param metric_options [Hash] (default: {}) Metric options
407
- # @return (see #collect_metric)
408
- # @note Supported by the datadog implementation only.
409
- def event(title, text, *metric_options)
410
- collect_metric(:_e, title, text, metric_options)
552
+ # @!method event(title, text, tags: nil, hostname: nil, timestamp: nil, aggregation_key: nil, priority: nil, source_type_name: nil, alert_type: nil) # rubocop:disable Metrics/LineLength
553
+ #
554
+ # Emits an event.
555
+ #
556
+ # @param title [String] Title of the event. A configured prefix may be applied to this title.
557
+ # @param text [String] Body of the event. Can contain newlines.
558
+ # @param [String] hostname The hostname to associate with the event.
559
+ # @param [Time] timestamp The moment the status of the service was checkes. Defaults to now.
560
+ # @param [String] aggregation_key A key to aggregate similar events into groups.
561
+ # @param [String] priority The event's priority, either `"low"` or `"normal"` (default).
562
+ # @param [String] source_type_name The source type.
563
+ # @param [String] alert_type The type of alert. Either `"info"` (default), `"warning"`, `"error"`, or `"success"`.
564
+ # @param tags (see #increment)
565
+ # @return [void]
566
+ #
567
+ # @note Supported by the Datadog implementation only.
568
+ def event(
569
+ title, text,
570
+ deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
571
+ sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
572
+ prefix: StatsD.prefix, no_prefix: false,
573
+ hostname: nil, date_happened: nil, timestamp: date_happened,
574
+ aggregation_key: nil, priority: nil, source_type_name: nil, alert_type: nil,
575
+ **_ignored
576
+ )
577
+ prefix = nil if no_prefix
578
+ collect_metric(:_e, title, text, sample_rate: sample_rate, tags: tags, prefix: prefix, metadata: {
579
+ hostname: hostname, timestamp: timestamp, aggregation_key: aggregation_key,
580
+ priority: priority, source_type_name: source_type_name, alert_type: alert_type
581
+ })
411
582
  end
412
583
 
413
- # Emits a service check metric.
414
- # @param title [String] Title of the event.
415
- # @param text [String] Body of the event.
416
- # @param metric_options [Hash] (default: {}) Metric options
417
- # @return (see #collect_metric)
418
- # @note Supported by the datadog implementation only.
419
- def service_check(name, status, *metric_options)
420
- collect_metric(:_sc, name, status, metric_options)
584
+ # @!method service_check(name, status, tags: nil, hostname: nil, timestamp: nil, message: nil)
585
+ #
586
+ # Emits a service check.
587
+ #
588
+ # @param [String] name Name of the service. A configured prefix may be applied to this title.
589
+ # @param [Symbol] status Current status of the service. Either `:ok`, `:warning`, `:critical`, or `:unknown`.
590
+ # @param [String] hostname The hostname to associate with the event.
591
+ # @param [Time] timestamp The moment the status of the service was checkes. Defaults to now.
592
+ # @param [String] message A message that describes the current status.
593
+ # @param tags (see #increment)
594
+ # @return [void]
595
+ #
596
+ # @note Supported by the Datadog implementation only.
597
+ def service_check(
598
+ name, status,
599
+ deprecated_sample_rate_arg = nil, deprecated_tags_arg = nil,
600
+ sample_rate: deprecated_sample_rate_arg, tags: deprecated_tags_arg,
601
+ prefix: StatsD.prefix, no_prefix: false,
602
+ hostname: nil, timestamp: nil, message: nil, **_ignored
603
+ )
604
+ prefix = nil if no_prefix
605
+ collect_metric(:_sc, name, status, sample_rate: sample_rate, prefix: prefix, tags: tags, metadata: {
606
+ hostname: hostname, timestamp: timestamp, message: message
607
+ })
421
608
  end
422
609
 
423
610
  private
424
611
 
425
- # Converts old-style ordered arguments in an argument hash for backwards compatibility.
426
- # @param args [Array] The list of non-required arguments.
427
- # @return [Hash] The hash of optional arguments.
428
- def hash_argument(args)
429
- return {} if args.length == 0
430
- return args.first if args.length == 1 && args.first.is_a?(Hash)
431
-
432
- order = [:sample_rate, :tags]
433
- hash = {}
434
- args.each_with_index do |value, index|
435
- hash[order[index]] = value
612
+ def measure_latency(type, key, sample_rate:, tags:, prefix:)
613
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
614
+ begin
615
+ yield
616
+ ensure
617
+ # Ensure catches both a raised exception and a return in the invoked block
618
+ value = 1000.0 * (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
619
+ collect_metric(type, key, value, sample_rate: sample_rate, tags: tags, prefix: prefix)
436
620
  end
437
-
438
- return hash
439
- end
440
-
441
- def parse_options(value, metric_options)
442
- if value.is_a?(Hash) && metric_options.empty?
443
- metric_options = [value]
444
- value = value.fetch(:value, nil)
445
- end
446
- [value, metric_options]
447
621
  end
448
622
 
449
623
  # Instantiates a metric, and sends it to the backend for further processing.
450
624
  # @param options (see StatsD::Instrument::Metric#initialize)
451
- # @return [StatsD::Instrument::Metric] The metric that was sent to the backend.
452
- def collect_metric(type, name, value, metric_options)
453
- value, metric_options = parse_options(value, metric_options)
454
-
455
- options = hash_argument(metric_options).merge(type: type, name: name, value: value)
456
- backend.collect_metric(metric = StatsD::Instrument::Metric.new(options))
457
- metric
625
+ # @return [void]
626
+ def collect_metric(type, name, value, sample_rate:, tags: nil, prefix:, metadata: nil)
627
+ sample_rate ||= default_sample_rate
628
+ name = "#{prefix}.#{name}" if prefix
629
+
630
+ metric = StatsD::Instrument::Metric.new(type: type, name: name, value: value,
631
+ sample_rate: sample_rate, tags: tags, metadata: metadata)
632
+ backend.collect_metric(metric)
633
+ metric # TODO: return `nil` in the next major version
458
634
  end
459
635
  end
460
636
 
@@ -466,4 +642,5 @@ require 'statsd/instrument/helpers'
466
642
  require 'statsd/instrument/assertions'
467
643
  require 'statsd/instrument/metric_expectation'
468
644
  require 'statsd/instrument/matchers' if defined?(::RSpec)
469
- require 'statsd/instrument/railtie' if defined?(Rails)
645
+ require 'statsd/instrument/railtie' if defined?(::Rails::Railtie)
646
+ require 'statsd/instrument/strict' if ENV['STATSD_STRICT_MODE']