dogstatsd-ruby 4.8.3 → 5.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,8 +5,13 @@ require_relative 'statsd/version'
5
5
  require_relative 'statsd/telemetry'
6
6
  require_relative 'statsd/udp_connection'
7
7
  require_relative 'statsd/uds_connection'
8
- require_relative 'statsd/batch'
8
+ require_relative 'statsd/connection_cfg'
9
+ require_relative 'statsd/message_buffer'
9
10
  require_relative 'statsd/serialization'
11
+ require_relative 'statsd/sender'
12
+ require_relative 'statsd/single_thread_sender'
13
+ require_relative 'statsd/forwarder'
14
+ require_relative 'statsd/timer'
10
15
 
11
16
  # = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
12
17
  #
@@ -26,13 +31,23 @@ require_relative 'statsd/serialization'
26
31
  # statsd = Datadog::Statsd.new 'localhost', 8125, tags: 'tag1:true'
27
32
  module Datadog
28
33
  class Statsd
34
+ class Error < StandardError
35
+ end
36
+
29
37
  OK = 0
30
38
  WARNING = 1
31
39
  CRITICAL = 2
32
40
  UNKNOWN = 3
33
41
 
34
- DEFAULT_BUFFER_SIZE = 8 * 1_024
42
+ UDP_DEFAULT_BUFFER_SIZE = 1_432
43
+ UDS_DEFAULT_BUFFER_SIZE = 8_192
44
+ DEFAULT_BUFFER_POOL_SIZE = Float::INFINITY
45
+
46
+ UDP_DEFAULT_SENDER_QUEUE_SIZE = 2048
47
+ UDS_DEFAULT_SENDER_QUEUE_SIZE = 512
48
+
35
49
  MAX_EVENT_SIZE = 8 * 1_024
50
+
36
51
  # minimum flush interval for the telemetry in seconds
37
52
  DEFAULT_TELEMETRY_FLUSH_INTERVAL = 10
38
53
 
@@ -51,77 +66,89 @@ module Datadog
51
66
  serializer.global_tags
52
67
  end
53
68
 
54
- # Buffer containing the statsd message before they are sent in batch
55
- attr_reader :buffer
56
-
57
- # Maximum buffer size in bytes before it is flushed
58
- attr_reader :max_buffer_bytes
59
-
60
69
  # Default sample rate
61
70
  attr_reader :sample_rate
62
71
 
63
- # Connection
64
- attr_reader :connection
65
-
66
72
  # @param [String] host your statsd host
67
73
  # @param [Integer] port your statsd port
68
74
  # @option [String] namespace set a namespace to be prepended to every metric name
69
75
  # @option [Array<String>|Hash] tags tags to be added to every metric
70
76
  # @option [Logger] logger for debugging
71
- # @option [Integer] max_buffer_bytes max bytes to buffer when using #batch
77
+ # @option [Integer] buffer_max_payload_size max bytes to buffer
78
+ # @option [Integer] buffer_max_pool_size max messages to buffer
79
+ # @option [Integer] sender_queue_size size of the sender queue in number of buffers
80
+ # @option [Numeric] buffer_flush_interval interval in second to flush buffer
72
81
  # @option [String] socket_path unix socket path
73
82
  # @option [Float] default sample rate if not overridden
83
+ # @option [Boolean] single_thread flushes the metrics on the main thread instead of in a companion thread
84
+ # @option [Boolean] delay_serialization delays stat serialization
74
85
  def initialize(
75
86
  host = nil,
76
87
  port = nil,
88
+ socket_path: nil,
89
+
77
90
  namespace: nil,
78
91
  tags: nil,
79
- max_buffer_bytes: DEFAULT_BUFFER_SIZE,
80
- socket_path: nil,
81
- logger: nil,
82
92
  sample_rate: nil,
83
- disable_telemetry: false,
93
+
94
+ buffer_max_payload_size: nil,
95
+ buffer_max_pool_size: nil,
96
+ buffer_overflowing_stategy: :drop,
97
+ buffer_flush_interval: nil,
98
+
99
+ sender_queue_size: nil,
100
+
101
+ logger: nil,
102
+
103
+ single_thread: false,
104
+ delay_serialization: false,
105
+
106
+ telemetry_enable: true,
84
107
  telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL
85
108
  )
