statsd-instrument 3.0.2 → 3.1.0

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