statsd-instrument 3.0.0.pre1 → 3.1.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint.yml +22 -0
  3. data/.github/workflows/tests.yml +31 -0
  4. data/.rubocop.yml +3 -13
  5. data/CHANGELOG.md +50 -0
  6. data/Gemfile +8 -2
  7. data/README.md +6 -3
  8. data/Rakefile +7 -7
  9. data/benchmark/send-metrics-to-dev-null-log +12 -11
  10. data/benchmark/send-metrics-to-local-udp-receiver +16 -15
  11. data/bin/rake +29 -0
  12. data/bin/rubocop +29 -0
  13. data/lib/statsd-instrument.rb +1 -1
  14. data/lib/statsd/instrument.rb +112 -145
  15. data/lib/statsd/instrument/assertions.rb +200 -208
  16. data/lib/statsd/instrument/batched_udp_sink.rb +154 -0
  17. data/lib/statsd/instrument/capture_sink.rb +23 -19
  18. data/lib/statsd/instrument/client.rb +410 -306
  19. data/lib/statsd/instrument/datagram.rb +69 -65
  20. data/lib/statsd/instrument/datagram_builder.rb +81 -77
  21. data/lib/statsd/instrument/dogstatsd_datagram.rb +76 -72
  22. data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +68 -64
  23. data/lib/statsd/instrument/environment.rb +88 -77
  24. data/lib/statsd/instrument/expectation.rb +96 -96
  25. data/lib/statsd/instrument/helpers.rb +11 -7
  26. data/lib/statsd/instrument/log_sink.rb +20 -16
  27. data/lib/statsd/instrument/matchers.rb +93 -74
  28. data/lib/statsd/instrument/null_sink.rb +12 -8
  29. data/lib/statsd/instrument/railtie.rb +11 -7
  30. data/lib/statsd/instrument/rubocop.rb +8 -8
  31. data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +1 -1
  32. data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +2 -2
  33. data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +1 -1
  34. data/lib/statsd/instrument/rubocop/metric_return_value.rb +2 -2
  35. data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +1 -1
  36. data/lib/statsd/instrument/rubocop/positional_arguments.rb +4 -4
  37. data/lib/statsd/instrument/rubocop/singleton_configuration.rb +1 -1
  38. data/lib/statsd/instrument/rubocop/splat_arguments.rb +2 -2
  39. data/lib/statsd/instrument/statsd_datagram_builder.rb +12 -8
  40. data/lib/statsd/instrument/strict.rb +1 -6
  41. data/lib/statsd/instrument/udp_sink.rb +49 -47
  42. data/lib/statsd/instrument/version.rb +1 -1
  43. data/statsd-instrument.gemspec +4 -8
  44. data/test/assertions_test.rb +205 -161
  45. data/test/benchmark/clock_gettime.rb +1 -1
  46. data/test/benchmark/default_tags.rb +9 -9
  47. data/test/benchmark/metrics.rb +8 -8
  48. data/test/benchmark/tags.rb +4 -4
  49. data/test/capture_sink_test.rb +14 -14
  50. data/test/client_test.rb +96 -96
  51. data/test/datagram_builder_test.rb +55 -55
  52. data/test/datagram_test.rb +5 -5
  53. data/test/dogstatsd_datagram_builder_test.rb +37 -37
  54. data/test/environment_test.rb +30 -21
  55. data/test/helpers/rubocop_helper.rb +12 -9
  56. data/test/helpers_test.rb +15 -15
  57. data/test/integration_test.rb +7 -7
  58. data/test/log_sink_test.rb +4 -4
  59. data/test/matchers_test.rb +54 -54
  60. data/test/null_sink_test.rb +4 -4
  61. data/test/rubocop/measure_as_dist_argument_test.rb +2 -2
  62. data/test/rubocop/metaprogramming_positional_arguments_test.rb +2 -2
  63. data/test/rubocop/metric_prefix_argument_test.rb +2 -2
  64. data/test/rubocop/metric_return_value_test.rb +6 -6
  65. data/test/rubocop/metric_value_keyword_argument_test.rb +3 -3
  66. data/test/rubocop/positional_arguments_test.rb +12 -12
  67. data/test/rubocop/singleton_configuration_test.rb +8 -8
  68. data/test/rubocop/splat_arguments_test.rb +2 -2
  69. data/test/statsd_datagram_builder_test.rb +6 -6
  70. data/test/statsd_instrumentation_test.rb +122 -122
  71. data/test/statsd_test.rb +69 -67
  72. data/test/test_helper.rb +19 -10
  73. data/test/udp_sink_test.rb +122 -50
  74. metadata +12 -92
  75. data/.github/workflows/ci.yml +0 -56
  76. data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +0 -1027
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module StatsD::Instrument::Helpers
4
- def capture_statsd_datagrams(client: nil, &block)
5
- client ||= StatsD.singleton_client
6
- client.capture(&block)
7
- end
3
+ module StatsD
4
+ module Instrument
5
+ module Helpers
6
+ def capture_statsd_datagrams(client: nil, &block)
7
+ client ||= StatsD.singleton_client
8
+ client.capture(&block)
9
+ end
8
10
 
