statsd-instrument 2.5.1 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +1 -1
  3. data/.rubocop.yml +11 -6
  4. data/.yardopts +5 -0
  5. data/CHANGELOG.md +75 -6
  6. data/README.md +54 -46
  7. data/benchmark/datagram-client +41 -0
  8. data/lib/statsd/instrument/assertions.rb +168 -57
  9. data/lib/statsd/instrument/backends/udp_backend.rb +20 -29
  10. data/lib/statsd/instrument/capture_sink.rb +27 -0
  11. data/lib/statsd/instrument/client.rb +313 -0
  12. data/lib/statsd/instrument/datagram.rb +75 -0
  13. data/lib/statsd/instrument/datagram_builder.rb +101 -0
  14. data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +71 -0
  15. data/lib/statsd/instrument/environment.rb +106 -29
  16. data/lib/statsd/instrument/log_sink.rb +24 -0
  17. data/lib/statsd/instrument/null_sink.rb +13 -0
  18. data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +39 -0
  19. data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +6 -10
  20. data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +37 -0
  21. data/lib/statsd/instrument/rubocop/metric_return_value.rb +7 -6
  22. data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +11 -20
  23. data/lib/statsd/instrument/rubocop/positional_arguments.rb +13 -13
  24. data/lib/statsd/instrument/rubocop/splat_arguments.rb +8 -14
  25. data/lib/statsd/instrument/rubocop.rb +64 -0
  26. data/lib/statsd/instrument/statsd_datagram_builder.rb +14 -0
  27. data/lib/statsd/instrument/strict.rb +112 -22
  28. data/lib/statsd/instrument/udp_sink.rb +62 -0
  29. data/lib/statsd/instrument/version.rb +1 -1
  30. data/lib/statsd/instrument.rb +191 -100
  31. data/test/assertions_test.rb +139 -176
  32. data/test/capture_sink_test.rb +44 -0
  33. data/test/client_test.rb +164 -0
  34. data/test/compatibility/dogstatsd_datagram_compatibility_test.rb +162 -0
  35. data/test/datagram_builder_test.rb +120 -0
  36. data/test/deprecations_test.rb +56 -10
  37. data/test/dogstatsd_datagram_builder_test.rb +32 -0
  38. data/test/environment_test.rb +73 -7
  39. data/test/log_sink_test.rb +37 -0
  40. data/test/null_sink_test.rb +13 -0
  41. data/test/rubocop/measure_as_dist_argument_test.rb +44 -0
  42. data/test/rubocop/metaprogramming_positional_arguments_test.rb +1 -1
  43. data/test/rubocop/metric_prefix_argument_test.rb +38 -0
  44. data/test/rubocop/metric_return_value_test.rb +1 -1
  45. data/test/rubocop/metric_value_keyword_argument_test.rb +1 -1
  46. data/test/rubocop/positional_arguments_test.rb +1 -1
  47. data/test/rubocop/splat_arguments_test.rb +1 -1
  48. data/test/statsd_datagram_builder_test.rb +22 -0
  49. data/test/statsd_instrumentation_test.rb +0 -24
  50. data/test/statsd_test.rb +0 -23
  51. data/test/test_helper.rb +0 -2
  52. data/test/udp_backend_test.rb +25 -8
  53. data/test/udp_sink_test.rb +85 -0
  54. metadata +38 -2
@@ -10,22 +10,22 @@ class EnvironmentTest < Minitest::Test
10
10
  ENV['IMPLEMENTATION'] = nil
11
11
  end
12
12
 
13
- def test_uses_logger_in_development_environment
13
+ def test_default_backend_uses_logger_in_development_environment
14
14
  StatsD::Instrument::Environment.stubs(:environment).returns('development')
15
15
  assert_instance_of StatsD::Instrument::Backends::LoggerBackend, StatsD::Instrument::Environment.default_backend
16
16
  end
17
17
 
18
- def test_uses_null_backend_in_test_environment
18
+ def test_default_backend_uses_null_backend_in_test_environment
19
19
  StatsD::Instrument::Environment.stubs(:environment).returns('test')
20
20
  assert_instance_of StatsD::Instrument::Backends::NullBackend, StatsD::Instrument::Environment.default_backend