86
109
  unless tags.nil? || tags.is_a?(Array) || tags.is_a?(Hash)
87
- raise ArgumentError, 'tags must be a Array<String> or a Hash'
110
+ raise ArgumentError, 'tags must be an array of string tags or a Hash'
88
111
  end
89
112
 
90
113
  @namespace = namespace
91
114
  @prefix = @namespace ? "#{@namespace}.".freeze : nil
92
-
93
115
  @serializer = Serialization::Serializer.new(prefix: @prefix, global_tags: tags)
116
+ @sample_rate = sample_rate
117
+ @delay_serialization = delay_serialization
94
118
 
95
- transport_type = socket_path.nil? ? :udp : :uds
119
+ @forwarder = Forwarder.new(
120
+ connection_cfg: ConnectionCfg.new(
121
+ host: host,
122
+ port: port,
123
+ socket_path: socket_path,
124
+ ),
96
125
 
97
- @telemetry = Telemetry.new(disable_telemetry, telemetry_flush_interval,
98
126
  global_tags: tags,
99
- transport_type: transport_type
100
- )
127
+ logger: logger,
101
128
 
102
- @connection = case transport_type
103
- when :udp
104
- UDPConnection.new(host, port, logger, telemetry)
105
- when :uds
106
- UDSConnection.new(socket_path, logger, telemetry)
107
- end
129
+ single_thread: single_thread,
108
130
 
109
- @logger = logger
131
+ buffer_max_payload_size: buffer_max_payload_size,
132
+ buffer_max_pool_size: buffer_max_pool_size,
133
+ buffer_overflowing_stategy: buffer_overflowing_stategy,
134
+ buffer_flush_interval: buffer_flush_interval,
110
135
 
111
- @sample_rate = sample_rate
136
+ sender_queue_size: sender_queue_size,
112
137
 
113
- # we reduce max_buffer_bytes by a the rough estimate of the telemetry payload
114
- @batch = Batch.new(connection, (max_buffer_bytes - telemetry.estimate_max_size))
138
+ telemetry_flush_interval: telemetry_enable ? telemetry_flush_interval : nil,
139
+ serializer: serializer
140
+ )
115
141
  end
116
142
 
117
143
  # yield a new instance to a block and close it when done
118
144
  # for short-term use-cases that don't want to close the socket manually
119
- def self.open(*args)
120
- instance = new(*args)
145
+ # TODO: replace with ... once we are on ruby 2.7
146
+ def self.open(*args, **kwargs)
147
+ instance = new(*args, **kwargs)
121
148
 
122
149
  yield instance
123
150
  ensure
124
- instance.close
151
+ instance.close if instance
125
152
  end
126
153
 
127
154
  # Sends an increment (count = 1) for the given stat to the statsd server.
@@ -129,6 +156,7 @@ module Datadog
129
156
  # @param [String] stat stat name
130
157
  # @param [Hash] opts the options to create the metric with
131
158
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
159
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
132
160
  # @option opts [Array<String>] :tags An array of tags
133
161
  # @option opts [Numeric] :by increment value, default 1
134
162
  # @see #count
@@ -143,6 +171,7 @@ module Datadog
143
171
  # @param [String] stat stat name
144
172
  # @param [Hash] opts the options to create the metric with
145
173
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
174
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
146
175
  # @option opts [Array<String>] :tags An array of tags
147
176
  # @option opts [Numeric] :by decrement value, default 1
148
177
  # @see #count
@@ -158,13 +187,14 @@ module Datadog
158
187
  # @param [Integer] count count
159
188
  # @param [Hash] opts the options to create the metric with
160
189
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
190
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
161
191
  # @option opts [Array<String>] :tags An array of tags
162
192
  def count(stat, count, opts = EMPTY_OPTIONS)
163
193
  opts = { sample_rate: opts } if opts.is_a?(Numeric)
164
194
  send_stats(stat, count, COUNTER_TYPE, opts)
165
195
  end
166
196
 
167
- # Sends an arbitary gauge value for the given stat to the statsd server.
197
+ # Sends an arbitrary gauge value for the given stat to the statsd server.
168
198
  #
169
199
  # This is useful for recording things like available disk space,
170
200
  # memory usage, and the like, which have different semantics than
@@ -174,6 +204,7 @@ module Datadog
174
204
  # @param [Numeric] value gauge value.
