statsd-instrument 3.0.2 → 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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint.yml +22 -0
  3. data/.github/workflows/{ci.yml → tests.yml} +3 -21
  4. data/.rubocop.yml +1 -1
  5. data/CHANGELOG.md +6 -0
  6. data/Gemfile +8 -10
  7. data/README.md +3 -0
  8. data/Rakefile +6 -6
  9. data/benchmark/send-metrics-to-dev-null-log +12 -12
  10. data/benchmark/send-metrics-to-local-udp-receiver +16 -16
  11. data/lib/statsd-instrument.rb +1 -1
  12. data/lib/statsd/instrument.rb +56 -59
  13. data/lib/statsd/instrument/assertions.rb +1 -1
  14. data/lib/statsd/instrument/batched_udp_sink.rb +154 -0
  15. data/lib/statsd/instrument/client.rb +3 -3
  16. data/lib/statsd/instrument/datagram.rb +1 -1
  17. data/lib/statsd/instrument/datagram_builder.rb +10 -10
  18. data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +2 -2
  19. data/lib/statsd/instrument/environment.rb +19 -11
  20. data/lib/statsd/instrument/expectation.rb +3 -3
  21. data/lib/statsd/instrument/matchers.rb +8 -4
  22. data/lib/statsd/instrument/railtie.rb +1 -1
  23. data/lib/statsd/instrument/rubocop.rb +8 -8
  24. data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +1 -1
  25. data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +2 -2
  26. data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +1 -1
  27. data/lib/statsd/instrument/rubocop/metric_return_value.rb +2 -2
  28. data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +1 -1
  29. data/lib/statsd/instrument/rubocop/positional_arguments.rb +4 -4
  30. data/lib/statsd/instrument/rubocop/singleton_configuration.rb +1 -1
  31. data/lib/statsd/instrument/rubocop/splat_arguments.rb +2 -2
  32. data/lib/statsd/instrument/strict.rb +1 -1
  33. data/lib/statsd/instrument/udp_sink.rb +10 -12
  34. data/lib/statsd/instrument/version.rb +1 -1
  35. data/statsd-instrument.gemspec +2 -0
  36. data/test/assertions_test.rb +167 -169
  37. data/test/benchmark/clock_gettime.rb +1 -1
  38. data/test/benchmark/default_tags.rb +9 -9
  39. data/test/benchmark/metrics.rb +8 -8
  40. data/test/benchmark/tags.rb +4 -4
  41. data/test/capture_sink_test.rb +11 -11
  42. data/test/client_test.rb +64 -64
  43. data/test/datagram_builder_test.rb +40 -40
  44. data/test/datagram_test.rb +5 -5
  45. data/test/dogstatsd_datagram_builder_test.rb +22 -22
  46. data/test/environment_test.rb +26 -17
  47. data/test/helpers/rubocop_helper.rb +2 -2
  48. data/test/helpers_test.rb +12 -12
  49. data/test/integration_test.rb +6 -6
  50. data/test/log_sink_test.rb +2 -2
  51. data/test/matchers_test.rb +46 -46
  52. data/test/null_sink_test.rb +2 -2
  53. data/test/rubocop/measure_as_dist_argument_test.rb +2 -2
  54. data/test/rubocop/metaprogramming_positional_arguments_test.rb +2 -2
  55. data/test/rubocop/metric_prefix_argument_test.rb +2 -2
  56. data/test/rubocop/metric_return_value_test.rb +3 -3
  57. data/test/rubocop/metric_value_keyword_argument_test.rb +2 -2
  58. data/test/rubocop/positional_arguments_test.rb +2 -2
  59. data/test/rubocop/singleton_configuration_test.rb +8 -8
  60. data/test/rubocop/splat_arguments_test.rb +2 -2
  61. data/test/statsd_datagram_builder_test.rb +6 -6
  62. data/test/statsd_instrumentation_test.rb +104 -104
  63. data/test/statsd_test.rb +35 -35
  64. data/test/test_helper.rb +13 -6
  65. data/test/udp_sink_test.rb +117 -45
  66. metadata +20 -4