21
21
  end
22
22
 
23
- def test_uses_udp_backend_in_production_environment
23
+ def test_default_backend_uses_udp_backend_in_production_environment
24
24
  StatsD::Instrument::Environment.stubs(:environment).returns('production')
25
25
  assert_instance_of StatsD::Instrument::Backends::UDPBackend, StatsD::Instrument::Environment.default_backend
26
26
  end
27
27
 
28
- def test_uses_environment_variables_in_production_environment
28
+ def test_default_backend_uses_environment_variables_in_production_environment
29
29
  StatsD::Instrument::Environment.stubs(:environment).returns('production')
30
30
  ENV['STATSD_ADDR'] = '127.0.0.1:1234'
31
31
  ENV['STATSD_IMPLEMENTATION'] = 'datadog'
@@ -36,12 +36,78 @@ class EnvironmentTest < Minitest::Test
36
36
  assert_equal :datadog, backend.implementation
37
37
  end
38
38
 
39
- def test_uses_env_when_rails_does_not_respond_to_env
40
- assert_equal ENV['ENV'], StatsD::Instrument::Environment.environment
39
+ def test_environment_prefers_statsd_env_if_available
40
+ env = StatsD::Instrument::Environment.new(
41
+ 'STATSD_ENV' => 'set_from_STATSD_ENV',
42
+ 'RACK_ENV' => 'set_from_RACK_ENV',
43
+ 'ENV' => 'set_from_ENV',
44
+ )
45
+ assert_equal 'set_from_STATSD_ENV', env.environment
41
46
  end
42
47
 
43
- def test_uses_rails_env_when_rails_is_available
48
+ def test_environment_uses_env_when_rails_does_not_respond_to_env_and_statsd_env_is_not_set
49
+ env = StatsD::Instrument::Environment.new(
50
+ 'ENV' => 'set_from_ENV',
51
+ )
52
+ assert_equal 'set_from_ENV', env.environment
53
+ end
54
+
55
+ def test_environment_uses_rails_env_when_rails_is_available
44
56
  Rails.stubs(:env).returns('production')
45
57
  assert_equal 'production', StatsD::Instrument::Environment.environment
46
58
  end
