statsd-instrument 3.0.0.pre1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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)