statsd-instrument 3.0.1 → 3.1.2
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/{ci.yml → tests.yml} +3 -21
- data/.rubocop.yml +2 -1
- data/CHANGELOG.md +18 -0
- data/Gemfile +8 -10
- data/README.md +7 -4
- data/Rakefile +6 -6
- data/benchmark/send-metrics-to-dev-null-log +14 -14
- data/benchmark/send-metrics-to-local-udp-receiver +18 -18
- data/lib/statsd/instrument/assertions.rb +7 -7
- data/lib/statsd/instrument/batched_udp_sink.rb +159 -0
- data/lib/statsd/instrument/client.rb +3 -3
- data/lib/statsd/instrument/datagram.rb +1 -1
- data/lib/statsd/instrument/datagram_builder.rb +10 -22
- data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +2 -2
- data/lib/statsd/instrument/environment.rb +19 -11
- data/lib/statsd/instrument/expectation.rb +6 -18
- data/lib/statsd/instrument/matchers.rb +8 -4
- data/lib/statsd/instrument/railtie.rb +1 -1
- 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 +3 -3
- 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/rubocop.rb +13 -34
- data/lib/statsd/instrument/strict.rb +1 -1
- data/lib/statsd/instrument/udp_sink.rb +11 -13
- data/lib/statsd/instrument/version.rb +1 -1
- data/lib/statsd/instrument.rb +56 -59
- data/lib/statsd-instrument.rb +1 -1
- data/statsd-instrument.gemspec +2 -0
- data/test/assertions_test.rb +200 -155
- data/test/benchmark/clock_gettime.rb +1 -1
- data/test/benchmark/metrics.rb +8 -8
- data/test/benchmark/tags.rb +4 -4
- data/test/capture_sink_test.rb +11 -11
- data/test/client_test.rb +64 -64
- data/test/datagram_builder_test.rb +41 -41
- data/test/datagram_test.rb +5 -5
- data/test/dogstatsd_datagram_builder_test.rb +22 -22
- data/test/environment_test.rb +26 -17
- data/test/helpers/rubocop_helper.rb +3 -3
- data/test/helpers_test.rb +12 -12
- data/test/integration_test.rb +6 -6
- data/test/log_sink_test.rb +2 -2
- data/test/matchers_test.rb +46 -46
- data/test/null_sink_test.rb +2 -2
- 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 +3 -3
- data/test/rubocop/metric_value_keyword_argument_test.rb +3 -3
- data/test/rubocop/positional_arguments_test.rb +2 -2
- 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 +104 -104
- data/test/statsd_test.rb +35 -35
- data/test/test_helper.rb +13 -6
- data/test/udp_sink_test.rb +142 -44
- metadata +21 -7
- data/test/benchmark/default_tags.rb +0 -47
@@ -53,9 +53,9 @@ module StatsD
|
|
53
53
|
# supported.
|
54
54
|
def datagram_builder_class_for_implementation(implementation)
|
55
55
|
case implementation.to_s
|
56
|
-
when
|
56
|
+
when "statsd"
|
57
57
|
StatsD::Instrument::StatsDDatagramBuilder
|
58
|
-
when
|
58
|
+
when "datadog", "dogstatsd"
|
59
59
|
StatsD::Instrument::DogStatsDDatagramBuilder
|
60
60
|
else
|
61
61
|
raise NotImplementedError, "Implementation named #{implementation} could not be found"
|
@@ -148,7 +148,7 @@ module StatsD
|
|
148
148
|
prefix: nil,
|
149
149
|
default_sample_rate: 1.0,
|
150
150
|
default_tags: nil,
|
151
|
-
implementation:
|
151
|
+
implementation: "datadog",
|
152
152
|
sink: StatsD::Instrument::NullSink.new,
|
153
153
|
datagram_builder_class: self.class.datagram_builder_class_for_implementation(implementation)
|
154
154
|
)
|
@@ -5,18 +5,6 @@ module StatsD
|
|
5
5
|
# @note This class is part of the new Client implementation that is intended
|
6
6
|
# to become the new default in the next major release of this library.
|
7
7
|
class DatagramBuilder
|
8
|
-
unless Regexp.method_defined?(:match?) # for ruby 2.3
|
9
|
-
module RubyBackports
|
10
|
-
refine Regexp do
|
11
|
-
def match?(str)
|
12
|
-
match(str) != nil
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
using(RubyBackports)
|
18
|
-
end
|
19
|
-
|
20
8
|
def self.unsupported_datagram_types(*types)
|
21
9
|
types.each do |type|
|
22
10
|
define_method(type) do |_, _, _, _|
|
@@ -35,31 +23,31 @@ module StatsD
|
|
35
23
|
end
|
36
24
|
|
37
25
|
def c(name, value, sample_rate, tags)
|
38
|
-
generate_generic_datagram(name, value,
|
26
|
+
generate_generic_datagram(name, value, "c", sample_rate, tags)
|
39
27
|
end
|
40
28
|
|
41
29
|
def g(name, value, sample_rate, tags)
|
42
|
-
generate_generic_datagram(name, value,
|
30
|
+
generate_generic_datagram(name, value, "g", sample_rate, tags)
|
43
31
|
end
|
44
32
|
|
45
33
|
def ms(name, value, sample_rate, tags)
|
46
|
-
generate_generic_datagram(name, value,
|
34
|
+
generate_generic_datagram(name, value, "ms", sample_rate, tags)
|
47
35
|
end
|
48
36
|
|
49
37
|
def s(name, value, sample_rate, tags)
|
50
|
-
generate_generic_datagram(name, value,
|
38
|
+
generate_generic_datagram(name, value, "s", sample_rate, tags)
|
51
39
|
end
|
52
40
|
|
53
41
|
def h(name, value, sample_rate, tags)
|
54
|
-
generate_generic_datagram(name, value,
|
42
|
+
generate_generic_datagram(name, value, "h", sample_rate, tags)
|
55
43
|
end
|
56
44
|
|
57
45
|
def d(name, value, sample_rate, tags)
|
58
|
-
generate_generic_datagram(name, value,
|
46
|
+
generate_generic_datagram(name, value, "d", sample_rate, tags)
|
59
47
|
end
|
60
48
|
|
61
49
|
def kv(name, value, sample_rate, tags)
|
62
|
-
generate_generic_datagram(name, value,
|
50
|
+
generate_generic_datagram(name, value, "kv", sample_rate, tags)
|
63
51
|
end
|
64
52
|
|
65
53
|
def latency_metric_type
|
@@ -83,21 +71,21 @@ module StatsD
|
|
83
71
|
|
84
72
|
# Fast path when no string replacement is needed
|
85
73
|
return tags unless tags.any? { |tag| /[|,]/.match?(tag) }
|
86
|
-
tags.map { |tag| tag.tr(
|
74
|
+
tags.map { |tag| tag.tr("|,", "") }
|
87
75
|
end
|
88
76
|
|
89
77
|
# Utility function to remove invalid characters from a StatsD metric name
|
90
78
|
def normalize_name(name)
|
91
79
|
# Fast path when no normalization is needed to avoid copying the string
|
92
80
|
return name unless /[:|@]/.match?(name)
|
93
|
-
name.tr(
|
81
|
+
name.tr(":|@", "_")
|
94
82
|
end
|
95
83
|
|
96
84
|
def generate_generic_datagram(name, value, type, sample_rate, tags)
|
97
85
|
tags = normalize_tags(tags) + default_tags
|
98
86
|
datagram = +"#{@prefix}#{normalize_name(name)}:#{value}|#{type}"
|
99
87
|
datagram << "|@#{sample_rate}" if sample_rate && sample_rate < 1
|
100
|
-
datagram << "|##{tags.join(
|
88
|
+
datagram << "|##{tags.join(",")}" unless tags.empty?
|
101
89
|
datagram
|
102
90
|
end
|
103
91
|
end
|
@@ -44,7 +44,7 @@ module StatsD
|
|
44
44
|
datagram << "|p:#{priority}" if priority
|
45
45
|
datagram << "|s:#{source_type_name}" if source_type_name
|
46
46
|
datagram << "|t:#{alert_type}" if alert_type
|
47
|
-
datagram << "|##{tags.join(
|
47
|
+
datagram << "|##{tags.join(",")}" unless tags.empty?
|
48
48
|
datagram
|
49
49
|
end
|
50
50
|
|
@@ -67,7 +67,7 @@ module StatsD
|
|
67
67
|
datagram = +"_sc|#{@prefix}#{normalize_name(name)}|#{status_number}"
|
68
68
|
datagram << "|h:#{hostname}" if hostname
|
69
69
|
datagram << "|d:#{timestamp.to_i}" if timestamp
|
70
|
-
datagram << "|##{tags.join(
|
70
|
+
datagram << "|##{tags.join(",")}" unless tags.empty?
|
71
71
|
datagram << "|m:#{normalize_name(message)}" if message
|
72
72
|
datagram
|
73
73
|
end
|
@@ -49,33 +49,37 @@ module StatsD
|
|
49
49
|
#
|
50
50
|
# @return [String] The detected environment.
|
51
51
|
def environment
|
52
|
-
if env[
|
53
|
-
env[
|
52
|
+
if env["STATSD_ENV"]
|
53
|
+
env["STATSD_ENV"]
|
54
54
|
elsif defined?(Rails) && Rails.respond_to?(:env)
|
55
55
|
Rails.env.to_s
|
56
56
|
else
|
57
|
-
env[
|
57
|
+
env["RAILS_ENV"] || env["RACK_ENV"] || env["ENV"] || "development"
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
61
|
def statsd_implementation
|
62
|
-
env.fetch(
|
62
|
+
env.fetch("STATSD_IMPLEMENTATION", "datadog")
|
63
63
|
end
|
64
64
|
|
65
65
|
def statsd_sample_rate
|
66
|
-
env.fetch(
|
66
|
+
env.fetch("STATSD_SAMPLE_RATE", 1.0).to_f
|
67
67
|
end
|
68
68
|
|
69
69
|
def statsd_prefix
|
70
|
-
env.fetch(
|
70
|
+
env.fetch("STATSD_PREFIX", nil)
|
71
71
|
end
|
72
72
|
|
73
73
|
def statsd_addr
|
74
|
-
env.fetch(
|
74
|
+
env.fetch("STATSD_ADDR", "localhost:8125")
|
75
75
|
end
|
76
76
|
|
77
77
|
def statsd_default_tags
|
78
|
-
env.key?(
|
78
|
+
env.key?("STATSD_DEFAULT_TAGS") ? env.fetch("STATSD_DEFAULT_TAGS").split(",") : nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def statsd_flush_interval
|
82
|
+
Float(env.fetch("STATSD_FLUSH_INTERVAL", 1.0))
|
79
83
|
end
|
80
84
|
|
81
85
|
def client
|
@@ -84,9 +88,13 @@ module StatsD
|
|
84
88
|
|
85
89
|
def default_sink_for_environment
|
86
90
|
case environment
|
87
|
-
when
|
88
|
-
|
89
|
-
|
91
|
+
when "production", "staging"
|
92
|
+
if statsd_flush_interval > 0.0
|
93
|
+
StatsD::Instrument::BatchedUDPSink.for_addr(statsd_addr, flush_interval: statsd_flush_interval)
|
94
|
+
else
|
95
|
+
StatsD::Instrument::UDPSink.for_addr(statsd_addr)
|
96
|
+
end
|
97
|
+
when "test"
|
90
98
|
StatsD::Instrument::NullSink.new
|
91
99
|
else
|
92
100
|
StatsD::Instrument::LogSink.new(StatsD.logger)
|
@@ -32,11 +32,12 @@ module StatsD
|
|
32
32
|
|
33
33
|
attr_accessor :times, :type, :name, :value, :sample_rate, :tags
|
34
34
|
|
35
|
-
def initialize(client:
|
35
|
+
def initialize(client: nil, type:, name:, value: nil,
|
36
36
|
sample_rate: nil, tags: nil, no_prefix: false, times: 1)
|
37
37
|
|
38
|
+
client ||= StatsD.singleton_client
|
38
39
|
@type = type
|
39
|
-
@name = client.prefix ? "#{client.prefix}.#{name}"
|
40
|
+
@name = no_prefix || !client.prefix ? name : "#{client.prefix}.#{name}"
|
40
41
|
@value = normalized_value_for_type(type, value) if value
|
41
42
|
@sample_rate = sample_rate
|
42
43
|
@tags = normalize_tags(tags)
|
@@ -65,9 +66,9 @@ module StatsD
|
|
65
66
|
end
|
66
67
|
|
67
68
|
def to_s
|
68
|
-
str = +"#{name}:#{value ||
|
69
|
+
str = +"#{name}:#{value || "<anything>"}|#{type}"
|
69
70
|
str << "|@#{sample_rate}" if sample_rate
|
70
|
-
str << "|#" << tags.join(
|
71
|
+
str << "|#" << tags.join(",") if tags
|
71
72
|
str << " (expected #{times} times)" if times > 1
|
72
73
|
str
|
73
74
|
end
|
@@ -78,19 +79,6 @@ module StatsD
|
|
78
79
|
|
79
80
|
private
|
80
81
|
|
81
|
-
# Needed for normalize_tags
|
82
|
-
unless Regexp.method_defined?(:match?) # for ruby 2.3
|
83
|
-
module RubyBackports
|
84
|
-
refine Regexp do
|
85
|
-
def match?(str)
|
86
|
-
(self =~ str) != nil
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
using(RubyBackports)
|
92
|
-
end
|
93
|
-
|
94
82
|
# @private
|
95
83
|
#
|
96
84
|
# Utility function to convert tags to the canonical form.
|
@@ -109,7 +97,7 @@ module StatsD
|
|
109
97
|
|
110
98
|
# Fast path when no string replacement is needed
|
111
99
|
return tags unless tags.any? { |tag| /[|,]/.match?(tag) }
|
112
|
-
tags.map { |tag| tag.tr(
|
100
|
+
tags.map { |tag| tag.tr("|,", "") }
|
113
101
|
end
|
114
102
|
end
|
115
103
|
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "rspec/expectations"
|
4
|
+
require "rspec/core/version"
|
5
5
|
|
6
6
|
module StatsD
|
7
7
|
module Instrument
|
8
8
|
module Matchers
|
9
9
|
class Matcher
|
10
|
-
include(RSpec::Matchers::Composable) if RSpec::Core::Version::STRING.start_with?(
|
10
|
+
include(RSpec::Matchers::Composable) if RSpec::Core::Version::STRING.start_with?("3")
|
11
11
|
include StatsD::Instrument::Helpers
|
12
12
|
|
13
13
|
def initialize(metric_type, metric_name, options = {})
|
@@ -35,6 +35,10 @@ module StatsD
|
|
35
35
|
true
|
36
36
|
end
|
37
37
|
|
38
|
+
def description
|
39
|
+
"trigger a statsd call for metric #{@metric_name}"
|
40
|
+
end
|
41
|
+
|
38
42
|
private
|
39
43
|
|
40
44
|
def expect_statsd_call(metric_type, metric_name, options, &block)
|
@@ -74,7 +78,7 @@ module StatsD
|
|
74
78
|
message += options[:times] ? "exactly #{options[:times]} times" : "at least once"
|
75
79
|
message += " with: #{options[expectation]}"
|
76
80
|
|
77
|
-
message += "\n captured metric values: #{metrics.map(&expectation).join(
|
81
|
+
message += "\n captured metric values: #{metrics.map(&expectation).join(", ")}"
|
78
82
|
|
79
83
|
message
|
80
84
|
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
|
@@ -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,9 +17,9 @@ 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
|
-
INVALID_PARENTS =
|
22
|
+
INVALID_PARENTS = [:lvasgn, :array, :pair, :send, :return, :yield]
|
23
23
|
|
24
24
|
def on_send(node)
|
25
25
|
if metric_method?(node) && node.arguments.last&.type != :block_pass
|
@@ -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)
|
@@ -3,34 +3,13 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module StatsD
|
6
|
-
METRIC_METHODS =
|
7
|
-
increment
|
8
|
-
gauge
|
9
|
-
measure
|
10
|
-
set
|
11
|
-
histogram
|
12
|
-
distribution
|
13
|
-
key_value
|
14
|
-
}
|
6
|
+
METRIC_METHODS = [:increment, :gauge, :measure, :set, :histogram, :distribution, :key_value]
|
15
7
|
|
16
|
-
METAPROGRAMMING_METHODS =
|
17
|
-
|
18
|
-
statsd_distribution
|
19
|
-
statsd_count_success
|
20
|
-
statsd_count_if
|
21
|
-
statsd_count
|
22
|
-
}
|
8
|
+
METAPROGRAMMING_METHODS = [:statsd_measure, :statsd_distribution, :statsd_count_success, :statsd_count_if,
|
9
|
+
:statsd_count,]
|
23
10
|
|
24
|
-
SINGLETON_CONFIGURATION_METHODS =
|
25
|
-
|
26
|
-
backend=
|
27
|
-
prefix
|
28
|
-
prefix=
|
29
|
-
default_tags
|
30
|
-
default_tags=
|
31
|
-
default_sample_rate
|
32
|
-
default_sample_rate=
|
33
|
-
}
|
11
|
+
SINGLETON_CONFIGURATION_METHODS = [:backend, :"backend=", :prefix, :"prefix=", :default_tags, :"default_tags=",
|
12
|
+
:default_sample_rate, :"default_sample_rate=",]
|
34
13
|
|
35
14
|
private
|
36
15
|
|
@@ -72,11 +51,11 @@ module RuboCop
|
|
72
51
|
end
|
73
52
|
end
|
74
53
|
|
75
|
-
require_relative
|
76
|
-
require_relative
|
77
|
-
require_relative
|
78
|
-
require_relative
|
79
|
-
require_relative
|
80
|
-
require_relative
|
81
|
-
require_relative
|
82
|
-
require_relative
|
54
|
+
require_relative "rubocop/metaprogramming_positional_arguments"
|
55
|
+
require_relative "rubocop/metric_return_value"
|
56
|
+
require_relative "rubocop/metric_value_keyword_argument"
|
57
|
+
require_relative "rubocop/positional_arguments"
|
58
|
+
require_relative "rubocop/splat_arguments"
|
59
|
+
require_relative "rubocop/measure_as_dist_argument"
|
60
|
+
require_relative "rubocop/metric_prefix_argument"
|
61
|
+
require_relative "rubocop/singleton_configuration"
|