statsd-instrument 3.5.12 → 3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41254f0057c645926c54b36ec4bf937352444740f0cae248b1feed1f35ce4044
4
- data.tar.gz: 3d2131889385d2755a738d735ca9a20dba0342e645d58312c4007b7a1835b249
3
+ metadata.gz: 0575c6d7547ea1494ba62807fa7ffd10a0d04b58a762eddf3eac56c47cd2c21e
4
+ data.tar.gz: 8370611d71ed66a31e67e150646df41f94f393cdb0bc953e23624113e99ffbd5
5
5
  SHA512:
6
- metadata.gz: 46a4f427f2e298c242a84c7f90a715a7d4397c28ad5db8f89f8b669e3867c7b253a5858a61e35768fada113b56202dc43d91cffb68b9626ea79c26fd15d10a67
7
- data.tar.gz: 99b1979f9b3b9a4e502c78a42bfa48348600eece64646ddfdd2470ae1d3b4cc52123e3edaef539886c667ff2e9eca98051c55c5fec7c6ccc76e1404e2f5f2800
6
+ metadata.gz: 41fe6f71cd25a79fdfa73f1e53e0976333af4cd2ea7387832564b6991014d432e135c7e9e5a770f4d436ce23770811e21048d4ccf28f2a8d1b77debc3137d59a
7
+ data.tar.gz: 4db9fb5d52dd7258e8a018de3fcdd90a93ecfa01fea3142f47fb3ef0150de30998a7a422dde012ffc8de9e95eded01c41896f78886df07fc8163dc530e0e2e22
data/CHANGELOG.md CHANGED
@@ -6,6 +6,10 @@ section below.
6
6
 
7
7
  ## Unreleased changes
8
8
 
9
+ ## Version 3.6.0
10
+
11
+ - Optimized datagram building.
12
+
9
13
  ## Version 3.5.12
10
14
 
11
15
  - Update CONTRIBUTING docs about release process
@@ -140,13 +140,15 @@ module StatsD
140
140
  # on their frequency, rather than changing the default sample rate.
141
141
  #
142
142
  # @return [Float] (default: 1.0) A value between 0.0 and 1.0.
143
- attr_reader :default_sample_rate
143
+ def default_sample_rate
144
+ @default_sample_rate || 1.0
145
+ end
144
146
 
145
147
  # Instantiates a new client.
146
148
  # @see .from_env to instantiate a client using environment variables.