9
- # For backwards compatibility
10
- alias_method :capture_statsd_calls, :capture_statsd_datagrams
11
+ # For backwards compatibility
12
+ alias_method :capture_statsd_calls, :capture_statsd_datagrams
13
+ end
14
+ end
11
15
  end
@@ -1,24 +1,28 @@
1
1
  # frozen_string_literal: true
2
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::LogSink
6
- attr_reader :logger, :severity
3
+ module StatsD
4
+ module Instrument
5
+ # @note This class is part of the new Client implementation that is intended
6
+ # to become the new default in the next major release of this library.
7
+ class LogSink
8
+ attr_reader :logger, :severity
7
9
 
8
- def initialize(logger, severity: Logger::DEBUG)
9
- @logger = logger
10
- @severity = severity
11
- end
10
+ def initialize(logger, severity: Logger::DEBUG)
11
+ @logger = logger
12
+ @severity = severity
13
+ end
12
14
 
13
- def sample?(_sample_rate)
14
- true
15
- end
15
+ def sample?(_sample_rate)
16
+ true
17
+ end
16
18
 
17
- def <<(datagram)
18
- # Some implementations require a newline at the end of datagrams.
19
- # When logging, we make sure those newlines are removed using chomp.
19
+ def <<(datagram)
20
+ # Some implementations require a newline at the end of datagrams.
21
+ # When logging, we make sure those newlines are removed using chomp.
20
22
 
21
- logger.add(severity, "[StatsD] #{datagram.chomp}")
22
- self
23
+ logger.add(severity, "[StatsD] #{datagram.chomp}")
24
+ self
25
+ end
26
+ end
23
27
  end
24
28
  end
@@ -1,100 +1,119 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rspec/expectations'
4
- require 'rspec/core/version'
5
-
6
- module StatsD::Instrument::Matchers
7
- CUSTOM_MATCHERS = {
8
- increment: :c,
9
- measure: :ms,
10
- gauge: :g,
11
- histogram: :h,
12
- distribution: :d,
13
- set: :s,
14
- key_value: :kv,
15
- }
16
-
17
- class Matcher
18
- include RSpec::Matchers::Composable if RSpec::Core::Version::STRING.start_with?('3')
19
- include StatsD::Instrument::Helpers
20
-
21
- def initialize(metric_type, metric_name, options = {})
22
- @metric_type = metric_type
23
- @metric_name = metric_name
24
- @options = options
25
- end
3
+ require "rspec/expectations"
4
+ require "rspec/core/version"
5
+
6
+ module StatsD
7
+ module Instrument
8
+ module Matchers
9
+ class Matcher
10
+ include(RSpec::Matchers::Composable) if RSpec::Core::Version::STRING.start_with?("3")
11
+ include StatsD::Instrument::Helpers
12
+
13
+ def initialize(metric_type, metric_name, options = {})
14
+ @metric_type = metric_type
15
+ @metric_name = metric_name
16
+ @options = options
17
+ end
26
18
 
27
- def matches?(block)
28
- expect_statsd_call(@metric_type, @metric_name, @options, &block)
29
- rescue RSpec::Expectations::ExpectationNotMetError => e
30
- @message = e.message
31
- false
32
- end
19
+ def matches?(block)
20
+ expect_statsd_call(@metric_type, @metric_name, @options, &block)
21
+ rescue RSpec::Expectations::ExpectationNotMetError => e
22
+ @message = e.message
23
+ false
24
+ end
33
25
 