59
+
60
+ def test_environment_defaults_to_development
61
+ env = StatsD::Instrument::Environment.new({})
62
+ assert_equal 'development', env.environment
63
+ end
64
+
65
+ def test_default_client_uses_log_sink_in_development_environment
66
+ env = StatsD::Instrument::Environment.new('STATSD_ENV' => 'development')
67
+ assert_kind_of StatsD::Instrument::LogSink, env.default_client.sink
68
+ end
69
+
70
+ def test_default_client_uses_null_sink_in_test_environment
71
+ env = StatsD::Instrument::Environment.new('STATSD_ENV' => 'test')
72
+ assert_kind_of StatsD::Instrument::NullSink, env.default_client.sink
73
+ end
74
+
75
+ def test_default_client_uses_udp_sink_in_staging_environment
76
+ env = StatsD::Instrument::Environment.new('STATSD_ENV' => 'staging')
77
+ assert_kind_of StatsD::Instrument::UDPSink, env.default_client.sink
78
+ end
79
+
80
+ def test_default_client_uses_udp_sink_in_production_environment
81
+ env = StatsD::Instrument::Environment.new('STATSD_ENV' => 'production')
82
+ assert_kind_of StatsD::Instrument::UDPSink, env.default_client.sink
83
+ end
84
+
85
+ def test_default_client_respects_statsd_environment_variables
86
+ env = StatsD::Instrument::Environment.new(
87
+ 'STATSD_ENV' => 'production',
88
+ 'STATSD_IMPLEMENTATION' => 'datadog',
89
+ 'STATSD_ADDR' => 'foo:8125',
90
+ 'STATSD_SAMPLE_RATE' => "0.1",
91
+ 'STATSD_PREFIX' => "foo",
92
+ 'STATSD_DEFAULT_TAGS' => "foo,bar:baz",
93
+ )
94
+
95
+ assert_equal StatsD::Instrument::DogStatsDDatagramBuilder, env.default_client.datagram_builder_class
96
+ assert_equal 'foo', env.default_client.sink.host
97
+ assert_equal 8125, env.default_client.sink.port
98
+ assert_equal 0.1, env.default_client.default_sample_rate
99
+ assert_equal "foo", env.default_client.prefix
100
+ assert_equal ["foo", "bar:baz"], env.default_client.default_tags
101
+ end
102
+
103
+ def test_default_client_has_sensible_defaults
104
+ env = StatsD::Instrument::Environment.new('STATSD_ENV' => 'production')
105
+
106
+ assert_equal StatsD::Instrument::StatsDDatagramBuilder, env.default_client.datagram_builder_class
107
+ assert_equal 'localhost', env.default_client.sink.host
108
+ assert_equal 8125, env.default_client.sink.port
109
+ assert_equal 1.0, env.default_client.default_sample_rate
110
+ assert_nil env.default_client.prefix
111
+ assert_nil env.default_client.default_tags
112
+ end
47
113
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ require 'statsd/instrument/client'
6
+
7
+ class LogSinktest < Minitest::Test
8
+ def test_log_sink
9
+ logger = Logger.new(log = StringIO.new)
10
+ logger.formatter = proc do |severity, _datetime, _progname, msg|
11
+ "#{severity}: #{msg}\n"
12
+ end
13
+
14
+ log_sink = StatsD::Instrument::LogSink.new(logger)
15
+ log_sink << 'foo:1|c' << 'bar:1|c'
16
+
17
+ assert_equal <<~LOG, log.string
18
+ DEBUG: [StatsD] foo:1|c
19
+ DEBUG: [StatsD] bar:1|c
20
+ LOG
21
+ end
22
+
23
+ def test_log_sink_chomps_trailing_newlines
24
+ logger = Logger.new(log = StringIO.new)
25
+ logger.formatter = proc do |severity, _datetime, _progname, msg|
26
+ "#{severity}: #{msg}\n"
27
+ end
28
+
29
+ log_sink = StatsD::Instrument::LogSink.new(logger)
30
+ log_sink << "foo:1|c\n" << "bar:1|c\n"
31
+
32
+ assert_equal <<~LOG, log.string
33
+ DEBUG: [StatsD] foo:1|c
34
+ DEBUG: [StatsD] bar:1|c
35
+ LOG
36
+ end
37
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ require 'statsd/instrument/client'
6
+
7
+ class NullSinktest < Minitest::Test
8
+ def test_null_sink
9
+ null_sink = StatsD::Instrument::NullSink.new
10
+ null_sink << 'foo:1|c' << 'bar:1|c'
11
+ pass # We don't have anything to assert, except that no exception was raised
12
+ end
13
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'statsd/instrument/rubocop'
5
+
6
+ module Rubocop
7
+ class MeasureAsDistArgumentTest < Minitest::Test
8
+ include RubocopHelper
9
+
10
+ def setup
11
+ @cop = RuboCop::Cop::StatsD::MeasureAsDistArgument.new
12
+ end
13
+
14
+ def test_ok_for_metric_method_without_as_dist_argument
15
+ assert_no_offenses("StatsD.measure('foo', 123)")
16
+ assert_no_offenses("StatsD.measure('foo', 123, sample_rate: 3)")
17
+ assert_no_offenses("StatsD.measure('foo') {}")
18
+ end
19
+
20
+ def test_ok_for_other_metric_methods
21
+ assert_no_offenses("StatsD.increment('foo', as_dist: true)")
22
+ end
23
+
24
+ def test_ok_for_metaprogramming_method_without_as_dist_argument
25
+ assert_no_offenses("statsd_measure(:method, 'metric_name', sample_rate: 1) {}")
26
+ end
27
+
28
+ def test_ok_for_other_metaprogramming_methods
29
+ assert_no_offenses("statsd_distribution(:method, 'metric_name', as_dist: true) {}")
30
+ end
31
+
32
+ def test_offense_when_using_as_dist_with_measure_metric_method
33
+ assert_offense("StatsD.measure('foo', 123, sample_rate: 1, as_dist: true, tags: ['foo'])")
34
+ assert_offense("StatsD.measure('foo', 123, as_dist: false)")
35
+ assert_offense("StatsD.measure('foo', as_dist: true, &block)")
36
+ assert_offense("StatsD.measure('foo', as_dist: true) { } ")
37
+ end
38
+
39
+ def test_offense_when_using_as_dist_with_measure_metaprogramming_method
40
+ assert_offense("statsd_measure(:method, 'foo', as_dist: true, &block)")
41
+ assert_offense("statsd_measure(:method, 'foo', as_dist: false) { } ")
42
+ end
43
+ end
44
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'test_helper'
4
- require 'statsd/instrument/rubocop/metaprogramming_positional_arguments'
4
+ require 'statsd/instrument/rubocop'
5
5
 
