statsd-instrument 3.0.2 → 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/{ci.yml → tests.yml} +3 -21
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/Gemfile +8 -10
- data/README.md +3 -0
- data/Rakefile +6 -6
- data/benchmark/send-metrics-to-dev-null-log +12 -12
- data/benchmark/send-metrics-to-local-udp-receiver +16 -16
- data/lib/statsd-instrument.rb +1 -1
- data/lib/statsd/instrument.rb +56 -59
- data/lib/statsd/instrument/assertions.rb +1 -1
- data/lib/statsd/instrument/batched_udp_sink.rb +154 -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 -10
- data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +2 -2
- data/lib/statsd/instrument/environment.rb +19 -11
- data/lib/statsd/instrument/expectation.rb +3 -3
- data/lib/statsd/instrument/matchers.rb +8 -4
- data/lib/statsd/instrument/railtie.rb +1 -1
- 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/strict.rb +1 -1
- data/lib/statsd/instrument/udp_sink.rb +10 -12
- data/lib/statsd/instrument/version.rb +1 -1
- data/statsd-instrument.gemspec +2 -0
- data/test/assertions_test.rb +167 -169
- 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 +11 -11
- data/test/client_test.rb +64 -64
- data/test/datagram_builder_test.rb +40 -40
- 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 +2 -2
- 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 +2 -2
- 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 +117 -45
- metadata +20 -4
@@ -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
|
)
|
@@ -35,31 +35,31 @@ module StatsD
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def c(name, value, sample_rate, tags)
|
38
|
-
generate_generic_datagram(name, value,
|
38
|
+
generate_generic_datagram(name, value, "c", sample_rate, tags)
|
39
39
|
end
|
40
40
|
|
41
41
|
def g(name, value, sample_rate, tags)
|
42
|
-
generate_generic_datagram(name, value,
|
42
|
+
generate_generic_datagram(name, value, "g", sample_rate, tags)
|
43
43
|
end
|
44
44
|
|
45
45
|
def ms(name, value, sample_rate, tags)
|
46
|
-
generate_generic_datagram(name, value,
|
46
|
+
generate_generic_datagram(name, value, "ms", sample_rate, tags)
|
47
47
|
end
|
48
48
|
|
49
49
|
def s(name, value, sample_rate, tags)
|
50
|
-
generate_generic_datagram(name, value,
|
50
|
+
generate_generic_datagram(name, value, "s", sample_rate, tags)
|
51
51
|
end
|
52
52
|
|
53
53
|
def h(name, value, sample_rate, tags)
|
54
|
-
generate_generic_datagram(name, value,
|
54
|
+
generate_generic_datagram(name, value, "h", sample_rate, tags)
|
55
55
|
end
|
56
56
|
|
57
57
|
def d(name, value, sample_rate, tags)
|
58
|
-
generate_generic_datagram(name, value,
|
58
|
+
generate_generic_datagram(name, value, "d", sample_rate, tags)
|
59
59
|
end
|
60
60
|
|
61
61
|
def kv(name, value, sample_rate, tags)
|
62
|
-
generate_generic_datagram(name, value,
|
62
|
+
generate_generic_datagram(name, value, "kv", sample_rate, tags)
|
63
63
|
end
|
64
64
|
|
65
65
|
def latency_metric_type
|
@@ -83,21 +83,21 @@ module StatsD
|
|
83
83
|
|
84
84
|
# Fast path when no string replacement is needed
|
85
85
|
return tags unless tags.any? { |tag| /[|,]/.match?(tag) }
|
86
|
-
tags.map { |tag| tag.tr(
|
86
|
+
tags.map { |tag| tag.tr("|,", "") }
|
87
87
|
end
|
88
88
|
|
89
89
|
# Utility function to remove invalid characters from a StatsD metric name
|
90
90
|
def normalize_name(name)
|
91
91
|
# Fast path when no normalization is needed to avoid copying the string
|
92
92
|
return name unless /[:|@]/.match?(name)
|
93
|
-
name.tr(
|
93
|
+
name.tr(":|@", "_")
|
94
94
|
end
|
95
95
|
|
96
96
|
def generate_generic_datagram(name, value, type, sample_rate, tags)
|
97
97
|
tags = normalize_tags(tags) + default_tags
|
98
98
|
datagram = +"#{@prefix}#{normalize_name(name)}:#{value}|#{type}"
|
99
99
|
datagram << "|@#{sample_rate}" if sample_rate && sample_rate < 1
|
100
|
-
datagram << "|##{tags.join(
|
100
|
+
datagram << "|##{tags.join(",")}" unless tags.empty?
|
101
101
|
datagram
|
102
102
|
end
|
103
103
|
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)
|
@@ -65,9 +65,9 @@ module StatsD
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def to_s
|
68
|
-
str = +"#{name}:#{value ||
|
68
|
+
str = +"#{name}:#{value || "<anything>"}|#{type}"
|
69
69
|
str << "|@#{sample_rate}" if sample_rate
|
70
|
-
str << "|#" << tags.join(
|
70
|
+
str << "|#" << tags.join(",") if tags
|
71
71
|
str << " (expected #{times} times)" if times > 1
|
72
72
|
str
|
73
73
|
end
|
@@ -109,7 +109,7 @@ module StatsD
|
|
109
109
|
|
110
110
|
# Fast path when no string replacement is needed
|
111
111
|
return tags unless tags.any? { |tag| /[|,]/.match?(tag) }
|
112
|
-
tags.map { |tag| tag.tr(
|
112
|
+
tags.map { |tag| tag.tr("|,", "") }
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
@@ -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
|
@@ -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)
|
@@ -6,7 +6,7 @@ module StatsD
|
|
6
6
|
# to become the new default in the next major release of this library.
|
7
7
|
class UDPSink
|
8
8
|
def self.for_addr(addr)
|
9
|
-
host, port_as_string = addr.split(
|
9
|
+
host, port_as_string = addr.split(":", 2)
|
10
10
|
new(host, Integer(port_as_string))
|
11
11
|
end
|
12
12
|
|
@@ -24,7 +24,7 @@ module StatsD
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def <<(datagram)
|
27
|
-
with_socket { |socket| socket.send(datagram, 0)
|
27
|
+
with_socket { |socket| socket.send(datagram, 0) }
|
28
28
|
self
|
29
29
|
|
30
30
|
rescue ThreadError
|
@@ -33,15 +33,13 @@ module StatsD
|
|
33
33
|
# to try and send the datagram without a lock.
|
34
34
|
socket.send(datagram, 0) > 0
|
35
35
|
|
36
|
-
rescue SocketError, IOError, SystemCallError
|
37
|
-
|
36
|
+
rescue SocketError, IOError, SystemCallError => error
|
37
|
+
StatsD.logger.debug do
|
38
|
+
"[StatsD::Instrument::UDPSink] Resetting connection because of #{error.class}: #{error.message}"
|
39
|
+
end
|
38
40
|
invalidate_socket
|
39
41
|
end
|
40
42
|
|
41
|
-
def addr
|
42
|
-
"#{host}:#{port}"
|
43
|
-
end
|
44
|
-
|
45
43
|
private
|
46
44
|
|
47
45
|
def with_socket
|
@@ -49,11 +47,11 @@ module StatsD
|
|
49
47
|
end
|
50
48
|
|
51
49
|
def socket
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
@socket ||= begin
|
51
|
+
socket = UDPSocket.new
|
52
|
+
socket.connect(@host, @port)
|
53
|
+
socket
|
55
54
|
end
|
56
|
-
@socket
|
57
55
|
end
|
58
56
|
|
59
57
|
def invalidate_socket
|