34
- def failure_message
35
- @message
36
- end
26
+ def failure_message
27
+ @message
28
+ end
37
29
 
38
- def failure_message_when_negated
39
- "No StatsD calls for metric #{@metric_name} expected."
40
- end
30
+ def failure_message_when_negated
31
+ "No StatsD calls for metric #{@metric_name} expected."
32
+ end
41
33
 
42
- def supports_block_expectations?
43
- true
44
- end
34
+ def supports_block_expectations?
35
+ true
36
+ end
45
37
 
46
- private
38
+ def description
39
+ "trigger a statsd call for metric #{@metric_name}"
40
+ end
47
41
 
48
- def expect_statsd_call(metric_type, metric_name, options, &block)
49
- metrics = capture_statsd_calls(&block)
50
- metrics = metrics.select { |m| m.type == metric_type && m.name == metric_name }
42
+ private
51
43
 
52
- if metrics.empty?
53
- raise RSpec::Expectations::ExpectationNotMetError, "No StatsD calls for metric #{metric_name} were made."
54
- elsif options[:times] && options[:times] != metrics.length
55
- raise RSpec::Expectations::ExpectationNotMetError, "The numbers of StatsD calls for metric #{metric_name} " \
56
- "was unexpected. Expected #{options[:times].inspect}, got #{metrics.length}"
57
- end
44
+ def expect_statsd_call(metric_type, metric_name, options, &block)
45
+ metrics = capture_statsd_calls(&block)
46
+ metrics = metrics.select { |m| m.type == metric_type && m.name == metric_name }
47
+
48
+ if metrics.empty?
49
+ raise RSpec::Expectations::ExpectationNotMetError, "No StatsD calls for metric #{metric_name} were made."
50
+ elsif options[:times] && options[:times] != metrics.length
51
+ raise RSpec::Expectations::ExpectationNotMetError, "The numbers of StatsD calls for metric " \
52
+ "#{metric_name} was unexpected. Expected #{options[:times].inspect}, got #{metrics.length}"
53
+ end
54
+
55
+ [:sample_rate, :value, :tags].each do |expectation|
56
+ next unless options[expectation]
58
57
 
59
- [:sample_rate, :value, :tags].each do |expectation|
60
- next unless options[expectation]
58
+ num_matches = metrics.count do |m|
59
+ matcher = RSpec::Matchers::BuiltIn::Match.new(options[expectation])
60
+ matcher.matches?(m.public_send(expectation))
61
+ end
61
62
 
62
- num_matches = metrics.count do |m|
63
- matcher = RSpec::Matchers::BuiltIn::Match.new(options[expectation])
64
- matcher.matches?(m.public_send(expectation))
63
+ found = options[:times] ? num_matches == options[:times] : num_matches > 0
64
+
65
+ unless found
66
+ message = metric_information(metric_name, options, metrics, expectation)
67
+ raise RSpec::Expectations::ExpectationNotMetError, message
68
+ end
69
+ end
70
+
71
+ true
65
72
  end
66
73
 
67
- found = options[:times] ? num_matches == options[:times] : num_matches > 0
74
+ def metric_information(metric_name, options, metrics, expectation)
75
+ message = "expected StatsD #{expectation.inspect} for metric '#{metric_name}' to be called"
76
+
77
+ message += "\n "
78
+ message += options[:times] ? "exactly #{options[:times]} times" : "at least once"
79
+ message += " with: #{options[expectation]}"
80
+
81
+ message += "\n captured metric values: #{metrics.map(&expectation).join(", ")}"
68
82
 
69
- unless found
70
- message = metric_information(metric_name, options, metrics, expectation)
71
- raise RSpec::Expectations::ExpectationNotMetError, message
83
+ message
72
84
  end
73
85
  end
74
86
 
75
- true
76
- end
87
+ Increment = Class.new(Matcher)
88
+ Measure = Class.new(Matcher)
89
+ Gauge = Class.new(Matcher)
90
+ Set = Class.new(Matcher)
91
+ Histogram = Class.new(Matcher)
92
+ Distribution = Class.new(Matcher)
77
93
 
