statsd-instrument 2.8.0 → 2.9.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/ci.yml +16 -7
- data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +6 -6
- data/CHANGELOG.md +28 -6
- data/CONTRIBUTING.md +3 -3
- data/README.md +11 -11
- data/lib/statsd/instrument.rb +33 -18
- data/lib/statsd/instrument/assertions.rb +7 -7
- data/lib/statsd/instrument/backend.rb +1 -1
- data/lib/statsd/instrument/client.rb +11 -10
- data/lib/statsd/instrument/datagram.rb +1 -2
- data/lib/statsd/instrument/datagram_builder.rb +1 -1
- data/lib/statsd/instrument/dogstatsd_datagram.rb +88 -0
- data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +4 -0
- data/lib/statsd/instrument/expectation.rb +37 -1
- data/lib/statsd/instrument/legacy_client.rb +5 -5
- data/lib/statsd/instrument/metric.rb +2 -2
- data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +1 -1
- data/lib/statsd/instrument/rubocop/positional_arguments.rb +6 -6
- data/lib/statsd/instrument/rubocop/singleton_configuration.rb +1 -1
- data/lib/statsd/instrument/strict.rb +37 -17
- data/lib/statsd/instrument/version.rb +1 -1
- data/test/assertions_on_legacy_client_test.rb +1 -1
- data/test/assertions_test.rb +26 -1
- data/test/capture_sink_test.rb +1 -1
- data/test/client_test.rb +5 -5
- data/test/compatibility/dogstatsd_datagram_compatibility_test.rb +1 -1
- data/test/datagram_builder_test.rb +1 -1
- data/test/deprecations_test.rb +8 -1
- data/test/dogstatsd_datagram_builder_test.rb +41 -4
- data/test/environment_test.rb +1 -1
- data/test/integration_test.rb +1 -1
- data/test/log_sink_test.rb +1 -1
- data/test/null_sink_test.rb +1 -1
- data/test/rubocop/metric_prefix_argument_test.rb +1 -1
- data/test/rubocop/positional_arguments_test.rb +3 -3
- data/test/statsd_instrumentation_test.rb +11 -0
- data/test/statsd_test.rb +2 -9
- data/test/udp_backend_test.rb +3 -0
- data/test/udp_sink_test.rb +1 -1
- metadata +3 -2
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# The Datagram class parses and inspects a StatsD
|
3
|
+
# The Datagram class parses and inspects a StatsD datagrams
|
4
4
|
#
|
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.
|
@@ -72,7 +72,6 @@ class StatsD::Instrument::Datagram
|
|
72
72
|
\n? # In some implementations, the datagram may include a trailing newline.
|
73
73
|
\z
|
74
74
|
}x
|
75
|
-
private_constant :PARSER
|
76
75
|
|
77
76
|
def parsed_datagram
|
78
77
|
@parsed ||= if (match_info = PARSER.match(@source))
|
@@ -18,7 +18,7 @@ class StatsD::Instrument::DatagramBuilder
|
|
18
18
|
def self.unsupported_datagram_types(*types)
|
19
19
|
types.each do |type|
|
20
20
|
define_method(type) do |_, _, _, _|
|
21
|
-
raise NotImplementedError, "Type #{type} metrics are not
|
21
|
+
raise NotImplementedError, "Type #{type} metrics are not supported by #{self.class.name}."
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The Datagram class parses and inspects a StatsD datagrams
|
4
|
+
#
|
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 StatsD::Instrument::DogStatsDDatagram < StatsD::Instrument::Datagram
|
8
|
+
def name
|
9
|
+
@name ||= case type
|
10
|
+
when :_e then parsed_datagram[:name].gsub('\n', "\n")
|
11
|
+
else super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def value
|
16
|
+
@value ||= case type
|
17
|
+
when :_sc then Integer(parsed_datagram[:value])
|
18
|
+
when :_e then parsed_datagram[:value].gsub('\n', "\n")
|
19
|
+
else super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def hostname
|
24
|
+
parsed_datagram[:hostname]
|
25
|
+
end
|
26
|
+
|
27
|
+
def timestamp
|
28
|
+
Time.at(Integer(parsed_datagram[:timestamp])).utc
|
29
|
+
end
|
30
|
+
|
31
|
+
def aggregation_key
|
32
|
+
parsed_datagram[:aggregation_key]
|
33
|
+
end
|
34
|
+
|
35
|
+
def source_type_name
|
36
|
+
parsed_datagram[:source_type_name]
|
37
|
+
end
|
38
|
+
|
39
|
+
def priority
|
40
|
+
parsed_datagram[:priority]
|
41
|
+
end
|
42
|
+
|
43
|
+
def alert_type
|
44
|
+
parsed_datagram[:alert_type]
|
45
|
+
end
|
46
|
+
|
47
|
+
def message
|
48
|
+
parsed_datagram[:message]
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def parsed_datagram
|
54
|
+
@parsed ||= if (match_info = PARSER.match(@source))
|
55
|
+
match_info
|
56
|
+
else
|
57
|
+
raise ArgumentError, "Invalid DogStatsD datagram: #{@source}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
SERVICE_CHECK_PARSER = %r{
|
62
|
+
\A
|
63
|
+
(?<type>_sc)\|(?<name>[^\|]+)\|(?<value>\d+)
|
64
|
+
(?:\|h:(?<hostname>[^\|]+))?
|
65
|
+
(?:\|d:(?<timestamp>\d+))?
|
66
|
+
(?:\|\#(?<tags>(?:[^\|,]+(?:,[^\|,]+)*)))?
|
67
|
+
(?:\|m:(?<message>[^\|]+))?
|
68
|
+
\n? # In some implementations, the datagram may include a trailing newline.
|
69
|
+
\z
|
70
|
+
}x
|
71
|
+
|
72
|
+
# |k:my-key|p:low|s:source|t:success|
|
73
|
+
EVENT_PARSER = %r{
|
74
|
+
\A
|
75
|
+
(?<type>_e)\{\d+\,\d+\}:(?<name>[^\|]+)\|(?<value>[^\|]+)
|
76
|
+
(?:\|h:(?<hostname>[^\|]+))?
|
77
|
+
(?:\|d:(?<timestamp>\d+))?
|
78
|
+
(?:\|k:(?<aggregation_key>[^\|]+))?
|
79
|
+
(?:\|p:(?<priority>[^\|]+))?
|
80
|
+
(?:\|s:(?<source_type_name>[^\|]+))?
|
81
|
+
(?:\|t:(?<alert_type>[^\|]+))?
|
82
|
+
(?:\|\#(?<tags>(?:[^\|,]+(?:,[^\|,]+)*)))?
|
83
|
+
\n? # In some implementations, the datagram may include a trailing newline.
|
84
|
+
\z
|
85
|
+
}x
|
86
|
+
|
87
|
+
PARSER = Regexp.union(StatsD::Instrument::Datagram::PARSER, SERVICE_CHECK_PARSER, EVENT_PARSER)
|
88
|
+
end
|
@@ -41,7 +41,7 @@ class StatsD::Instrument::Expectation
|
|
41
41
|
@name = client.prefix ? "#{client.prefix}.#{name}" : name unless no_prefix
|
42
42
|
@value = normalized_value_for_type(type, value) if value
|
43
43
|
@sample_rate = sample_rate
|
44
|
-
@tags =
|
44
|
+
@tags = normalize_tags(tags)
|
45
45
|
@times = times
|
46
46
|
end
|
47
47
|
|
@@ -77,6 +77,42 @@ class StatsD::Instrument::Expectation
|
|
77
77
|
def inspect
|
78
78
|
"#<StatsD::Instrument::Expectation:\"#{self}\">"
|
79
79
|
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# Needed for normalize_tags
|
84
|
+
unless Regexp.method_defined?(:match?) # for ruby 2.3
|
85
|
+
module RubyBackports
|
86
|
+
refine Regexp do
|
87
|
+
def match?(str)
|
88
|
+
(self =~ str) != nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
using RubyBackports
|
94
|
+
end
|
95
|
+
|
96
|
+
# @private
|
97
|
+
#
|
98
|
+
# Utility function to convert tags to the canonical form.
|
99
|
+
#
|
100
|
+
# - Tags specified as key value pairs will be converted into an array
|
101
|
+
# - Tags are normalized to remove unsupported characters
|
102
|
+
#
|
103
|
+
# @param tags [Array<String>, Hash<String, String>, nil] Tags specified in any form.
|
104
|
+
# @return [Array<String>, nil] the list of tags in canonical form.
|
105
|
+
#
|
106
|
+
# @todo We should delegate this to thje datagram builder of the current client,
|
107
|
+
# to ensure that this logic matches the logic of the active datagram builder.
|
108
|
+
def normalize_tags(tags)
|
109
|
+
return [] unless tags
|
110
|
+
tags = tags.map { |k, v| "#{k}:#{v}" } if tags.is_a?(Hash)
|
111
|
+
|
112
|
+
# Fast path when no string replacement is needed
|
113
|
+
return tags unless tags.any? { |tag| /[|,]/.match?(tag) }
|
114
|
+
tags.map { |tag| tag.tr('|,', '') }
|
115
|
+
end
|
80
116
|
end
|
81
117
|
|
82
118
|
# For backwards compatibility
|
@@ -120,7 +120,7 @@ class StatsD::Instrument::LegacyClient
|
|
120
120
|
#
|
121
121
|
# Emits a set metric, which counts the number of distinct values that have occurred.
|
122
122
|
#
|
123
|
-
# @example
|
123
|
+
# @example Counting the number of unique visitors
|
124
124
|
# StatsD.set('visitors.unique', Current.user.id)
|
125
125
|
#
|
126
126
|
# @param key [String] The name of the metric.
|
@@ -222,7 +222,7 @@ class StatsD::Instrument::LegacyClient
|
|
222
222
|
# @param title [String] Title of the event. A configured prefix may be applied to this title.
|
223
223
|
# @param text [String] Body of the event. Can contain newlines.
|
224
224
|
# @param [String] hostname The hostname to associate with the event.
|
225
|
-
# @param [Time] timestamp The moment the status of the service was
|
225
|
+
# @param [Time] timestamp The moment the status of the service was checked. Defaults to now.
|
226
226
|
# @param [String] aggregation_key A key to aggregate similar events into groups.
|
227
227
|
# @param [String] priority The event's priority, either `"low"` or `"normal"` (default).
|
228
228
|
# @param [String] source_type_name The source type.
|
@@ -254,7 +254,7 @@ class StatsD::Instrument::LegacyClient
|
|
254
254
|
# @param [String] name Name of the service. A configured prefix may be applied to this title.
|
255
255
|
# @param [Symbol] status Current status of the service. Either `:ok`, `:warning`, `:critical`, or `:unknown`.
|
256
256
|
# @param [String] hostname The hostname to associate with the event.
|
257
|
-
# @param [Time] timestamp The moment the status of the service was
|
257
|
+
# @param [Time] timestamp The moment the status of the service was checked. Defaults to now.
|
258
258
|
# @param [String] message A message that describes the current status.
|
259
259
|
# @param tags (see #increment)
|
260
260
|
# @return [void]
|
@@ -273,7 +273,7 @@ class StatsD::Instrument::LegacyClient
|
|
273
273
|
})
|
274
274
|
end
|
275
275
|
|
276
|
-
|
276
|
+
protected
|
277
277
|
|
278
278
|
def measure_latency(type, key, sample_rate:, tags:, prefix:)
|
279
279
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
@@ -296,6 +296,6 @@ class StatsD::Instrument::LegacyClient
|
|
296
296
|
metric = StatsD::Instrument::Metric.new(type: type, name: name, value: value,
|
297
297
|
sample_rate: sample_rate, tags: tags, metadata: metadata)
|
298
298
|
backend.collect_metric(metric)
|
299
|
-
metric
|
299
|
+
metric
|
300
300
|
end
|
301
301
|
end
|
@@ -54,11 +54,11 @@ class StatsD::Instrument::Metric
|
|
54
54
|
# The default value for this metric, which will be used if it is not set.
|
55
55
|
#
|
56
56
|
# A default value is only defined for counter metrics (<tt>1</tt>). For all other
|
57
|
-
# metric types, this
|
57
|
+
# metric types, this method will raise an <tt>ArgumentError</tt>.
|
58
58
|
#
|
59
59
|
#
|
60
60
|
# A default value is only defined for counter metrics (<tt>1</tt>). For all other
|
61
|
-
# metric types, this
|
61
|
+
# metric types, this method will raise an <tt>ArgumentError</tt>.
|
62
62
|
#
|
63
63
|
# @return [Numeric, String] The default value for this metric.
|
64
64
|
# @raise ArgumentError if the metric type doesn't have a default value
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
module Cop
|
7
7
|
module StatsD
|
8
8
|
# This Rubocop will check for using the metaprogramming macros for positional
|
9
|
-
# argument usage, which is deprecated. These macros include `
|
9
|
+
# argument usage, which is deprecated. These macros include `statsd_count_if`,
|
10
10
|
# `statsd_measure`, etc.
|
11
11
|
#
|
12
12
|
# Use the following Rubocop invocation to check your project's codebase:
|
@@ -42,16 +42,16 @@ module RuboCop
|
|
42
42
|
|
43
43
|
def autocorrect(node)
|
44
44
|
-> (corrector) do
|
45
|
-
|
45
|
+
positional_arguments = if node.arguments.last.type == :block_pass
|
46
46
|
node.arguments[2...node.arguments.length - 1]
|
47
47
|
else
|
48
48
|
node.arguments[2...node.arguments.length]
|
49
49
|
end
|
50
50
|
|
51
|
-
case
|
51
|
+
case positional_arguments[0].type
|
52
52
|
when *UNKNOWN_ARGUMENT_TYPES
|
53
53
|
# We don't know whether the method returns a hash, in which case it would be interpreted
|
54
|
-
# as keyword arguments. In this case, the fix would be to add a
|
54
|
+
# as keyword arguments. In this case, the fix would be to add a keyword splat:
|
55
55
|
#
|
56
56
|
# `StatsD.instrument('foo', 1, method_call)`
|
57
57
|
# => `StatsD.instrument('foo', 1, **method_call)`
|
@@ -68,17 +68,17 @@ module RuboCop
|
|
68
68
|
when *POSITIONAL_ARGUMENT_TYPES
|
69
69
|
value_argument = node.arguments[1]
|
70
70
|
from = value_argument.source_range.end_pos
|
71
|
-
to =
|
71
|
+
to = positional_arguments.last.source_range.end_pos
|
72
72
|
range = Parser::Source::Range.new(node.source_range.source_buffer, from, to)
|
73
73
|
corrector.remove(range)
|
74
74
|
|
75
75
|
keyword_arguments = []
|
76
|
-
sample_rate =
|
76
|
+
sample_rate = positional_arguments[0]
|
77
77
|
if sample_rate && sample_rate.type != :nil
|
78
78
|
keyword_arguments << "sample_rate: #{sample_rate.source}"
|
79
79
|
end
|
80
80
|
|
81
|
-
tags =
|
81
|
+
tags = positional_arguments[1]
|
82
82
|
if tags && tags.type != :nil
|
83
83
|
keyword_arguments << if tags.type == :hash && tags.source[0] != '{'
|
84
84
|
"tags: { #{tags.source} }"
|
@@ -5,7 +5,7 @@ require_relative '../rubocop' unless defined?(RuboCop::Cop::StatsD)
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
7
7
|
module StatsD
|
8
|
-
# This Rubocop will check for calls to StatsD singleton
|
8
|
+
# This Rubocop will check for calls to StatsD singleton configuration methods
|
9
9
|
# (e.g. `StatsD.prefix`). The library is moving away from having just a single
|
10
10
|
# singleton client, so these methods are deprecated.
|
11
11
|
#
|
@@ -15,7 +15,7 @@ module StatsD
|
|
15
15
|
# - Only accept a position argument for value, rather than a keyword argument.
|
16
16
|
# - The provided arguments have the right type.
|
17
17
|
#
|
18
|
-
# You can enable
|
18
|
+
# You can enable this monkeypatch by changing your Gemfile as follows:
|
19
19
|
#
|
20
20
|
# gem 'statsd-instrument', require: 'statsd/instrument/strict'
|
21
21
|
#
|
@@ -81,6 +81,11 @@ module StatsD
|
|
81
81
|
super
|
82
82
|
end
|
83
83
|
|
84
|
+
def key_value(*)
|
85
|
+
raise NotImplementedError, "The key_value metric type will be removed " \
|
86
|
+
"from the next major version of statsd-instrument"
|
87
|
+
end
|
88
|
+
|
84
89
|
private
|
85
90
|
|
86
91
|
def check_block_or_numeric_value(value)
|
@@ -99,49 +104,59 @@ module StatsD
|
|
99
104
|
raise ArgumentError, "The tags argument should be a hash or an array, got #{tags.inspect}"
|
100
105
|
end
|
101
106
|
end
|
107
|
+
end
|
108
|
+
|
109
|
+
module VoidCollectMetric
|
110
|
+
protected
|
102
111
|
|
103
112
|
def collect_metric(type, name, value, sample_rate:, tags: nil, prefix:, metadata: nil)
|
104
113
|
super
|
105
|
-
|
114
|
+
StatsD::Instrument::VOID
|
106
115
|
end
|
107
116
|
end
|
108
117
|
|
109
118
|
module StrictMetaprogramming
|
110
|
-
def statsd_measure(method, name, sample_rate: nil, tags: nil,
|
119
|
+
def statsd_measure(method, name, sample_rate: nil, tags: nil,
|
120
|
+
no_prefix: false, client: StatsD.singleton_client)
|
121
|
+
|
111
122
|
check_method_and_metric_name(method, name)
|
112
123
|
|
113
|
-
# Unfortunately, we have to inline the new method implementation
|
124
|
+
# Unfortunately, we have to inline the new method implementation because we have to fix the
|
114
125
|
# Stats.measure call to not use the `as_dist` and `prefix` arguments.
|
115
126
|
add_to_method(method, name, :measure) do
|
116
127
|
define_method(method) do |*args, &block|
|
117
128
|
key = StatsD::Instrument.generate_metric_name(nil, name, self, *args)
|
118
|
-
|
129
|
+
client.measure(key, sample_rate: sample_rate, tags: tags, no_prefix: no_prefix) do
|
119
130
|
super(*args, &block)
|
120
131
|
end
|
121
132
|
end
|
122
133
|
end
|
123
134
|
end
|
124
135
|
|
125
|
-
def statsd_distribution(method, name, sample_rate: nil, tags: nil,
|
136
|
+
def statsd_distribution(method, name, sample_rate: nil, tags: nil,
|
137
|
+
no_prefix: false, client: StatsD.singleton_client)
|
138
|
+
|
126
139
|
check_method_and_metric_name(method, name)
|
127
140
|
|
128
|
-
# Unfortunately, we have to inline the new method implementation
|
141
|
+
# Unfortunately, we have to inline the new method implementation because we have to fix the
|
129
142
|
# Stats.distribution call to not use the `prefix` argument.
|
130
143
|
|
131
144
|
add_to_method(method, name, :distribution) do
|
132
145
|
define_method(method) do |*args, &block|
|
133
146
|
key = StatsD::Instrument.generate_metric_name(nil, name, self, *args)
|
134
|
-
|
147
|
+
client.distribution(key, sample_rate: sample_rate, tags: tags, no_prefix: no_prefix) do
|
135
148
|
super(*args, &block)
|
136
149
|
end
|
137
150
|
end
|
138
151
|
end
|
139
152
|
end
|
140
153
|
|
141
|
-
def statsd_count_success(method, name, sample_rate: nil, tags: nil,
|
154
|
+
def statsd_count_success(method, name, sample_rate: nil, tags: nil,
|
155
|
+
no_prefix: false, client: StatsD.singleton_client)
|
156
|
+
|
142
157
|
check_method_and_metric_name(method, name)
|
143
158
|
|
144
|
-
# Unfortunately, we have to inline the new method implementation
|
159
|
+
# Unfortunately, we have to inline the new method implementation because we have to fix the
|
145
160
|
# Stats.increment call to not use the `prefix` argument.
|
146
161
|
|
147
162
|
add_to_method(method, name, :count_success) do
|
@@ -163,16 +178,18 @@ module StatsD
|
|
163
178
|
ensure
|
164
179
|
suffix = truthiness == false ? 'failure' : 'success'
|
165
180
|
key = "#{StatsD::Instrument.generate_metric_name(nil, name, self, *args)}.#{suffix}"
|
166
|
-
|
181
|
+
client.increment(key, sample_rate: sample_rate, tags: tags, no_prefix: no_prefix)
|
167
182
|
end
|
168
183
|
end
|
169
184
|
end
|
170
185
|
end
|
171
186
|
|
172
|
-
def statsd_count_if(method, name, sample_rate: nil, tags: nil,
|
187
|
+
def statsd_count_if(method, name, sample_rate: nil, tags: nil,
|
188
|
+
no_prefix: false, client: StatsD.singleton_client)
|
189
|
+
|
173
190
|
check_method_and_metric_name(method, name)
|
174
191
|
|
175
|
-
# Unfortunately, we have to inline the new method implementation
|
192
|
+
# Unfortunately, we have to inline the new method implementation because we have to fix the
|
176
193
|
# Stats.increment call to not use the `prefix` argument.
|
177
194
|
|
178
195
|
add_to_method(method, name, :count_if) do
|
@@ -194,23 +211,25 @@ module StatsD
|
|
194
211
|
ensure
|
195
212
|
if truthiness
|
196
213
|
key = StatsD::Instrument.generate_metric_name(nil, name, self, *args)
|
197
|
-
|
214
|
+
client.increment(key, sample_rate: sample_rate, tags: tags, no_prefix: no_prefix)
|
198
215
|
end
|
199
216
|
end
|
200
217
|
end
|
201
218
|
end
|
202
219
|
end
|
203
220
|
|
204
|
-
def statsd_count(method, name, sample_rate: nil, tags: nil,
|
221
|
+
def statsd_count(method, name, sample_rate: nil, tags: nil,
|
222
|
+
no_prefix: false, client: StatsD.singleton_client)
|
223
|
+
|
205
224
|
check_method_and_metric_name(method, name)
|
206
225
|
|
207
|
-
# Unfortunately, we have to inline the new method implementation
|
226
|
+
# Unfortunately, we have to inline the new method implementation because we have to fix the
|
208
227
|
# Stats.increment call to not use the `prefix` argument.
|
209
228
|
|
210
229
|
add_to_method(method, name, :count) do
|
211
230
|
define_method(method) do |*args, &block|
|
212
231
|
key = StatsD::Instrument.generate_metric_name(nil, name, self, *args)
|
213
|
-
|
232
|
+
client.increment(key, sample_rate: sample_rate, tags: tags, no_prefix: no_prefix)
|
214
233
|
super(*args, &block)
|
215
234
|
end
|
216
235
|
end
|
@@ -232,4 +251,5 @@ module StatsD
|
|
232
251
|
end
|
233
252
|
|
234
253
|
StatsD.singleton_class.prepend(StatsD::Instrument::Strict)
|
254
|
+
StatsD::Instrument::LegacyClient.prepend(StatsD::Instrument::VoidCollectMetric)
|
235
255
|
StatsD::Instrument.prepend(StatsD::Instrument::StrictMetaprogramming)
|