6
6
  module Rubocop
7
7
  class MetaprogrammingPositionalArgumentsTest < Minitest::Test
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'statsd/instrument/rubocop'
5
+
6
+ module Rubocop
7
+ class MetricPrefixArgumentTest < Minitest::Test
8
+ include RubocopHelper
9
+
10
+ def setup
11
+ @cop = RuboCop::Cop::StatsD::MetricPrefixArgument.new
12
+ end
13
+
14
+ def test_ok_for_metric_method_without_prefix_argument
15
+ assert_no_offenses("StatsD.measure('foo', 123) {}")
16
+ assert_no_offenses("StatsD.increment('foo', 123, sample_rate: 3, no_prefix: true)")
17
+ assert_no_offenses("StatsD.gauge('foo', 123)")
18
+ end
19
+
20
+ def test_ok_for_metaprogramming_method_without_prefix_argument
21
+ assert_no_offenses("statsd_measure(:method, 'metric_name')")
22
+ assert_no_offenses("statsd_count(:method, 'metric_name', sample_rate: 1, no_pefix: true)")
23
+ assert_no_offenses("statsd_count_if(:method, 'metric_name', sample_rate: 1) {}")
24
+ end
25
+
26
+ def test_offense_when_using_as_dist_with_measure_metric_method
27
+ assert_offense("StatsD.measure('foo', 123, sample_rate: 1, prefix: 'pre', tags: ['bar'])")
28
+ assert_offense("StatsD.gauge('foo', 123, prefix: nil)")
29
+ assert_offense("StatsD.increment('foo', prefix: 'pre', &block)")
30
+ assert_offense("StatsD.set('foo', prefix: 'pre') { } ")
31
+ end
32
+
33
+ def test_offense_when_using_as_dist_with_measure_metaprogramming_method
34
+ assert_offense("statsd_measure(:method, 'foo', prefix: 'foo')")
35
+ assert_offense("statsd_count_if(:method, 'foo', prefix: nil) { } ")
36
+ end
37
+ end
38
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'test_helper'
4
- require 'statsd/instrument/rubocop/metric_return_value'
4
+ require 'statsd/instrument/rubocop'
5
5
 
6
6
  module Rubocop
7
7
  class MetricReturnValueTest < Minitest::Test
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'test_helper'
4
- require 'statsd/instrument/rubocop/metric_value_keyword_argument'
4
+ require 'statsd/instrument/rubocop'
5
5
 
6
6
  module Rubocop
7
7
  class MetricValueKeywordArgumentTest < Minitest::Test
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'test_helper'
4
- require 'statsd/instrument/rubocop/positional_arguments'
4
+ require 'statsd/instrument/rubocop'
5
5
 
6
6
  module Rubocop
7
7
  class PositionalArgumentsTest < Minitest::Test
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'test_helper'
4
- require 'statsd/instrument/rubocop/splat_arguments'
4
+ require 'statsd/instrument/rubocop'
5
5
 
6
6
  module Rubocop