78
- def metric_information(metric_name, options, metrics, expectation)
79
- message = "expected StatsD #{expectation.inspect} for metric '#{metric_name}' to be called"
94
+ def trigger_statsd_increment(metric_name, options = {})
95
+ Increment.new(:c, metric_name, options)
96
+ end
80
97
 
81
- message += "\n "
82
- message += options[:times] ? "exactly #{options[:times]} times" : "at least once"
83
- message += " with: #{options[expectation]}"
98
+ def trigger_statsd_measure(metric_name, options = {})
99
+ Measure.new(:ms, metric_name, options)
100
+ end
84
101
 
85
- message += "\n captured metric values: #{metrics.map(&expectation).join(', ')}"
102
+ def trigger_statsd_gauge(metric_name, options = {})
103
+ Gauge.new(:g, metric_name, options)
104
+ end
86
105
 
87
- message
88
- end
89
- end
106
+ def trigger_statsd_set(metric_name, options = {})
107
+ Set.new(:s, metric_name, options)
108
+ end
90
109
 
91
- CUSTOM_MATCHERS.each do |method_name, metric_type|
92
- klass = Class.new(Matcher)
110
+ def trigger_statsd_histogram(metric_name, options = {})
111
+ Histogram.new(:h, metric_name, options)
112
+ end
93
113
 
94
- define_method "trigger_statsd_#{method_name}" do |metric_name, options = {}|
95
- klass.new(metric_type, metric_name, options)
114
+ def trigger_statsd_distribution(metric_name, options = {})
115
+ Distribution.new(:d, metric_name, options)
116
+ end
96
117
  end
97
-
98
- StatsD::Instrument::Matchers.const_set(method_name.capitalize, klass)
99
118
  end
100
119
  end
@@ -1,13 +1,17 @@
1
1
  # frozen_string_literal: true
2
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::NullSink
6
- def sample?(_sample_rate)
7
- true
8
- end
3
+ module StatsD
4
+ module Instrument
5
+ # @note This class is part of the new Client implementation that is intended
6
+ # to become the new default in the next major release of this library.
7
+ class NullSink
8
+ def sample?(_sample_rate)
9
+ true
10
+ end
9
11
 
10
- def <<(_datagram)
11
- self # noop
12
+ def <<(_datagram)
13
+ self # noop
14
+ end
15
+ end
12
16
  end
13
17
  end
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # This Railtie runs some initializers that will set the logger to <tt>Rails#logger</tt>,
4
- # and will initialize the {StatsD#backend} based on the Rails environment.
5
- #
6
- # @see StatsD::Instrument::Environment
7
- class StatsD::Instrument::Railtie < Rails::Railtie
8
- initializer 'statsd-instrument.use_rails_logger' do
9
- ::StatsD.logger = Rails.logger
3
+ module StatsD
4
+ module Instrument
5
+ # This Railtie runs some initializers that will set the logger to <tt>Rails#logger</tt>,
6
+ # and will initialize the {StatsD#backend} based on the Rails environment.
7
+ #
8
+ # @see StatsD::Instrument::Environment
9
+ class Railtie < Rails::Railtie
10
+ initializer "statsd-instrument.use_rails_logger" do
11
+ ::StatsD.logger = Rails.logger
12
+ end
13
+ end
10
14
  end
11
15
  end
@@ -72,11 +72,11 @@ module RuboCop
72
72
  end
73
73
  end
74
74
 
75
- require_relative 'rubocop/metaprogramming_positional_arguments'
76
- require_relative 'rubocop/metric_return_value'
77
- require_relative 'rubocop/metric_value_keyword_argument'
78
- require_relative 'rubocop/positional_arguments'
79
- require_relative 'rubocop/splat_arguments'
80
- require_relative 'rubocop/measure_as_dist_argument'
81
- require_relative 'rubocop/metric_prefix_argument'
82
- require_relative 'rubocop/singleton_configuration'
75
+ require_relative "rubocop/metaprogramming_positional_arguments"
76
+ require_relative "rubocop/metric_return_value"
77
+ require_relative "rubocop/metric_value_keyword_argument"
78
+ require_relative "rubocop/positional_arguments"
79
+ require_relative "rubocop/splat_arguments"
80
+ require_relative "rubocop/measure_as_dist_argument"
81
+ require_relative "rubocop/metric_prefix_argument"
82
+ require_relative "rubocop/singleton_configuration"
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require_relative '../rubocop' unless defined?(RuboCop::Cop::StatsD)
3
+ require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
4
4
 