175
205
  # @param [Hash] opts the options to create the metric with
176
206
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
207
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
177
208
  # @option opts [Array<String>] :tags An array of tags
178
209
  # @example Report the current user count:
179
210
  # $statsd.gauge('user.count', User.count)
@@ -188,6 +219,7 @@ module Datadog
188
219
  # @param [Numeric] value histogram value.
189
220
  # @param [Hash] opts the options to create the metric with
190
221
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
222
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
191
223
  # @option opts [Array<String>] :tags An array of tags
192
224
  # @example Report the current user count:
193
225
  # $statsd.histogram('user.count', User.count)
@@ -201,6 +233,7 @@ module Datadog
201
233
  # @param [Numeric] value distribution value.
202
234
  # @param [Hash] opts the options to create the metric with
203
235
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
236
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
204
237
  # @option opts [Array<String>] :tags An array of tags
205
238
  # @example Report the current user count:
206
239
  # $statsd.distribution('user.count', User.count)
@@ -208,6 +241,26 @@ module Datadog
208
241
  send_stats(stat, value, DISTRIBUTION_TYPE, opts)
209
242
  end
210
243
 
244
+ # Reports execution time of the provided block as a distribution.
245
+ #
246
+ # If the block fails, the stat is still reported, then the error
247
+ # is reraised
248
+ #
249
+ # @param [String] stat stat name.
250
+ # @param [Numeric] value distribution value.
251
+ # @param [Hash] opts the options to create the metric with
252
+ # @option opts [Numeric] :sample_rate sample rate, 1 for always
253
+ # @option opts [Array<String>] :tags An array of tags
254
+ # @example Report the time (in ms) taken to activate an account
255
+ # $statsd.distribution_time('account.activate') { @account.activate! }
256
+ def distribution_time(stat, opts = EMPTY_OPTIONS)
257
+ opts = { sample_rate: opts } if opts.is_a?(Numeric)
258
+ start = now
259
+ yield
260
+ ensure
261
+ distribution(stat, ((now - start) * 1000).round, opts)
262
+ end
263
+
211
264
  # Sends a timing (in ms) for the given stat to the statsd server. The
212
265
  # sample_rate determines what percentage of the time this report is sent. The
213
266
  # statsd server then uses the sample_rate to correctly track the average
@@ -217,6 +270,7 @@ module Datadog
217
270
  # @param [Integer] ms timing in milliseconds
218
271
  # @param [Hash] opts the options to create the metric with
219
272
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
273
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
220
274
  # @option opts [Array<String>] :tags An array of tags
221
275
  def timing(stat, ms, opts = EMPTY_OPTIONS)
222
276
  opts = { sample_rate: opts } if opts.is_a?(Numeric)
@@ -231,6 +285,7 @@ module Datadog
231
285
  # @param [String] stat stat name
232
286
  # @param [Hash] opts the options to create the metric with
233
287
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
288
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
234
289
  # @option opts [Array<String>] :tags An array of tags
235
290
  # @yield The operation to be timed
236
291
  # @see #timing
@@ -250,6 +305,7 @@ module Datadog
250
305
  # @param [Numeric] value set value.
251
306
  # @param [Hash] opts the options to create the metric with
252
307
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
308
+ # @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
253
309
  # @option opts [Array<String>] :tags An array of tags
254
310
  # @example Record a unique visitory by id:
255
311
  # $statsd.set('visitors.uniques', User.id)
@@ -270,9 +326,9 @@ module Datadog
270
326
  # @example Report a critical service check status
271
327
  # $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
272
328
  def service_check(name, status, opts = EMPTY_OPTIONS)
273
- telemetry.sent(service_checks: 1)
329
+ telemetry.sent(service_checks: 1) if telemetry
274
330
 
275
- send_stat(serializer.to_service_check(name, status, opts))
331
+ forwarder.send_message(serializer.to_service_check(name, status, opts))
276
332
  end
277
333
 
278
334
  # This end point allows you to post events to the stream. You can tag them, set priority and even aggregate them with other events.
@@ -295,13 +351,20 @@ module Datadog
295
351
  # @example Report an awful event:
296
352
  # $statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent'])
297
353
  def event(title, text, opts = EMPTY_OPTIONS)