data/test/statsd_test.rb CHANGED
@@ -1,33 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'test_helper'
3
+ require "test_helper"
4
4
 
5
5
  class StatsDTest < Minitest::Test
6
6
  include StatsD::Instrument::Assertions
7
7
 
8
8
  def test_statsd_measure_with_explicit_value
9
- metric = capture_statsd_call { StatsD.measure('values.foobar', 42) }
10
- assert_equal('values.foobar', metric.name)
9
+ metric = capture_statsd_call { StatsD.measure("values.foobar", 42) }
10
+ assert_equal("values.foobar", metric.name)
11
11
  assert_equal(42, metric.value)
12
12
  assert_equal(:ms, metric.type)
13
13
  end
14
14
 
15
15
  def test_statsd_measure_with_explicit_value_and_sample_rate
16
- metric = capture_statsd_call { StatsD.measure('values.foobar', 42, sample_rate: 0.1) }
16
+ metric = capture_statsd_call { StatsD.measure("values.foobar", 42, sample_rate: 0.1) }
17
17
  assert_equal(0.1, metric.sample_rate)
18
18
  end
19
19
 
20
20
  def test_statsd_measure_with_benchmarked_block_duration
21
21
  Process.stubs(:clock_gettime).returns(5.0, 5.0 + 1.12)
22
22
  metric = capture_statsd_call do
23
- StatsD.measure('values.foobar') { 'foo' }
23
+ StatsD.measure("values.foobar") { "foo" }
24
24
  end
25
25
  assert_equal(1120.0, metric.value)
26
26
  end
27
27
 
28
28
  def test_statsd_measure_returns_return_value_of_block
29
- return_value = StatsD.measure('values.foobar') { 'sarah' }
30
- assert_equal('sarah', return_value)
29
+ return_value = StatsD.measure("values.foobar") { "sarah" }
30
+ assert_equal("sarah", return_value)
31
31
  end
32
32
 
33
33
  def test_statsd_measure_with_return_in_block_still_captures
@@ -35,13 +35,13 @@ class StatsDTest < Minitest::Test
35
35
  result = nil
36
36
  metric = capture_statsd_call do
37
37
  lambda = -> do
38
- StatsD.measure('values.foobar') { return 'from lambda' }
38
+ StatsD.measure("values.foobar") { return "from lambda" }
39
39
  end
40
40
 
41
41
  result = lambda.call
42
42
  end
43
43
 
44
- assert_equal('from lambda', result)
44
+ assert_equal("from lambda", result)
45
45
  assert_equal(1120.0, metric.value)
46
46
  end
47
47
 
@@ -50,7 +50,7 @@ class StatsDTest < Minitest::Test
50
50
  result = nil
51
51
  metric = capture_statsd_call do
52
52
  lambda = -> do
53
- StatsD.measure('values.foobar') { raise 'from lambda' }
53
+ StatsD.measure("values.foobar") { raise "from lambda" }
54
54
  end
55
55
 
56
56
  begin
@@ -65,56 +65,56 @@ class StatsDTest < Minitest::Test
65
65
  end
66
66
 
67
67
  def test_statsd_increment
68
- metric = capture_statsd_call { StatsD.increment('values.foobar', 3) }
68
+ metric = capture_statsd_call { StatsD.increment("values.foobar", 3) }
69
69
  assert_equal(:c, metric.type)
70
- assert_equal('values.foobar', metric.name)
70
+ assert_equal("values.foobar", metric.name)
71
71
  assert_equal(3, metric.value)
72
72
  end
73
73
 
74
74
  def test_statsd_increment_with_hash_argument
