statsd-instrument 3.0.0.pre1 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/lint.yml +22 -0
- data/.github/workflows/tests.yml +31 -0
- data/.rubocop.yml +3 -13
- data/CHANGELOG.md +50 -0
- data/Gemfile +8 -2
- data/README.md +6 -3
- data/Rakefile +7 -7
- data/benchmark/send-metrics-to-dev-null-log +12 -11
- data/benchmark/send-metrics-to-local-udp-receiver +16 -15
- data/bin/rake +29 -0
- data/bin/rubocop +29 -0
- data/lib/statsd-instrument.rb +1 -1
- data/lib/statsd/instrument.rb +112 -145
- data/lib/statsd/instrument/assertions.rb +200 -208
- data/lib/statsd/instrument/batched_udp_sink.rb +154 -0
- data/lib/statsd/instrument/capture_sink.rb +23 -19
- data/lib/statsd/instrument/client.rb +410 -306
- data/lib/statsd/instrument/datagram.rb +69 -65
- data/lib/statsd/instrument/datagram_builder.rb +81 -77
- data/lib/statsd/instrument/dogstatsd_datagram.rb +76 -72
- data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +68 -64
- data/lib/statsd/instrument/environment.rb +88 -77
- data/lib/statsd/instrument/expectation.rb +96 -96
- data/lib/statsd/instrument/helpers.rb +11 -7
- data/lib/statsd/instrument/log_sink.rb +20 -16
- data/lib/statsd/instrument/matchers.rb +93 -74
- data/lib/statsd/instrument/null_sink.rb +12 -8
- data/lib/statsd/instrument/railtie.rb +11 -7
- data/lib/statsd/instrument/rubocop.rb +8 -8
- data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +1 -1
- data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +2 -2
- data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +1 -1
- data/lib/statsd/instrument/rubocop/metric_return_value.rb +2 -2
- data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +1 -1
- data/lib/statsd/instrument/rubocop/positional_arguments.rb +4 -4
- data/lib/statsd/instrument/rubocop/singleton_configuration.rb +1 -1
- data/lib/statsd/instrument/rubocop/splat_arguments.rb +2 -2
- data/lib/statsd/instrument/statsd_datagram_builder.rb +12 -8
- data/lib/statsd/instrument/strict.rb +1 -6
- data/lib/statsd/instrument/udp_sink.rb +49 -47
- data/lib/statsd/instrument/version.rb +1 -1
- data/statsd-instrument.gemspec +4 -8
- data/test/assertions_test.rb +205 -161
- data/test/benchmark/clock_gettime.rb +1 -1
- data/test/benchmark/default_tags.rb +9 -9
- data/test/benchmark/metrics.rb +8 -8
- data/test/benchmark/tags.rb +4 -4
- data/test/capture_sink_test.rb +14 -14
- data/test/client_test.rb +96 -96
- data/test/datagram_builder_test.rb +55 -55
- data/test/datagram_test.rb +5 -5
- data/test/dogstatsd_datagram_builder_test.rb +37 -37
- data/test/environment_test.rb +30 -21
- data/test/helpers/rubocop_helper.rb +12 -9
- data/test/helpers_test.rb +15 -15
- data/test/integration_test.rb +7 -7
- data/test/log_sink_test.rb +4 -4
- data/test/matchers_test.rb +54 -54
- data/test/null_sink_test.rb +4 -4
- data/test/rubocop/measure_as_dist_argument_test.rb +2 -2
- data/test/rubocop/metaprogramming_positional_arguments_test.rb +2 -2
- data/test/rubocop/metric_prefix_argument_test.rb +2 -2
- data/test/rubocop/metric_return_value_test.rb +6 -6
- data/test/rubocop/metric_value_keyword_argument_test.rb +3 -3
- data/test/rubocop/positional_arguments_test.rb +12 -12
- data/test/rubocop/singleton_configuration_test.rb +8 -8
- data/test/rubocop/splat_arguments_test.rb +2 -2
- data/test/statsd_datagram_builder_test.rb +6 -6
- data/test/statsd_instrumentation_test.rb +122 -122
- data/test/statsd_test.rb +69 -67
- data/test/test_helper.rb +19 -10
- data/test/udp_sink_test.rb +122 -50
- metadata +12 -92
- data/.github/workflows/ci.yml +0 -56
- 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
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
10
|
-
|
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
|
-
|
4
|
-
|
5
|
-
class
|
6
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
def initialize(logger, severity: Logger::DEBUG)
|
11
|
+
@logger = logger
|
12
|
+
@severity = severity
|
13
|
+
end
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
def sample?(_sample_rate)
|
16
|
+
true
|
17
|
+
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
+
logger.add(severity, "[StatsD] #{datagram.chomp}")
|
24
|
+
self
|
25
|
+
end
|
26
|
+
end
|
23
27
|
end
|
24
28
|
end
|
@@ -1,100 +1,119 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
|
6
|
-
module StatsD
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
include RSpec::Matchers::Composable if RSpec::Core::Version::STRING.start_with?('3')
|
19
|
-
include StatsD::Instrument::Helpers
|
20
|
-
|
21
|
-
def initialize(metric_type, metric_name, options = {})
|
22
|
-
@metric_type = metric_type
|
23
|
-
@metric_name = metric_name
|
24
|
-
@options = options
|
25
|
-
end
|
3
|
+
require "rspec/expectations"
|
4
|
+
require "rspec/core/version"
|
5
|
+
|
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
|
26
18
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
33
25
|
|
34
|
-
|
35
|
-
|
36
|
-
|
26
|
+
def failure_message
|
27
|
+
@message
|
28
|
+
end
|
37
29
|
|
38
|
-
|
39
|
-
|
40
|
-
|
30
|
+
def failure_message_when_negated
|
31
|
+
"No StatsD calls for metric #{@metric_name} expected."
|
32
|
+
end
|
41
33
|
|
42
|
-
|
43
|
-
|
44
|
-
|
34
|
+
def supports_block_expectations?
|
35
|
+
true
|
36
|
+
end
|
45
37
|
|
46
|
-
|
38
|
+
def description
|
39
|
+
"trigger a statsd call for metric #{@metric_name}"
|
40
|
+
end
|
47
41
|
|
48
|
-
|
49
|
-
metrics = capture_statsd_calls(&block)
|
50
|
-
metrics = metrics.select { |m| m.type == metric_type && m.name == metric_name }
|
42
|
+
private
|
51
43
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
44
|
+
def expect_statsd_call(metric_type, metric_name, options, &block)
|
45
|
+
metrics = capture_statsd_calls(&block)
|
46
|
+
metrics = metrics.select { |m| m.type == metric_type && m.name == metric_name }
|
47
|
+
|
48
|
+
if metrics.empty?
|
49
|
+
raise RSpec::Expectations::ExpectationNotMetError, "No StatsD calls for metric #{metric_name} were made."
|
50
|
+
elsif options[:times] && options[:times] != metrics.length
|
51
|
+
raise RSpec::Expectations::ExpectationNotMetError, "The numbers of StatsD calls for metric " \
|
52
|
+
"#{metric_name} was unexpected. Expected #{options[:times].inspect}, got #{metrics.length}"
|
53
|
+
end
|
54
|
+
|
55
|
+
[:sample_rate, :value, :tags].each do |expectation|
|
56
|
+
next unless options[expectation]
|
58
57
|
|
59
|
-
|
60
|
-
|
58
|
+
num_matches = metrics.count do |m|
|
59
|
+
matcher = RSpec::Matchers::BuiltIn::Match.new(options[expectation])
|
60
|
+
matcher.matches?(m.public_send(expectation))
|
61
|
+
end
|
61
62
|
|
62
|
-
|
63
|
-
|
64
|
-
|
63
|
+
found = options[:times] ? num_matches == options[:times] : num_matches > 0
|
64
|
+
|
65
|
+
unless found
|
66
|
+
message = metric_information(metric_name, options, metrics, expectation)
|
67
|
+
raise RSpec::Expectations::ExpectationNotMetError, message
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
true
|
65
72
|
end
|
66
73
|
|
67
|
-
|
74
|
+
def metric_information(metric_name, options, metrics, expectation)
|
75
|
+
message = "expected StatsD #{expectation.inspect} for metric '#{metric_name}' to be called"
|
76
|
+
|
77
|
+
message += "\n "
|
78
|
+
message += options[:times] ? "exactly #{options[:times]} times" : "at least once"
|
79
|
+
message += " with: #{options[expectation]}"
|
80
|
+
|
81
|
+
message += "\n captured metric values: #{metrics.map(&expectation).join(", ")}"
|
68
82
|
|
69
|
-
|
70
|
-
message = metric_information(metric_name, options, metrics, expectation)
|
71
|
-
raise RSpec::Expectations::ExpectationNotMetError, message
|
83
|
+
message
|
72
84
|
end
|
73
85
|
end
|
74
86
|
|
75
|
-
|
76
|
-
|
87
|
+
Increment = Class.new(Matcher)
|
88
|
+
Measure = Class.new(Matcher)
|
89
|
+
Gauge = Class.new(Matcher)
|
90
|
+
Set = Class.new(Matcher)
|
91
|
+
Histogram = Class.new(Matcher)
|
92
|
+
Distribution = Class.new(Matcher)
|
77
93
|
|
78
|
-
|
79
|
-
|
94
|
+
def trigger_statsd_increment(metric_name, options = {})
|
95
|
+
Increment.new(:c, metric_name, options)
|
96
|
+
end
|
80
97
|
|
81
|
-
|
82
|
-
|
83
|
-
|
98
|
+
def trigger_statsd_measure(metric_name, options = {})
|
99
|
+
Measure.new(:ms, metric_name, options)
|
100
|
+
end
|
84
101
|
|
85
|
-
|
102
|
+
def trigger_statsd_gauge(metric_name, options = {})
|
103
|
+
Gauge.new(:g, metric_name, options)
|
104
|
+
end
|
86
105
|
|
87
|
-
|
88
|
-
|
89
|
-
|
106
|
+
def trigger_statsd_set(metric_name, options = {})
|
107
|
+
Set.new(:s, metric_name, options)
|
108
|
+
end
|
90
109
|
|
91
|
-
|
92
|
-
|
110
|
+
def trigger_statsd_histogram(metric_name, options = {})
|
111
|
+
Histogram.new(:h, metric_name, options)
|
112
|
+
end
|
93
113
|
|
94
|
-
|
95
|
-
|
114
|
+
def trigger_statsd_distribution(metric_name, options = {})
|
115
|
+
Distribution.new(:d, metric_name, options)
|
116
|
+
end
|
96
117
|
end
|
97
|
-
|
98
|
-
StatsD::Instrument::Matchers.const_set(method_name.capitalize, klass)
|
99
118
|
end
|
100
119
|
end
|
@@ -1,13 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
11
|
-
|
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
|
-
|
4
|
-
|
5
|
-
#
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
@@ -72,11 +72,11 @@ module RuboCop
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
require_relative
|
76
|
-
require_relative
|
77
|
-
require_relative
|
78
|
-
require_relative
|
79
|
-
require_relative
|
80
|
-
require_relative
|
81
|
-
require_relative
|
82
|
-
require_relative
|
75
|
+
require_relative "rubocop/metaprogramming_positional_arguments"
|
76
|
+
require_relative "rubocop/metric_return_value"
|
77
|
+
require_relative "rubocop/metric_value_keyword_argument"
|
78
|
+
require_relative "rubocop/positional_arguments"
|
79
|
+
require_relative "rubocop/splat_arguments"
|
80
|
+
require_relative "rubocop/measure_as_dist_argument"
|
81
|
+
require_relative "rubocop/metric_prefix_argument"
|
82
|
+
require_relative "rubocop/singleton_configuration"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
|
4
4
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
@@ -19,7 +19,7 @@ module RuboCop
|
|
19
19
|
class MetaprogrammingPositionalArguments < Cop
|
20
20
|
include RuboCop::Cop::StatsD
|
21
21
|
|
22
|
-
MSG =
|
22
|
+
MSG = "Use keyword arguments for StatsD metaprogramming macros"
|
23
23
|
|
24
24
|
def on_send(node)
|
25
25
|
if metaprogramming_method?(node)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
|
4
4
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
@@ -17,7 +17,7 @@ module RuboCop
|
|
17
17
|
class MetricReturnValue < Cop
|
18
18
|
include RuboCop::Cop::StatsD
|
19
19
|
|
20
|
-
MSG =
|
20
|
+
MSG = "Do not use the return value of StatsD metric methods"
|
21
21
|
|
22
22
|
INVALID_PARENTS = %i{lvasgn array pair send return yield}
|
23
23
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
|
4
4
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
@@ -17,7 +17,7 @@ module RuboCop
|
|
17
17
|
class PositionalArguments < Cop
|
18
18
|
include RuboCop::Cop::StatsD
|
19
19
|
|
20
|
-
MSG =
|
20
|
+
MSG = "Use keyword arguments for StatsD calls"
|
21
21
|
|
22
22
|
POSITIONAL_ARGUMENT_TYPES = Set[:int, :float, :nil]
|
23
23
|
UNKNOWN_ARGUMENT_TYPES = Set[:send, :const, :lvar, :splat]
|
@@ -80,7 +80,7 @@ module RuboCop
|
|
80
80
|
|
81
81
|
tags = positional_arguments[1]
|
82
82
|
if tags && tags.type != :nil
|
83
|
-
keyword_arguments << if tags.type == :hash && tags.source[0] !=
|
83
|
+
keyword_arguments << if tags.type == :hash && tags.source[0] != "{"
|
84
84
|
"tags: { #{tags.source} }"
|
85
85
|
else
|
86
86
|
"tags: #{tags.source}"
|
@@ -88,7 +88,7 @@ module RuboCop
|
|
88
88
|
end
|
89
89
|
|
90
90
|
unless keyword_arguments.empty?
|
91
|
-
corrector.insert_after(value_argument.source_range, ", #{keyword_arguments.join(
|
91
|
+
corrector.insert_after(value_argument.source_range, ", #{keyword_arguments.join(", ")}")
|
92
92
|
end
|
93
93
|
end
|
94
94
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "../rubocop" unless defined?(RuboCop::Cop::StatsD)
|
4
4
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
@@ -16,7 +16,7 @@ module RuboCop
|
|
16
16
|
class SplatArguments < Cop
|
17
17
|
include RuboCop::Cop::StatsD
|
18
18
|
|
19
|
-
MSG =
|
19
|
+
MSG = "Do not use splat arguments in StatsD metric calls"
|
20
20
|
|
21
21
|
def on_send(node)
|
22
22
|
if metric_method?(node)
|