statsd-instrument 2.5.1 → 2.6.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 (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