298
- telemetry.sent(events: 1)
354
+ telemetry.sent(events: 1) if telemetry
299
355
 
300
- send_stat(serializer.to_event(title, text, opts))
356
+ forwarder.send_message(serializer.to_event(title, text, opts))
301
357
  end
302
358
 
303
- # Send several metrics in the same UDP Packet
304
- # They will be buffered and flushed when the block finishes
359
+ # Send several metrics in the same packet.
360
+ # They will be buffered and flushed when the block finishes.
361
+ #
362
+ # This method exists for compatibility with v4.x versions, it is not needed
363
+ # anymore since the batching is now automatically done internally.
364
+ # It also means that an automatic flush could occur if the buffer is filled
365
+ # during the execution of the batch block.
366
+ #
367
+ # This method is DEPRECATED and will be removed in future v6.x API.
305
368
  #
306
369
  # @example Send several metrics in one packet:
307
370
  # $statsd.batch do |s|
@@ -309,50 +372,71 @@ module Datadog
309
372
  # s.increment('page.views')
310
373
  # end
311
374
  def batch
312
- @batch.open do
313
- yield self
314
- end
375
+ yield self
376
+ flush(sync: true)
315
377
  end
316
378
 
317
379
  # Close the underlying socket
318
- def close
319
- connection.close
380
+ #
381
+ # @param [Boolean, true] flush Should we flush the metrics before closing
382
+ def close(flush: true)
383
+ flush(sync: true) if flush
384
+ forwarder.close
385
+ end
386
+
387
+ def sync_with_outbound_io
388
+ forwarder.sync_with_outbound_io
389
+ end
390
+
391
+ # Flush the buffer into the connection
392
+ def flush(flush_telemetry: false, sync: false)
393
+ forwarder.flush(flush_telemetry: flush_telemetry, sync: sync)
394
+ end
395
+
396
+ def telemetry
397
+ forwarder.telemetry
398
+ end
399
+
400
+ def host
401
+ forwarder.host
402
+ end
403
+
404
+ def port
405
+ forwarder.port
406
+ end
407
+
408
+ def socket_path
409
+ forwarder.socket_path
410
+ end
411
+
412
+ def transport_type
413
+ forwarder.transport_type
320
414
  end
321
415
 
322
416
  private
323
417
  attr_reader :serializer
324
- attr_reader :telemetry
418
+ attr_reader :forwarder
325
419
 
326
- PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= '2.1.0')
327
420
  EMPTY_OPTIONS = {}.freeze
328
421
 
329
- if PROCESS_TIME_SUPPORTED
330
- def now
331
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
332
- end
333
- else
334
- def now
335
- Time.now.to_f
336
- end
422
+ def now
423
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
337
424
  end
338
425
 
339
426
  def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
340
- telemetry.sent(metrics: 1)
427
+ telemetry.sent(metrics: 1) if telemetry
341
428
 
342
429
  sample_rate = opts[:sample_rate] || @sample_rate || 1
343
430
 
344
- if sample_rate == 1 || rand <= sample_rate
345
- full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
346
-
347
- send_stat(full_stat)
348
- end
349
- end
431
+ if sample_rate == 1 || opts[:pre_sampled] || rand <= sample_rate
432
+ full_stat =
433
+ if @delay_serialization
434
+ [[stat, delta, type], {tags: opts[:tags], sample_rate: sample_rate}]
435
+ else
436
+ serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
437
+ end
350
438
 
351
- def send_stat(message)
352
- if @batch.open?
353
- @batch.add(message)
354
- else
355
- @connection.write(message)
439
+ forwarder.send_message(full_stat)
356
440
  end
357
441
  end
358
442
  end
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dogstatsd-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.8.3
4
+ version: 5.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rein Henrichs
8
+ - Karim Bogtob
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2021-02-22 00:00:00.000000000 Z
12
+ date: 2023-09-07 00:00:00.000000000 Z
12
13
  dependencies: []
13
- description: A Ruby DogStastd client
14
+ description: A Ruby DogStatsd client
14
15
  email: code@datadoghq.com
15
16
  executables: []
16
17
  extensions: []
@@ -21,15 +22,20 @@ files:
21
22
  - LICENSE.txt
22
23
  - README.md
23
24
  - lib/datadog/statsd.rb
24
- - lib/datadog/statsd/batch.rb
25
25
  - lib/datadog/statsd/connection.rb
