statsd-instrument 3.0.0 → 3.0.1

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