7
7
  class SplatArgumentsTest < Minitest::Test
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ require 'statsd/instrument/client'
6
+
7
+ class StatsDDatagramBuilderTest < Minitest::Test
8
+ def setup
9
+ @datagram_builder = StatsD::Instrument::StatsDDatagramBuilder.new
10
+ end
11
+
12
+ def test_raises_on_unsupported_metrics
13
+ assert_raises(NotImplementedError) { @datagram_builder.h('fo:o', 10, nil, nil) }
14
+ assert_raises(NotImplementedError) { @datagram_builder.d('fo:o', 10, nil, nil) }
15
+ assert_raises(NotImplementedError) { @datagram_builder.kv('fo:o', 10, nil, nil) }
16
+ end
17
+
18
+ def test_raises_when_using_tags
19
+ assert_raises(NotImplementedError) { @datagram_builder.c('fo:o', 10, nil, foo: 'bar') }
20
+ assert_raises(NotImplementedError) { StatsD::Instrument::StatsDDatagramBuilder.new(default_tags: ['foo']) }
21
+ end
22
+ end
@@ -248,16 +248,6 @@ class StatsDInstrumentationTest < Minitest::Test
248
248
  ActiveMerchant::UniqueGateway.statsd_remove_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post'
249
249
  end
250
250
 
251
- def test_statsd_measure_as_distribution
252
- ActiveMerchant::UniqueGateway.statsd_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post', as_dist: true
253
-
254
- assert_statsd_distribution('ActiveMerchant.Gateway.ssl_post') do
255
- ActiveMerchant::UniqueGateway.new.purchase(true)
256
- end
257
- ensure
258
- ActiveMerchant::UniqueGateway.statsd_remove_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post'
259
- end
260
-
261
251
  def test_statsd_distribution
262
252
  ActiveMerchant::UniqueGateway.statsd_distribution :ssl_post, 'ActiveMerchant.Gateway.ssl_post', sample_rate: 0.3
263
253
 
@@ -351,20 +341,6 @@ class StatsDInstrumentationTest < Minitest::Test
351
341
  ActiveMerchant::Gateway.singleton_class.statsd_remove_count :sync, 'ActiveMerchant.Gateway.sync'
352
342
  end
353
343
 
354
- def test_statsd_macro_can_overwrite_prefix
355
- StatsD.prefix = 'Foo'
356
- ActiveMerchant::Gateway.singleton_class.extend StatsD::Instrument
357
- ActiveMerchant::Gateway.singleton_class.statsd_measure :sync, 'ActiveMerchant.Gateway.sync', prefix: 'Bar'
358
- StatsD.prefix = 'Quc'
359
-
360
- statsd_calls = capture_statsd_calls { ActiveMerchant::Gateway.sync }
361
- assert_equal 1, statsd_calls.length
362
- assert_equal "Bar.ActiveMerchant.Gateway.sync", statsd_calls.first.name
363
- ensure
364
- StatsD.prefix = nil
365
- ActiveMerchant::Gateway.singleton_class.statsd_remove_measure :sync, 'ActiveMerchant.Gateway.sync'
366
- end
367
-
368
344
  def test_statsd_macro_can_disable_prefix
369
345
  StatsD.prefix = 'Foo'
370
346
  ActiveMerchant::Gateway.singleton_class.extend StatsD::Instrument
data/test/statsd_test.rb CHANGED
@@ -21,11 +21,6 @@ class StatsDTest < Minitest::Test
21
21
  assert_equal :ms, metric.type
22
22
  end
23
23
 
24
- def test_statsd_measure_with_explicit_value_and_distribution_override
25
- metric = capture_statsd_call { StatsD.measure('values.foobar', 42, as_dist: true) }
26
- assert_equal :d, metric.type
27
- end
28
-
29
24
  def test_statsd_measure_without_value_or_block
30
25
  assert_raises(ArgumentError) { StatsD.measure('values.foobar', tags: 123) }
31
26
  end
@@ -43,23 +38,11 @@ class StatsDTest < Minitest::Test
43
38
  assert_equal 1120.0, metric.value
44
39
  end
45
40
 
46
- def test_statsd_measure_use_distribution_override_for_a_block
47
- metric = capture_statsd_call do
48
- StatsD.measure('values.foobar', as_dist: true) { 'foo' }
49
- end
50
- assert_equal :d, metric.type
51
- end
52
-
53
41
  def test_statsd_measure_returns_return_value_of_block
54
42
  return_value = StatsD.measure('values.foobar') { 'sarah' }
55
43
  assert_equal 'sarah', return_value
56
44
  end
57
45
 