75
- metric = capture_statsd_call { StatsD.increment('values.foobar', tags: ['test']) }
75
+ metric = capture_statsd_call { StatsD.increment("values.foobar", tags: ["test"]) }
76
76
  assert_equal(StatsD.singleton_client.default_sample_rate, metric.sample_rate)
77
- assert_equal(['test'], metric.tags)
77
+ assert_equal(["test"], metric.tags)
78
78
  assert_equal(1, metric.value)
79
79
  end
80
80
 
81
81
  def test_statsd_gauge
82
- metric = capture_statsd_call { StatsD.gauge('values.foobar', 12) }
82
+ metric = capture_statsd_call { StatsD.gauge("values.foobar", 12) }
83
83
  assert_equal(:g, metric.type)
84
- assert_equal('values.foobar', metric.name)
84
+ assert_equal("values.foobar", metric.name)
85
85
  assert_equal(12, metric.value)
86
86
  end
87
87
 
88
88
  def test_statsd_gauge_without_value
89
- assert_raises(ArgumentError) { StatsD.gauge('values.foobar') }
89
+ assert_raises(ArgumentError) { StatsD.gauge("values.foobar") }
90
90
  end
91
91
 
92
92
  def test_statsd_set
93
- metric = capture_statsd_call { StatsD.set('values.foobar', 'unique_identifier') }
93
+ metric = capture_statsd_call { StatsD.set("values.foobar", "unique_identifier") }
94
94
  assert_equal(:s, metric.type)
95
- assert_equal('values.foobar', metric.name)
96
- assert_equal('unique_identifier', metric.value)
95
+ assert_equal("values.foobar", metric.name)
96
+ assert_equal("unique_identifier", metric.value)
97
97
  end
98
98
 
99
99
  def test_statsd_histogram
100
- metric = capture_statsd_call { StatsD.histogram('values.foobar', 42) }
100
+ metric = capture_statsd_call { StatsD.histogram("values.foobar", 42) }
101
101
  assert_equal(:h, metric.type)
102
- assert_equal('values.foobar', metric.name)
102
+ assert_equal("values.foobar", metric.name)
103
103
  assert_equal(42, metric.value)
104
104
  end
105
105
 
106
106
  def test_statsd_distribution
107
- metric = capture_statsd_call { StatsD.distribution('values.foobar', 42) }
107
+ metric = capture_statsd_call { StatsD.distribution("values.foobar", 42) }
108
108
  assert_equal(:d, metric.type)
109
- assert_equal('values.foobar', metric.name)
109
+ assert_equal("values.foobar", metric.name)
110
110
  assert_equal(42, metric.value)
111
111
  end
112
112
 
113
113
  def test_statsd_distribution_with_benchmarked_block_duration
114
114
  Process.stubs(:clock_gettime).returns(5.0, 5.0 + 1.12)
115
115
  metric = capture_statsd_call do
116
- result = StatsD.distribution('values.foobar') { 'foo' }
117
- assert_equal('foo', result)
116
+ result = StatsD.distribution("values.foobar") { "foo" }
117
+ assert_equal("foo", result)
118
118
  end
119
119
  assert_equal(:d, metric.type)
120
120
  assert_equal(1120.0, metric.value)
@@ -125,14 +125,14 @@ class StatsDTest < Minitest::Test
125
125
  result = nil
126
126
  metric = capture_statsd_call do
127
127
  lambda = -> do
128
- StatsD.distribution('values.foobar') { return 'from lambda' }
128
+ StatsD.distribution("values.foobar") { return "from lambda" }
129
129
  flunk("This code should not be reached")
130
130
  end
131
131
 
132
132
  result = lambda.call
133
133
  end
134
134
 
135
- assert_equal('from lambda', result)
135
+ assert_equal("from lambda", result)
136
136
  assert_equal(:d, metric.type)
137
137
  assert_equal(1120.0, metric.value)
138
138
  end
@@ -142,7 +142,7 @@ class StatsDTest < Minitest::Test
142
142
  result = nil
