statsd-instrument 3.0.0 → 3.0.1

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +3 -3
  3. data/.rubocop.yml +3 -13
  4. data/CHANGELOG.md +6 -0
  5. data/Gemfile +8 -0
  6. data/README.md +2 -2
  7. data/Rakefile +1 -1
  8. data/bin/rake +29 -0
  9. data/bin/rubocop +29 -0
  10. data/lib/statsd/instrument.rb +4 -1
  11. data/lib/statsd/instrument/assertions.rb +200 -196
  12. data/lib/statsd/instrument/capture_sink.rb +23 -19
  13. data/lib/statsd/instrument/client.rb +414 -410
  14. data/lib/statsd/instrument/datagram.rb +69 -65
  15. data/lib/statsd/instrument/datagram_builder.rb +81 -77
  16. data/lib/statsd/instrument/dogstatsd_datagram.rb +76 -72
  17. data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +68 -64
  18. data/lib/statsd/instrument/environment.rb +80 -77
  19. data/lib/statsd/instrument/expectation.rb +96 -92
  20. data/lib/statsd/instrument/helpers.rb +11 -7
  21. data/lib/statsd/instrument/log_sink.rb +20 -16
  22. data/lib/statsd/instrument/matchers.rb +86 -70
  23. data/lib/statsd/instrument/null_sink.rb +12 -8
  24. data/lib/statsd/instrument/railtie.rb +11 -7
  25. data/lib/statsd/instrument/statsd_datagram_builder.rb +12 -8
  26. data/lib/statsd/instrument/udp_sink.rb +50 -46
  27. data/lib/statsd/instrument/version.rb +1 -1
  28. data/statsd-instrument.gemspec +2 -8
  29. data/test/assertions_test.rb +12 -12
  30. data/test/capture_sink_test.rb +8 -8
  31. data/test/client_test.rb +54 -54
  32. data/test/datagram_builder_test.rb +29 -29
  33. data/test/datagram_test.rb +1 -1
  34. data/test/dogstatsd_datagram_builder_test.rb +28 -28
  35. data/test/environment_test.rb +9 -9
  36. data/test/helpers/rubocop_helper.rb +9 -6
  37. data/test/helpers_test.rb +5 -5
  38. data/test/integration_test.rb +1 -1
  39. data/test/log_sink_test.rb +2 -2
  40. data/test/matchers_test.rb +36 -36
  41. data/test/null_sink_test.rb +2 -2
  42. data/test/rubocop/metric_return_value_test.rb +3 -3
  43. data/test/rubocop/positional_arguments_test.rb +10 -10
  44. data/test/statsd_instrumentation_test.rb +66 -66
  45. data/test/statsd_test.rb +44 -44
  46. data/test/test_helper.rb +6 -4
  47. data/test/udp_sink_test.rb +8 -8
  48. metadata +7 -103
  49. 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
@@ -3,97 +3,113 @@
3
3
  require 'rspec/expectations'
4
4
  require 'rspec/core/version'
5
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
- }
15
-
16
- class Matcher
17
- include RSpec::Matchers::Composable if RSpec::Core::Version::STRING.start_with?('3')
18
- include StatsD::Instrument::Helpers
19
-
20
- def initialize(metric_type, metric_name, options = {})
21
- @metric_type = metric_type
22
- @metric_name = metric_name
23
- @options = options
24
- end
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
25
18
 
26
- def matches?(block)
27
- expect_statsd_call(@metric_type, @metric_name, @options, &block)
28
- rescue RSpec::Expectations::ExpectationNotMetError => e
29
- @message = e.message
30
- false
31
- 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
32
25
 
33
- def failure_message
34
- @message
35
- end
26
+ def failure_message
27
+ @message
28
+ end
36
29
 
37
- def failure_message_when_negated
38
- "No StatsD calls for metric #{@metric_name} expected."
39
- end
30
+ def failure_message_when_negated
31
+ "No StatsD calls for metric #{@metric_name} expected."
32
+ end
40
33
 
41
- def supports_block_expectations?
42
- true
43
- end
34
+ def supports_block_expectations?
35
+ true
36
+ end
44
37
 
45
- private
38
+ private
46
39
 