58
- def test_statsd_measure_as_distribution_returns_return_value_of_block_even_if_nil
59
- return_value = StatsD.measure('values.foobar', as_dist: true) { nil }
60
- assert_nil return_value
61
- end
62
-
63
46
  def test_statsd_measure_with_return_in_block_still_captures
64
47
  Process.stubs(:clock_gettime).returns(5.0, 6.12)
65
48
  result = nil
@@ -236,12 +219,6 @@ class StatsDTest < Minitest::Test
236
219
 
237
220
  m = capture_statsd_call { StatsD.increment('counter', no_prefix: true) }
238
221
  assert_equal 'counter', m.name
239
-
240
- m = capture_statsd_call { StatsD.increment('counter', prefix: "foobar") }
241
- assert_equal 'foobar.counter', m.name
242
-
243
- m = capture_statsd_call { StatsD.increment('counter', prefix: "foobar", no_prefix: true) }
244
- assert_equal 'counter', m.name
245
222
  end
246
223
 
247
224
  protected
data/test/test_helper.rb CHANGED
@@ -11,8 +11,6 @@ require 'statsd-instrument'
11
11
 
12
12
  require_relative 'helpers/rubocop_helper'
13
13
 
14
- require 'statsd/instrument/strict' if ENV['STATSD_STRICT_MODE']
15
-
16
14
  module StatsD::Instrument
17
15
  def self.strict_mode_enabled?
18
16
  StatsD::Instrument.const_defined?(:Strict) &&
@@ -86,8 +86,8 @@ class UDPBackendTest < Minitest::Test
86
86
 
87
87
  def test_event_on_datadog
88
88
  @backend.implementation = :datadog
89
- @backend.expects(:write_packet).with('_e{4,3}:fooh|baz|h:localhost:3000|@0.01|#foo')
90
- StatsD.event('fooh', 'baz', hostname: 'localhost:3000', sample_rate: 0.01, tags: ["foo"])
89
+ @backend.expects(:write_packet).with('_e{4,3}:fooh|baz|h:localhost|#foo')
90
+ StatsD.event('fooh', 'baz', hostname: 'localhost', tags: ["foo"])
91
91
  end
92
92
 
93
93
  def test_event_on_datadog_escapes_newlines
@@ -98,8 +98,13 @@ class UDPBackendTest < Minitest::Test
98
98
 
99
99
  def test_event_on_datadog_ignores_invalid_metadata
100
100
  @backend.implementation = :datadog
101
- @backend.expects(:write_packet).with('_e{4,3}:fooh|baz')
102
- StatsD.event('fooh', 'baz', i_am_not_supported: 'not-supported')
101
+ if StatsD::Instrument.strict_mode_enabled?
102
+ assert_raises(ArgumentError) { StatsD.event('fooh', 'baz', sample_rate: 0.01) }
103
+ assert_raises(ArgumentError) { StatsD.event('fooh', 'baz', unsupported: 'foo') }
104
+ else
105
+ @backend.expects(:write_packet).with('_e{4,3}:fooh|baz')
106
+ StatsD.event('fooh', 'baz', sample_rate: 0.01, i_am_not_supported: 'not-supported')
107
+ end
103
108
  end
104
109
 
105
110
  def test_event_warns_when_not_using_datadog
@@ -111,14 +116,26 @@ class UDPBackendTest < Minitest::Test
111
116
 
112
117
  def test_service_check_on_datadog
113
118
  @backend.implementation = :datadog
114
- @backend.expects(:write_packet).with('_sc|fooh|baz|h:localhost:3000|@0.01|#foo')
115
- StatsD.service_check('fooh', 'baz', hostname: 'localhost:3000', sample_rate: 0.01, tags: ["foo"])
119
+ @backend.expects(:write_packet).with('_sc|fooh|0|h:localhost|#foo')
120
+ StatsD.service_check('fooh', 0, hostname: 'localhost', tags: ["foo"])
116
121
  end
117
122
 
118
123
  def test_service_check_on_datadog_ignores_invalid_metadata
119
124
  @backend.implementation = :datadog