143
143
  metric = capture_statsd_call do
144
144
  lambda = -> do
145
- StatsD.distribution('values.foobar') { raise 'from lambda' }
145
+ StatsD.distribution("values.foobar") { raise "from lambda" }
146
146
  end
147
147
 
148
148
  begin
@@ -160,21 +160,21 @@ class StatsDTest < Minitest::Test
160
160
  def test_statsd_distribution_with_block_and_options
161
161
  Process.stubs(:clock_gettime).returns(5.0, 5.0 + 1.12)
162
162
  metric = capture_statsd_call do
163
- StatsD.distribution('values.foobar', tags: ['test'], sample_rate: 0.9) { 'foo' }
163
+ StatsD.distribution("values.foobar", tags: ["test"], sample_rate: 0.9) { "foo" }
164
164
  end
165
165
  assert_equal(1120.0, metric.value)
166
- assert_equal('values.foobar', metric.name)
166
+ assert_equal("values.foobar", metric.name)
167
167
  assert_equal(0.9, metric.sample_rate)
168
- assert_equal(['test'], metric.tags)
168
+ assert_equal(["test"], metric.tags)
169
169
  end
170
170
 
171
171
  def test_statsd_distribution_returns_return_value_of_block
172
- return_value = StatsD.distribution('values.foobar') { 'sarah' }
173
- assert_equal('sarah', return_value)
172
+ return_value = StatsD.distribution("values.foobar") { "sarah" }
173
+ assert_equal("sarah", return_value)
174
174
  end
175
175
 
176
176
  def test_statsd_measure_returns_return_value_of_block_even_if_nil
177
- return_value = StatsD.distribution('values.foobar') { nil }
177
+ return_value = StatsD.distribution("values.foobar") { nil }
178
178
  assert_nil(return_value)
179
179
  end
180
180
 
data/test/test_helper.rb CHANGED
@@ -1,13 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- ENV['ENV'] = 'test'
3
+ if Warning.respond_to?(:[]=)
4
+ Warning[:deprecated] = true
5
+ end
6
+
7
+ ENV["ENV"] = "test"
4
8
 
5
- require 'minitest/autorun'
6
- require 'minitest/pride'
7
- require 'mocha/minitest'
8
- require 'statsd-instrument'
9
+ require "minitest/autorun"
10
+ require "minitest/pride"
11
+ require "mocha/minitest"
12
+ require "statsd-instrument"
9
13
 
10
- require_relative 'helpers/rubocop_helper'
14
+ require_relative "helpers/rubocop_helper"
11
15
 
12
16
  module StatsD
13
17
  module Instrument
@@ -19,3 +23,6 @@ module StatsD
19
23
  end
20
24
 
21
25
  StatsD.logger = Logger.new(File::NULL)
26
+
27
+ Thread.abort_on_exception = true
28
+ Thread.report_on_exception = true
@@ -1,29 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'test_helper'
4
-
5
- class UDPSinkTest < Minitest::Test
6
- def setup
7
- @receiver = UDPSocket.new
8
- @receiver.bind('localhost', 0)
9
- @host = @receiver.addr[2]
10
- @port = @receiver.addr[1]
11
- end
12
-
13
- def teardown
14
- @receiver.close
15
- end
3
+ require "test_helper"
16
4
 
5
+ module UDPSinkTests
17
6
  def test_udp_sink_sends_data_over_udp
18
- udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
19
- udp_sink << 'foo:1|c'
7
+ udp_sink = build_sink(@host, @port)
8
+ udp_sink << "foo:1|c"
20
9
 
21
10
  datagram, _source = @receiver.recvfrom(100)
22
- assert_equal('foo:1|c', datagram)
11
+ assert_equal("foo:1|c", datagram)
12
+ end
13
+
14
+ def large_datagram
15
+ datagram = "#{"a" * 1000}:1|c"
16
+ udp_sink = build_sink(@host, @port)
17
+ udp_sink << datagram
18
+
19
+ datagram, _source = @receiver.recvfrom(1500)
20
+ assert_equal(datagram, datagram)
23
21
  end