47
- def expect_statsd_call(metric_type, metric_name, options, &block)
48
- metrics = capture_statsd_calls(&block)
49
- metrics = metrics.select { |m| m.type == metric_type && m.name == metric_name }
40
+ def expect_statsd_call(metric_type, metric_name, options, &block)
41
+ metrics = capture_statsd_calls(&block)
42
+ metrics = metrics.select { |m| m.type == metric_type && m.name == metric_name }
50
43
 
51
- if metrics.empty?
52
- raise RSpec::Expectations::ExpectationNotMetError, "No StatsD calls for metric #{metric_name} were made."
53
- elsif options[:times] && options[:times] != metrics.length
54
- raise RSpec::Expectations::ExpectationNotMetError, "The numbers of StatsD calls for metric #{metric_name} " \
55
- "was unexpected. Expected #{options[:times].inspect}, got #{metrics.length}"
56
- end
44
+ if metrics.empty?
45
+ raise RSpec::Expectations::ExpectationNotMetError, "No StatsD calls for metric #{metric_name} were made."
46
+ elsif options[:times] && options[:times] != metrics.length
47
+ raise RSpec::Expectations::ExpectationNotMetError, "The numbers of StatsD calls for metric " \
48
+ "#{metric_name} was unexpected. Expected #{options[:times].inspect}, got #{metrics.length}"
49
+ end
50
+
51
+ [:sample_rate, :value, :tags].each do |expectation|
52
+ next unless options[expectation]
53
+
54
+ num_matches = metrics.count do |m|
55
+ matcher = RSpec::Matchers::BuiltIn::Match.new(options[expectation])
56
+ matcher.matches?(m.public_send(expectation))
57
+ end
57
58
 
58
- [:sample_rate, :value, :tags].each do |expectation|
59
- next unless options[expectation]
59
+ found = options[:times] ? num_matches == options[:times] : num_matches > 0
60
60
 
61
- num_matches = metrics.count do |m|
62
- matcher = RSpec::Matchers::BuiltIn::Match.new(options[expectation])
63
- matcher.matches?(m.public_send(expectation))
61
+ unless found
62
+ message = metric_information(metric_name, options, metrics, expectation)
63
+ raise RSpec::Expectations::ExpectationNotMetError, message
64
+ end
65
+ end
66
+
67
+ true
64
68
  end
65
69
 
66
- found = options[:times] ? num_matches == options[:times] : num_matches > 0
70
+ def metric_information(metric_name, options, metrics, expectation)
71
+ message = "expected StatsD #{expectation.inspect} for metric '#{metric_name}' to be called"
72
+
73
+ message += "\n "
74
+ message += options[:times] ? "exactly #{options[:times]} times" : "at least once"
75
+ message += " with: #{options[expectation]}"
67
76
 
68
- unless found
69
- message = metric_information(metric_name, options, metrics, expectation)
70
- raise RSpec::Expectations::ExpectationNotMetError, message
77
+ message += "\n captured metric values: #{metrics.map(&expectation).join(', ')}"
78
+
79
+ message
71
80
  end
72
81
  end
73
82
 
74
- true
75
- end
83
+ Increment = Class.new(Matcher)
84
+ Measure = Class.new(Matcher)
85
+ Gauge = Class.new(Matcher)
86
+ Set = Class.new(Matcher)
87
+ Histogram = Class.new(Matcher)
88
+ Distribution = Class.new(Matcher)
76
89
 
77
- def metric_information(metric_name, options, metrics, expectation)
78
- message = "expected StatsD #{expectation.inspect} for metric '#{metric_name}' to be called"
90
+ def trigger_statsd_increment(metric_name, options = {})
91
+ Increment.new(:c, metric_name, options)
92
+ end
79
93
 
80
- message += "\n "
81
- message += options[:times] ? "exactly #{options[:times]} times" : "at least once"
82
- message += " with: #{options[expectation]}"
94
+ def trigger_statsd_measure(metric_name, options = {})
95
+ Measure.new(:ms, metric_name, options)
96
+ end
83
97
 
84
- message += "\n captured metric values: #{metrics.map(&expectation).join(', ')}"
98
+ def trigger_statsd_gauge(metric_name, options = {})
99
+ Gauge.new(:g, metric_name, options)
100
+ end
85
101
 
86
- message
87
- end
88
- end
102
+ def trigger_statsd_set(metric_name, options = {})
103
+ Set.new(:s, metric_name, options)
104
+ end
89
105
 
90
- CUSTOM_MATCHERS.each do |method_name, metric_type|
91
- klass = Class.new(Matcher)
106
+ def trigger_statsd_histogram(metric_name, options = {})
107
+ Histogram.new(:h, metric_name, options)
108
+ end
92
109
 