5
5
  module RuboCop
6
6
  module Cop
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require_relative '../rubocop' unless defined?(RuboCop::Cop::StatsD)
3
+ require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
4
4
 
5
5
  module RuboCop
6
6
  module Cop
@@ -19,7 +19,7 @@ module RuboCop
19
19
  class MetaprogrammingPositionalArguments < Cop
20
20
  include RuboCop::Cop::StatsD
21
21
 
22
- MSG = 'Use keyword arguments for StatsD metaprogramming macros'
22
+ MSG = "Use keyword arguments for StatsD metaprogramming macros"
23
23
 
24
24
  def on_send(node)
25
25
  if metaprogramming_method?(node)
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require_relative '../rubocop' unless defined?(RuboCop::Cop::StatsD)
3
+ require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
4
4
 
5
5
  module RuboCop
6
6
  module Cop
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require_relative '../rubocop' unless defined?(RuboCop::Cop::StatsD)
3
+ require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
4
4
 
5
5
  module RuboCop
6
6
  module Cop
@@ -17,7 +17,7 @@ module RuboCop
17
17
  class MetricReturnValue < Cop
18
18
  include RuboCop::Cop::StatsD
19
19
 
20
- MSG = 'Do not use the return value of StatsD metric methods'
20
+ MSG = "Do not use the return value of StatsD metric methods"
21
21
 
22
22
  INVALID_PARENTS = %i{lvasgn array pair send return yield}
23
23
 
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require_relative '../rubocop' unless defined?(RuboCop::Cop::StatsD)
3
+ require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
4
4
 
5
5
  module RuboCop
6
6
  module Cop
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require_relative '../rubocop' unless defined?(RuboCop::Cop::StatsD)
3
+ require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
4
4
 
5
5
  module RuboCop
6
6
  module Cop
@@ -17,7 +17,7 @@ module RuboCop
17
17
  class PositionalArguments < Cop
18
18
  include RuboCop::Cop::StatsD
19
19
 
20
- MSG = 'Use keyword arguments for StatsD calls'
20
+ MSG = "Use keyword arguments for StatsD calls"
21
21
 
22
22
  POSITIONAL_ARGUMENT_TYPES = Set[:int, :float, :nil]
23
23
  UNKNOWN_ARGUMENT_TYPES = Set[:send, :const, :lvar, :splat]
@@ -80,7 +80,7 @@ module RuboCop
80
80
 
81
81
  tags = positional_arguments[1]
82
82
  if tags && tags.type != :nil
83
- keyword_arguments << if tags.type == :hash && tags.source[0] != '{'
83
+ keyword_arguments << if tags.type == :hash && tags.source[0] != "{"
84
84
  "tags: { #{tags.source} }"
85
85
  else
86
86
  "tags: #{tags.source}"
@@ -88,7 +88,7 @@ module RuboCop
88
88
  end
89
89
 
90
90
  unless keyword_arguments.empty?
91
- corrector.insert_after(value_argument.source_range, ", #{keyword_arguments.join(', ')}")
91
+ corrector.insert_after(value_argument.source_range, ", #{keyword_arguments.join(", ")}")
92
92
  end
93
93
  end
94
94
  end
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require_relative '../rubocop' unless defined?(RuboCop::Cop::StatsD)
3
+ require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
4
4
 
5
5
  module RuboCop
6
6
  module Cop
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require_relative '../rubocop' unless defined?(RuboCop::Cop::StatsD)
3
+ require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
4
4
 
5
5
  module RuboCop
6
6
  module Cop
@@ -16,7 +16,7 @@ module RuboCop
16
16
  class SplatArguments < Cop
17
17
  include RuboCop::Cop::StatsD
18
18
 
19
- MSG = 'Do not use splat arguments in StatsD metric calls'
19
+ MSG = "Do not use splat arguments in StatsD metric calls"
20
20
 
21
21
  def on_send(node)
22
22
  if metric_method?(node)