26
+ - lib/datadog/statsd/connection_cfg.rb
27
+ - lib/datadog/statsd/forwarder.rb
28
+ - lib/datadog/statsd/message_buffer.rb
29
+ - lib/datadog/statsd/sender.rb
26
30
  - lib/datadog/statsd/serialization.rb
27
31
  - lib/datadog/statsd/serialization/event_serializer.rb
28
32
  - lib/datadog/statsd/serialization/serializer.rb
29
33
  - lib/datadog/statsd/serialization/service_check_serializer.rb
30
34
  - lib/datadog/statsd/serialization/stat_serializer.rb
31
35
  - lib/datadog/statsd/serialization/tag_serializer.rb
36
+ - lib/datadog/statsd/single_thread_sender.rb
32
37
  - lib/datadog/statsd/telemetry.rb
38
+ - lib/datadog/statsd/timer.rb
33
39
  - lib/datadog/statsd/udp_connection.rb
34
40
  - lib/datadog/statsd/uds_connection.rb
35
41
  - lib/datadog/statsd/version.rb
@@ -38,10 +44,14 @@ licenses:
38
44
  - MIT
39
45
  metadata:
40
46
  bug_tracker_uri: https://github.com/DataDog/dogstatsd-ruby/issues
41
- changelog_uri: https://github.com/DataDog/dogstatsd-ruby/blob/v4.8.3/CHANGELOG.md
42
- documentation_uri: https://www.rubydoc.info/gems/dogstatsd-ruby/4.8.3
43
- source_code_uri: https://github.com/DataDog/dogstatsd-ruby/tree/v4.8.3
44
- post_install_message:
47
+ changelog_uri: https://github.com/DataDog/dogstatsd-ruby/blob/v5.6.1/CHANGELOG.md
48
+ documentation_uri: https://www.rubydoc.info/gems/dogstatsd-ruby/5.6.1
49
+ source_code_uri: https://github.com/DataDog/dogstatsd-ruby/tree/v5.6.1
50
+ post_install_message: |2+
51
+
52
+ If you are upgrading from v4.x of the dogstatsd-ruby library, note the major change to the threading model:
53
+ https://github.com/DataDog/dogstatsd-ruby#migrating-from-v4x-to-v5x
54
+
45
55
  rdoc_options: []
46
56
  require_paths:
47
57
  - lib
@@ -49,16 +59,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
59
  requirements:
50
60
  - - ">="
51
61
  - !ruby/object:Gem::Version
52
- version: 2.0.0
62
+ version: 2.1.0
53
63
  required_rubygems_version: !ruby/object:Gem::Requirement
54
64
  requirements:
55
65
  - - ">="
56
66
  - !ruby/object:Gem::Version
57
67
  version: '0'
58
68
  requirements: []
59
- rubyforge_project:
60
- rubygems_version: 2.7.10
69
+ rubygems_version: 3.2.3
61
70
  signing_key:
62
71
  specification_version: 4
63
72
  summary: A Ruby DogStatsd client
64
73
  test_files: []
74
+ ...
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Datadog
4
- class Statsd
5
- class Batch
6
- def initialize(connection, max_buffer_bytes)
7
- @connection = connection
8
- @max_buffer_bytes = max_buffer_bytes
9
- @depth = 0
10
- reset
11
- end
12
-
13
- def open
14
- @depth += 1
15
-
16
- yield
17
- ensure
18
- @depth -= 1
19
- flush if !open?
20
- end
21
-
22
- def open?
23
- @depth > 0
24
- end
25
-
26
- def add(message)
27
- message_bytes = message.bytesize
28
-
29
- unless @buffer_bytes == 0
30
- if @buffer_bytes + 1 + message_bytes >= @max_buffer_bytes
31
- flush
32
- else
33
- @buffer << "\n"
34
- @buffer_bytes += 1
35
- end
36
- end
37
-
38
- @buffer << message
39
- @buffer_bytes += message_bytes
40
- end
41
-
42
- def flush
43
- return if @buffer_bytes == 0
44
- @connection.write(@buffer)
45
- reset
46
- end
47
-
48
- private
49
-
50
- def reset
51
- @buffer = String.new
52
- @buffer_bytes = 0
53
- end
54
- end
55
- end
56
- end