120
- @backend.expects(:write_packet).with('_sc|fooh|baz')
121
- StatsD.service_check('fooh', 'baz', i_am_not_supported: 'not-supported')
125
+ if StatsD::Instrument.strict_mode_enabled?
126
+ assert_raises(ArgumentError) { StatsD.service_check('fooh', "warning", sample_rate: 0.01) }
127
+ assert_raises(ArgumentError) { StatsD.service_check('fooh', "warning", unsupported: 'foo') }
128
+ else
129
+ @backend.expects(:write_packet).with('_sc|fooh|1')
130
+ StatsD.service_check('fooh', "warning", sample_rate: 0.01, i_am_not_supported: 'not-supported')
131
+ end
132
+ end
133
+
134
+ def test_service_check_on_datadog_will_append_message_as_final_metadata_field
135
+ @backend.implementation = :datadog
136
+ @backend.expects(:write_packet).with('_sc|fooh|0|d:1230768000|#quc|m:Everything OK')
137
+ StatsD.service_check('fooh', :ok, message: "Everything OK",
138
+ timestamp: Time.parse('2009-01-01T00:00:00Z'), tags: ['quc'])
122
139
  end
123
140
 
124
141
  def test_service_check_warns_when_not_using_datadog
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ require 'statsd/instrument/client'
6
+
7
+ class UDPSinktest < Minitest::Test
8
+ def setup
9
+ @receiver = UDPSocket.new
10
+ @receiver.bind('localhost', 0)
11
+ @host = @receiver.addr[2]
12
+ @port = @receiver.addr[1]
13
+ end
14
+
15
+ def teardown
16
+ @receiver.close
17
+ end
18
+
19
+ def test_udp_sink_sends_data_over_udp
20
+ udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
21
+ udp_sink << 'foo:1|c'
22
+
23
+ datagram, _source = @receiver.recvfrom(100)
24
+ assert_equal 'foo:1|c', datagram
25
+ end
26
+
27
+ def test_sample?
28
+ udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
29
+ assert udp_sink.sample?(1)
30
+ refute udp_sink.sample?(0)
31
+
32
+ udp_sink.stubs(:rand).returns(0.3)
33
+ assert udp_sink.sample?(0.5)
34
+
35
+ udp_sink.stubs(:rand).returns(0.7)
36
+ refute udp_sink.sample?(0.5)
37
+ end
38
+
39
+ def test_parallelism
40
+ udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
41
+ 50.times { |i| Thread.new { udp_sink << "foo:#{i}|c" << "bar:#{i}|c" } }
42
+ datagrams = []
43
+ 100.times do
44
+ datagram, _source = @receiver.recvfrom(100)
45
+ datagrams << datagram
46
+ end
47
+
48
+ assert_equal 100, datagrams.size
49
+ end
50
+
51
+ def test_socket_error_should_invalidate_socket
52
+ UDPSocket.stubs(:new).returns(socket = mock('socket'))
53
+
54
+ seq = sequence('connect_fail_connect_succeed')
55
+ socket.expects(:connect).with('localhost', 8125).in_sequence(seq)
56
+ socket.expects(:send).raises(Errno::EDESTADDRREQ).in_sequence(seq)
57
+ socket.expects(:connect).with('localhost', 8125).in_sequence(seq)
58
+ socket.expects(:send).returns(1).in_sequence(seq)
59
+
60
+ udp_sink = StatsD::Instrument::UDPSink.new('localhost', 8125)
61
+ udp_sink << 'foo:1|c'
62
+ udp_sink << 'bar:1|c'
63
+ end
64
+
65
+ def test_sends_datagram_in_signal_handler
66
+ udp_sink = StatsD::Instrument::UDPSink.new(@host, @port)
67
+ pid = fork do
68
+ Signal.trap('TERM') do
69
+ udp_sink << "exiting:1|c"
70
+ Process.exit!(0)
71
+ end
72
+
73
+ sleep(10)
74
+ end
75
+
76
+ Process.kill('TERM', pid)
77
+ _, exit_status = Process.waitpid2(pid)
78
+
79
+ assert_equal 0, exit_status, "The forked process did not exit cleanly"
80
+ assert_equal "exiting:1|c", @receiver.recvfrom_nonblock(100).first
81
+
82
+ rescue NotImplementedError
83
+ pass("Fork is not implemented on #{RUBY_PLATFORM}")
84
+ end
85
+ end