24
22
 
25
23
  def test_sample?
26
- udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
24
+ udp_sink = build_sink(@host, @port)
27
25
  assert(udp_sink.sample?(1))
28
26
  refute(udp_sink.sample?(0))
29
27
 
@@ -35,49 +33,123 @@ class UDPSinkTest < Minitest::Test
35
33
  end
36
34
 
37
35
  def test_parallelism
38
- udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
39
- 50.times { |i| Thread.new { udp_sink << "foo:#{i}|c" << "bar:#{i}|c" } }
36
+ udp_sink = build_sink(@host, @port)
37
+ 50.times.map { |i| Thread.new { udp_sink << "foo:#{i}|c" << "bar:#{i}|c" } }
40
38
  datagrams = []
41
- 100.times do
42
- datagram, _source = @receiver.recvfrom(100)
43
- datagrams << datagram
39
+
40
+ while @receiver.wait_readable(2)
41
+ datagram, _source = @receiver.recvfrom(4000)
42
+ datagrams += datagram.split("\n")
44
43
  end
45
44
 
46
45
  assert_equal(100, datagrams.size)
47
46
  end
48
47
 
49
- def test_socket_error_should_invalidate_socket
50
- UDPSocket.stubs(:new).returns(socket = mock('socket'))
51
-
52
- seq = sequence('connect_fail_connect_succeed')
53
- socket.expects(:connect).with('localhost', 8125).in_sequence(seq)
54
- socket.expects(:send).raises(Errno::EDESTADDRREQ).in_sequence(seq)
55
- socket.expects(:connect).with('localhost', 8125).in_sequence(seq)
56
- socket.expects(:send).returns(1).in_sequence(seq)
57
-
58
- udp_sink = StatsD::Instrument::UDPSink.new('localhost', 8125)
59
- udp_sink << 'foo:1|c'
60
- udp_sink << 'bar:1|c'
48
+ class SimpleFormatter < ::Logger::Formatter
49
+ def call(_severity, _timestamp, _progname, msg)
50
+ "#{String === msg ? msg : msg.inspect}\n"
51
+ end
61
52
  end
62
53
 
63
54
  def test_sends_datagram_in_signal_handler
64
- udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
65
- pid = fork do
66
- Signal.trap('TERM') do
67
- udp_sink << "exiting:1|c"
68
- Process.exit!(0)
69
- end
55
+ udp_sink = build_sink(@host, @port)
56
+ Signal.trap("USR1") { udp_sink << "exiting:1|c" }
70
57
 
71
- sleep(10)
58
+ pid = fork do
59
+ sleep(5)
72
60
  end
73
61
 
74
- Process.kill('TERM', pid)
75
- _, exit_status = Process.waitpid2(pid)
62
+ Signal.trap("USR1", "DEFAULT")
76
63
 
77
- assert_equal(0, exit_status, "The forked process did not exit cleanly")
64
+ Process.kill("USR1", pid)
65
+ @receiver.wait_readable(1)
78
66
  assert_equal("exiting:1|c", @receiver.recvfrom_nonblock(100).first)
79
-
67
+ Process.kill("KILL", pid)
80
68
  rescue NotImplementedError
81
69
  pass("Fork is not implemented on #{RUBY_PLATFORM}")
82
70
  end