93
- define_method "trigger_statsd_#{method_name}" do |metric_name, options = {}|
94
- klass.new(metric_type, metric_name, options)
110
+ def trigger_statsd_distribution(metric_name, options = {})
111
+ Distribution.new(:d, metric_name, options)
112
+ end
95
113
  end
96
-
97
- StatsD::Instrument::Matchers.const_set(method_name.capitalize, klass)
98
114
  end
99
115
  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
@@ -1,14 +1,18 @@
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::StatsDDatagramBuilder < StatsD::Instrument::DatagramBuilder
6
- unsupported_datagram_types :h, :d, :kv
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 StatsDDatagramBuilder < StatsD::Instrument::DatagramBuilder
8
+ unsupported_datagram_types :h, :d, :kv
7
9
 
8
- protected
10
+ protected
9
11
 
10
- def normalize_tags(tags)
11
- raise NotImplementedError, "#{self.class.name} does not support tags" if tags
12
- super
12
+ def normalize_tags(tags)
13
+ raise NotImplementedError, "#{self.class.name} does not support tags" if tags
14
+ super
15
+ end
16
+ end
13
17
  end
14
18
  end
@@ -1,62 +1,66 @@
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::UDPSink
6
- def self.for_addr(addr)
7
- host, port_as_string = addr.split(':', 2)
8
- new(host, Integer(port_as_string))
9
- 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 UDPSink
8
+ def self.for_addr(addr)
9
+ host, port_as_string = addr.split(':', 2)
10
+ new(host, Integer(port_as_string))
11
+ end
10
12
 
11
- attr_reader :host, :port
13
+ attr_reader :host, :port
12
14
 
13
- def initialize(host, port)
14
- @host = host
15
- @port = port
16
- @mutex = Mutex.new
17
- @socket = nil
18
- end
15
+ def initialize(host, port)
16
+ @host = host
17
+ @port = port
18
+ @mutex = Mutex.new
19
+ @socket = nil
20
+ end
19
21
 
20
- def sample?(sample_rate)
21
- sample_rate == 1 || rand < sample_rate
22
- end
22
+ def sample?(sample_rate)
23
+ sample_rate == 1 || rand < sample_rate
24
+ end
23
25
 
24
- def <<(datagram)
25
- with_socket { |socket| socket.send(datagram, 0) > 0 }
26
- self
26
+ def <<(datagram)
27
+ with_socket { |socket| socket.send(datagram, 0) > 0 }
28
+ self
27
29
 
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
30
+ rescue ThreadError
31
+ # In cases where a TERM or KILL signal has been sent, and we send stats as
32
+ # part of a signal handler, locks cannot be acquired, so we do our best
33
+ # to try and send the datagram without a lock.
34
+ socket.send(datagram, 0) > 0
33
35
 
34
- rescue SocketError, IOError, SystemCallError
35
- # TODO: log?
36
- invalidate_socket
37
- end
36
+ rescue SocketError, IOError, SystemCallError
37
+ # TODO: log?
38
+ invalidate_socket
39
+ end
38
40
 
39
- def addr
40
- "#{host}:#{port}"
41
- end
41
+ def addr
42
+ "#{host}:#{port}"
43
+ end
42
44
 
43
- private
45
+ private
44
46
 
45
- def with_socket
46
- @mutex.synchronize { yield(socket) }
47
- end
47
+ def with_socket
48
+ @mutex.synchronize { yield(socket) }
49
+ end
48
50
 
49
- def socket
50
- if @socket.nil?
51
- @socket = UDPSocket.new
52
- @socket.connect(@host, @port)
53
- end
54
- @socket
55
- end
51
+ def socket
52
+ if @socket.nil?
53
+ @socket = UDPSocket.new
54
+ @socket.connect(@host, @port)
55
+ end
56
+ @socket
57
+ end
56
58
 
57
- def invalidate_socket
58
- @mutex.synchronize do
59
- @socket = nil
59
+ def invalidate_socket
60
+ @mutex.synchronize do
61
+ @socket = nil
62
+ end
63
+ end
60
64
  end
61
65
  end
62
66
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module StatsD
4
4
  module Instrument
5
- VERSION = "3.0.0"
5
+ VERSION = "3.0.1"
6
6
  end
7
7
  end