147
149
  def initialize(
148
150
  prefix: nil,
149
- default_sample_rate: 1.0,
151
+ default_sample_rate: nil,
150
152
  default_tags: nil,
151
153
  implementation: "datadog",
152
154
  sink: StatsD::Instrument::NullSink.new,
@@ -199,9 +201,10 @@ module StatsD
199
201
  # @return [void]
200
202
  def increment(name, value = 1, sample_rate: nil, tags: nil, no_prefix: false)
201
203
  sample_rate ||= @default_sample_rate
202
- return StatsD::Instrument::VOID unless sample?(sample_rate)
203
-
204
- emit(datagram_builder(no_prefix: no_prefix).c(name, value, sample_rate, tags))
204
+ if sample_rate.nil? || sample?(sample_rate)
205
+ emit(datagram_builder(no_prefix: no_prefix).c(name, value, sample_rate, tags))
206
+ end
207
+ StatsD::Instrument::VOID
205
208
  end
206
209
 
207
210
  # Emits a timing metric.
@@ -217,9 +220,10 @@ module StatsD
217
220
  end
218
221
 
219
222
  sample_rate ||= @default_sample_rate
220
- return StatsD::Instrument::VOID unless sample?(sample_rate)
221
-
222
- emit(datagram_builder(no_prefix: no_prefix).ms(name, value, sample_rate, tags))
223
+ if sample_rate.nil? || sample?(sample_rate)
224
+ emit(datagram_builder(no_prefix: no_prefix).ms(name, value, sample_rate, tags))
225
+ end
226
+ StatsD::Instrument::VOID
223
227
  end
224
228
 
225
229
  # Emits a gauge metric.
@@ -237,9 +241,10 @@ module StatsD
237
241
  # @return [void]
238
242
  def gauge(name, value, sample_rate: nil, tags: nil, no_prefix: false)
239
243
  sample_rate ||= @default_sample_rate
240
- return StatsD::Instrument::VOID unless sample?(sample_rate)
241
-
242
- emit(datagram_builder(no_prefix: no_prefix).g(name, value, sample_rate, tags))
244
+ if sample_rate.nil? || sample?(sample_rate)
245
+ emit(datagram_builder(no_prefix: no_prefix).g(name, value, sample_rate, tags))
246
+ end
247
+ StatsD::Instrument::VOID
243
248
  end
244
249
 
245
250
  # Emits a set metric, which counts distinct values.
@@ -251,9 +256,10 @@ module StatsD
251
256
  # @return [void]
252
257
  def set(name, value, sample_rate: nil, tags: nil, no_prefix: false)
253
258
  sample_rate ||= @default_sample_rate
254
- return StatsD::Instrument::VOID unless sample?(sample_rate)
255
-
256
- emit(datagram_builder(no_prefix: no_prefix).s(name, value, sample_rate, tags))
259
+ if sample_rate.nil? || sample?(sample_rate)
260
+ emit(datagram_builder(no_prefix: no_prefix).s(name, value, sample_rate, tags))
261
+ end
262
+ StatsD::Instrument::VOID
257
263
  end
258
264
 
259
265
  # Emits a distribution metric, which builds a histogram of the reported
@@ -274,9 +280,10 @@ module StatsD
274
280
  end
275
281
 
276
282
  sample_rate ||= @default_sample_rate
277
- return StatsD::Instrument::VOID unless sample?(sample_rate)
278
-
279
- emit(datagram_builder(no_prefix: no_prefix).d(name, value, sample_rate, tags))
283
+ if sample_rate.nil? || sample?(sample_rate)
284
+ emit(datagram_builder(no_prefix: no_prefix).d(name, value, sample_rate, tags))
285
+ end
286
+ StatsD::Instrument::VOID
280
287
  end
281
288
 
282
289
  # Emits a histogram metric, which builds a histogram of the reported values.
@@ -292,9 +299,10 @@ module StatsD
292
299
  # @return [void]
293
300
  def histogram(name, value, sample_rate: nil, tags: nil, no_prefix: false)
294
301
  sample_rate ||= @default_sample_rate
295
- return StatsD::Instrument::VOID unless sample?(sample_rate)
296
-
297
- emit(datagram_builder(no_prefix: no_prefix).h(name, value, sample_rate, tags))
302
+ if sample_rate.nil? || sample?(sample_rate)
303
+ emit(datagram_builder(no_prefix: no_prefix).h(name, value, sample_rate, tags))
304
+ end
305
+ StatsD::Instrument::VOID
298
306
  end
299
307
 
300
308
  # @!endgroup
@@ -317,7 +325,7 @@ module StatsD
317
325
  stop = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
318
326
 
319
327
  sample_rate ||= @default_sample_rate
320
- if sample?(sample_rate)
328
+ if sample_rate.nil? || sample?(sample_rate)
321
329
  metric_type ||= datagram_builder(no_prefix: no_prefix).latency_metric_type
322
330
  latency_in_ms = stop - start
323
331
  emit(datagram_builder(no_prefix: no_prefix).send(metric_type, name, latency_in_ms, sample_rate, tags))
@@ -20,8 +20,8 @@ module StatsD
20
20
  end
21
21
 
22
22
  def initialize(prefix: nil, default_tags: nil)
23
- @prefix = prefix.nil? ? "" : "#{normalize_name(prefix)}."
24
- @default_tags = normalize_tags(default_tags)
23
+ @prefix = prefix.nil? ? "" : "#{prefix}.".tr(":|@", "_")
24
+ @default_tags = default_tags.nil? || default_tags.empty? ? nil : compile_tags(default_tags, "|#".b)
25
25
  end
26
26
 
27
27
  def c(name, value, sample_rate, tags)
@@ -58,26 +58,6 @@ module StatsD
58
58
 
59
59
  protected
60
60
 
61
- attr_reader :prefix, :default_tags
62
-
63
- # Utility function to convert tags to the canonical form.
64
- #
65
- # - Tags specified as key value pairs will be converted into an array
66
- # - Tags are normalized to remove unsupported characters
67
- #
68
- # @param tags [Array<String>, Hash<String, String>, nil] Tags specified in any form.
69
- # @return [Array<String>, nil] the list of tags in canonical form.
70
- def normalize_tags(tags)
71
- return [] unless tags
72
-
73
- tags = tags.map { |k, v| "#{k}:#{v}" } if tags.is_a?(Hash)
74
-
75
- # Fast path when no string replacement is needed
76
- return tags unless tags.any? { |tag| /[|,]/.match?(tag) }
77
-
78
- tags.map { |tag| tag.tr("|,", "") }
79
- end
80
-
81
61
  # Utility function to remove invalid characters from a StatsD metric name
82
62
  def normalize_name(name)
83
63
  # Fast path when no normalization is needed to avoid copying the string
@@ -87,12 +67,49 @@ module StatsD
87
67
  end
88
68
 
89
69
  def generate_generic_datagram(name, value, type, sample_rate, tags)
90
- tags = normalize_tags(tags) + default_tags
91
- datagram = +"#{@prefix}#{normalize_name(name)}:#{value}|#{type}"
92
- datagram << "|@#{sample_rate}" if sample_rate && sample_rate < 1
93
- datagram << "|##{tags.join(",")}" unless tags.empty?
70
+ datagram = "".b <<
71
+ @prefix <<
72
+ (/[:|@]/.match?(name) ? name.tr(":|@", "_") : name) <<
73
+ ":" << value.to_s <<
74
+ "|" << type
75
+
76
+ datagram << "|@" << sample_rate.to_s if sample_rate && sample_rate < 1
77
+
78
+ unless @default_tags.nil?
79
+ datagram << @default_tags
80
+ end
81
+
82
+ unless tags.nil?
83
+ datagram << (@default_tags.nil? ? "|#" : ",")
84
+ compile_tags(tags, datagram)
85
+ end
86
+
94
87
  datagram
95
88
  end
89
+
90
+ def compile_tags(tags, buffer = "".b)
91
+ if tags.is_a?(Hash)
92
+ first = true
93
+ tags.each do |key, value|
94
+ if first
95
+ first = false
96
+ else
97
+ buffer << ","
98
+ end
99
+ key = key.to_s
100
+ key = key.tr("|,", "") if /[|,]/.match?(key)
101
+ value = value.to_s
102
+ value = value.tr("|,", "") if /[|,]/.match?(value)
103
+ buffer << key << ":" << value
104
+ end
105
+ else
106
+ if tags.any? { |tag| /[|,]/.match?(tag) }
107
+ tags = tags.map { |tag| tag.tr("|,", "") }
108
+ end
109
+ buffer << tags.join(",")
110
+ end
111
+ buffer
112
+ end
96
113
  end
97
114
  end
98
115
  end
@@ -36,7 +36,6 @@ module StatsD
36
36
 
37
37
  escaped_title = "#{@prefix}#{title}".gsub("\n", '\n')
38
38
  escaped_text = text.gsub("\n", '\n')
39
- tags = normalize_tags(tags) + default_tags
40
39
 
41
40
  datagram = +"_e{#{escaped_title.length},#{escaped_text.length}}:#{escaped_title}|#{escaped_text}"
42
41
  datagram << "|h:#{hostname}" if hostname
@@ -45,7 +44,16 @@ module StatsD
45
44
  datagram << "|p:#{priority}" if priority
46
45
  datagram << "|s:#{source_type_name}" if source_type_name
47
46
  datagram << "|t:#{alert_type}" if alert_type
48
- datagram << "|##{tags.join(",")}" unless tags.empty?
47
+
48
+ unless @default_tags.nil?
49
+ datagram << @default_tags
50
+ end
51
+
52
+ unless tags.nil?
53
+ datagram << (@default_tags.nil? ? "|#" : ",")
54
+ compile_tags(tags, datagram)
55
+ end
56
+
49
57
  datagram
50
58
  end
51
59
 
@@ -63,12 +71,20 @@ module StatsD
63
71
  # @see https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/#service-checks
64
72
  def _sc(name, status, timestamp: nil, hostname: nil, tags: nil, message: nil)
65
73
  status_number = status.is_a?(Integer) ? status : SERVICE_CHECK_STATUS_VALUES.fetch(status.to_sym)
66
- tags = normalize_tags(tags) + default_tags
67
74
 
68
75
  datagram = +"_sc|#{@prefix}#{normalize_name(name)}|#{status_number}"
69
76
  datagram << "|h:#{hostname}" if hostname
70
77
  datagram << "|d:#{timestamp.to_i}" if timestamp
71
- datagram << "|##{tags.join(",")}" unless tags.empty?
78
+
79
+ unless @default_tags.nil?
80
+ datagram << @default_tags
81
+ end
82
+
83
+ unless tags.nil?
84
+ datagram << (@default_tags.nil? ? "|#" : ",")
85
+ compile_tags(tags, datagram)
86
+ end
87
+
72
88
  datagram << "|m:#{normalize_name(message)}" if message
73
89
  datagram
74
90
  end
@@ -9,10 +9,8 @@ module StatsD
9
9
 
10
10
  protected
11
11
 
12
- def normalize_tags(tags)
13
- raise NotImplementedError, "#{self.class.name} does not support tags" if tags
14
-
15
- super
12
+ def compile_tags(*)
13
+ raise NotImplementedError, "#{self.class.name} does not support tags"
16
14
  end
17
15
  end
18
16
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module StatsD
4
4
  module Instrument
5
- VERSION = "3.5.12"
5
+ VERSION = "3.6.0"
6
6
  end
7
7
  end
data/test/client_test.rb CHANGED
@@ -206,7 +206,7 @@ class ClientTest < Minitest::Test
206
206
  mock_sink.stubs(:sample?).returns(false, true, false, false, true)
207
207
  mock_sink.expects(:<<).twice
208
208
 
209
- client = StatsD::Instrument::Client.new(sink: mock_sink)
209
+ client = StatsD::Instrument::Client.new(sink: mock_sink, default_sample_rate: 0.5)
210
210
  5.times { client.increment("metric") }
211
211
  end
212
212
 
@@ -14,17 +14,17 @@ class DatagramBuilderTest < Minitest::Test
14
14
  assert_equal("fo_o", @datagram_builder.send(:normalize_name, "fo:o"))
15
15
  end
16
16
 
17
- def test_normalize_unsupported_tag_names
18
- assert_equal(["ign#ored"], @datagram_builder.send(:normalize_tags, ["ign#o|re,d"]))
17
+ def test_compile_unsupported_tag_names
18
+ assert_equal("ign#ored", @datagram_builder.send(:compile_tags, ["ign#o|re,d"]))
19
19
  # NOTE: how this is interpreted by the backend is undefined.
20
20
  # We rely on the user to not do stuff like this if they don't want to be surprised.
21
21
  # We do not want to take the performance hit of normalizing this.
22
- assert_equal(["lol::class:omg::lol"], @datagram_builder.send(:normalize_tags, "lol::class" => "omg::lol"))
22
+ assert_equal("lol::class:omg::lol", @datagram_builder.send(:compile_tags, { "lol::class" => "omg::lol" }))
23
23
  end
24
24
 
25
- def test_normalize_tags_converts_hash_to_array
26
- assert_equal(["tag:value"], @datagram_builder.send(:normalize_tags, tag: "value"))
27
- assert_equal(["tag1:v1", "tag2:v2"], @datagram_builder.send(:normalize_tags, tag1: "v1", tag2: "v2"))
25
+ def test_compile_tags_converts_hash_to_array
26
+ assert_equal("tag:value", @datagram_builder.send(:compile_tags, { tag: "value" }))
27
+ assert_equal("tag1:v1,tag2:v2", @datagram_builder.send(:compile_tags, { tag1: "v1", tag2: "v2" }))
28
28
  end
29
29
 
30
30
  def test_c
@@ -103,7 +103,7 @@ class DatagramBuilderTest < Minitest::Test
103
103
  assert_equal("bar:1|c|#foo", datagram)
104
104
 
105
105
  datagram = datagram_builder.c("bar", 1, nil, a: "b")
106
- assert_equal("bar:1|c|#a:b,foo", datagram)
106
+ assert_equal("bar:1|c|#foo,a:b", datagram)
107
107
 
108
108
  # We do not filter out duplicates, because detecting dupes is too time consuming.
109
109
  # We let the server deal with the situation
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statsd-instrument
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.12
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jesse Storimer
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-08-25 00:00:00.000000000 Z
13
+ date: 2023-11-03 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: A StatsD client for Ruby apps. Provides metaprogramming methods to inject
16
16
  StatsD instrumentation into your code.
@@ -122,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
122
  - !ruby/object:Gem::Version
123
123
  version: '0'
124
124
  requirements: []
125
- rubygems_version: 3.4.18
125
+ rubygems_version: 3.4.21
126
126
  signing_key:
127
127
  specification_version: 4
128
128
  summary: A StatsD client for Ruby apps