71
+
72
+ private
73
+
74
+ def build_sink(host = @host, port = @port)
75
+ @sink_class.new(host, port)
76
+ end
77
+
78
+ class UDPSinkTest < Minitest::Test
79
+ include UDPSinkTests
80
+
81
+ def setup
82
+ @receiver = UDPSocket.new
83
+ @receiver.bind("localhost", 0)
84
+ @host = @receiver.addr[2]
85
+ @port = @receiver.addr[1]
86
+ @sink_class = StatsD::Instrument::UDPSink
87
+ end
88
+
89
+ def teardown
90
+ @receiver.close
91
+ end
92
+
93
+ def test_socket_error_should_invalidate_socket
94
+ previous_logger = StatsD.logger
95
+ begin
96
+ logs = StringIO.new
97
+ StatsD.logger = Logger.new(logs)
98
+ StatsD.logger.formatter = SimpleFormatter.new
99
+ UDPSocket.stubs(:new).returns(socket = mock("socket"))
100
+
101
+ seq = sequence("connect_fail_connect_succeed")
102
+ socket.expects(:connect).with("localhost", 8125).in_sequence(seq)
103
+ socket.expects(:send).raises(Errno::EDESTADDRREQ).in_sequence(seq)
104
+ socket.expects(:connect).with("localhost", 8125).in_sequence(seq)
105
+ socket.expects(:send).returns(1).in_sequence(seq)
106
+
107
+ udp_sink = build_sink("localhost", 8125)
108
+ udp_sink << "foo:1|c"
109
+ udp_sink << "bar:1|c"
110
+
111
+ assert_equal(
112
+ "[#{@sink_class}] Resetting connection because of " \
113
+ "Errno::EDESTADDRREQ: Destination address required\n",
114
+ logs.string,
115
+ )
116
+ ensure
117
+ StatsD.logger = previous_logger
118
+ end
119
+ end
120
+ end
121
+
122
+ class BatchedUDPSinkTest < Minitest::Test
123
+ include UDPSinkTests
124
+
125
+ def setup
126
+ @receiver = UDPSocket.new
127
+ @receiver.bind("localhost", 0)
128
+ @host = @receiver.addr[2]
129
+ @port = @receiver.addr[1]
130
+ @sink_class = StatsD::Instrument::BatchedUDPSink
131
+ end
132
+
133
+ def teardown
134
+ @receiver.close
135
+ end
136
+
137
+ def test_parallelism_buffering
138
+ udp_sink = build_sink(@host, @port)
139
+ 50.times.map do |i|
140
+ Thread.new do
141
+ udp_sink << "foo:#{i}|c" << "bar:#{i}|c" << "baz:#{i}|c" << "plop:#{i}|c"
142
+ end
143
+ end
144
+
145
+ datagrams = []
146
+
147
+ while @receiver.wait_readable(2)
148
+ datagram, _source = @receiver.recvfrom(1000)
149
+ datagrams += datagram.split("\n")
150
+ end
151
+
152
+ assert_equal(200, datagrams.size)
153
+ end
154
+ end
83
155
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statsd-instrument
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jesse Storimer
@@ -10,8 +10,22 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-01-20 00:00:00.000000000 Z
14
- dependencies: []
13
+ date: 2021-04-09 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: concurrent-ruby
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
15
29
  description: A StatsD client for Ruby apps. Provides metaprogramming methods to inject
16
30
  StatsD instrumentation into your code.
17
31
  email:
@@ -23,7 +37,8 @@ files:
23
37
  - ".github/CODEOWNERS"
24
38
  - ".github/probots.yml"
25
39
  - ".github/workflows/benchmark.yml"
26
- - ".github/workflows/ci.yml"
40
+ - ".github/workflows/lint.yml"
41
+ - ".github/workflows/tests.yml"
27
42
  - ".gitignore"
28
43
  - ".rubocop.yml"
29
44
  - ".yardopts"
@@ -41,6 +56,7 @@ files:
41
56
  - lib/statsd-instrument.rb
42
57
  - lib/statsd/instrument.rb
43
58
  - lib/statsd/instrument/assertions.rb
59
+ - lib/statsd/instrument/batched_udp_sink.rb
44
60
  - lib/statsd/instrument/capture_sink.rb
45
61
  - lib/statsd/instrument/client.rb
46
62
  - lib/statsd/instrument/datagram.rb