dogstatsd-ruby 4.8.3 → 5.0.1
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/README.md +1 -1
- data/lib/datadog/statsd.rb +81 -55
- data/lib/datadog/statsd/connection.rb +5 -8
- data/lib/datadog/statsd/forwarder.rb +120 -0
- data/lib/datadog/statsd/message_buffer.rb +88 -0
- data/lib/datadog/statsd/sender.rb +116 -0
- data/lib/datadog/statsd/serialization/tag_serializer.rb +5 -1
- data/lib/datadog/statsd/telemetry.rb +21 -23
- data/lib/datadog/statsd/udp_connection.rb +3 -3
- data/lib/datadog/statsd/uds_connection.rb +3 -3
- data/lib/datadog/statsd/version.rb +1 -1
- metadata +10 -7
- data/lib/datadog/statsd/batch.rb +0 -56
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c77f82b5b9a858517a937a5b2db1a1f890a80c94ca63e60f12e256ab28f7192d
|
|
4
|
+
data.tar.gz: e95ee401174c084edb117068f73d1c72f08e602ea9b51363e57e53c176072472
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88d54866c8693d2dab18a1370d6ba5951a42ec4ed62615c6194548dd5faa109731840187e2a7ddaf591f08d07064c8e173df74728f182fe5cb5593c896503e19
|
|
7
|
+
data.tar.gz: d8021d0b6b21efe7ab14841665b01819c397d4419b247615d88b7685b332b1c8bb9a13d05fc22d21baf5d709d5452f9a9a3d932438addfbf1238e8ab5656a8e5
|
data/README.md
CHANGED
|
@@ -86,7 +86,7 @@ statsd = Datadog::Statsd.new('localhost', 8125, buffer_max_payload_size: 4096)
|
|
|
86
86
|
|
|
87
87
|
## Credits
|
|
88
88
|
|
|
89
|
-
dogstatsd-ruby is forked from
|
|
89
|
+
dogstatsd-ruby is forked from Rein Henrichs [original Statsd
|
|
90
90
|
client](https://github.com/reinh/statsd).
|
|
91
91
|
|
|
92
92
|
Copyright (c) 2011 Rein Henrichs. See LICENSE.txt for
|
data/lib/datadog/statsd.rb
CHANGED
|
@@ -5,8 +5,10 @@ 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/
|
|
8
|
+
require_relative 'statsd/message_buffer'
|
|
9
9
|
require_relative 'statsd/serialization'
|
|
10
|
+
require_relative 'statsd/sender'
|
|
11
|
+
require_relative 'statsd/forwarder'
|
|
10
12
|
|
|
11
13
|
# = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
|
|
12
14
|
#
|
|
@@ -26,12 +28,17 @@ require_relative 'statsd/serialization'
|
|
|
26
28
|
# statsd = Datadog::Statsd.new 'localhost', 8125, tags: 'tag1:true'
|
|
27
29
|
module Datadog
|
|
28
30
|
class Statsd
|
|
31
|
+
class Error < StandardError
|
|
32
|
+
end
|
|
33
|
+
|
|
29
34
|
OK = 0
|
|
30
35
|
WARNING = 1
|
|
31
36
|
CRITICAL = 2
|
|
32
37
|
UNKNOWN = 3
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
UDP_DEFAULT_BUFFER_SIZE = 1_432
|
|
40
|
+
UDS_DEFAULT_BUFFER_SIZE = 8_192
|
|
41
|
+
DEFAULT_BUFFER_POOL_SIZE = Float::INFINITY
|
|
35
42
|
MAX_EVENT_SIZE = 8 * 1_024
|
|
36
43
|
# minimum flush interval for the telemetry in seconds
|
|
37
44
|
DEFAULT_TELEMETRY_FLUSH_INTERVAL = 10
|
|
@@ -51,67 +58,59 @@ module Datadog
|
|
|
51
58
|
serializer.global_tags
|
|
52
59
|
end
|
|
53
60
|
|
|
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
61
|
# Default sample rate
|
|
61
62
|
attr_reader :sample_rate
|
|
62
63
|
|
|
63
|
-
# Connection
|
|
64
|
-
attr_reader :connection
|
|
65
|
-
|
|
66
64
|
# @param [String] host your statsd host
|
|
67
65
|
# @param [Integer] port your statsd port
|
|
68
66
|
# @option [String] namespace set a namespace to be prepended to every metric name
|
|
69
67
|
# @option [Array<String>|Hash] tags tags to be added to every metric
|
|
70
68
|
# @option [Logger] logger for debugging
|
|
71
|
-
# @option [Integer]
|
|
69
|
+
# @option [Integer] buffer_max_payload_size max bytes to buffer
|
|
70
|
+
# @option [Integer] buffer_max_pool_size max messages to buffer
|
|
72
71
|
# @option [String] socket_path unix socket path
|
|
73
72
|
# @option [Float] default sample rate if not overridden
|
|
74
73
|
def initialize(
|
|
75
74
|
host = nil,
|
|
76
75
|
port = nil,
|
|
76
|
+
socket_path: nil,
|
|
77
|
+
|
|
77
78
|
namespace: nil,
|
|
78
79
|
tags: nil,
|
|
79
|
-
max_buffer_bytes: DEFAULT_BUFFER_SIZE,
|
|
80
|
-
socket_path: nil,
|
|
81
|
-
logger: nil,
|
|
82
80
|
sample_rate: nil,
|
|
83
|
-
|
|
81
|
+
|
|
82
|
+
buffer_max_payload_size: nil,
|
|
83
|
+
buffer_max_pool_size: nil,
|
|
84
|
+
buffer_overflowing_stategy: :drop,
|
|
85
|
+
|
|
86
|
+
logger: nil,
|
|
87
|
+
|
|
88
|
+
telemetry_enable: true,
|
|
84
89
|
telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL
|
|
85
90
|
)
|
|
86
91
|
unless tags.nil? || tags.is_a?(Array) || tags.is_a?(Hash)
|
|
87
|
-
raise ArgumentError, 'tags must be
|
|
92
|
+
raise ArgumentError, 'tags must be an array of string tags or a Hash'
|
|
88
93
|
end
|
|
89
94
|
|
|
90
95
|
@namespace = namespace
|
|
91
96
|
@prefix = @namespace ? "#{@namespace}.".freeze : nil
|
|
92
|
-
|
|
93
97
|
@serializer = Serialization::Serializer.new(prefix: @prefix, global_tags: tags)
|
|
98
|
+
@sample_rate = sample_rate
|
|
94
99
|
|
|
95
|
-
|
|
100
|
+
@forwarder = Forwarder.new(
|
|
101
|
+
host: host,
|
|
102
|
+
port: port,
|
|
103
|
+
socket_path: socket_path,
|
|
96
104
|
|
|
97
|
-
@telemetry = Telemetry.new(disable_telemetry, telemetry_flush_interval,
|
|
98
105
|
global_tags: tags,
|
|
99
|
-
|
|
100
|
-
)
|
|
101
|
-
|
|
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
|
|
106
|
+
logger: logger,
|
|
108
107
|
|
|
109
|
-
|
|
108
|
+
buffer_max_payload_size: buffer_max_payload_size,
|
|
109
|
+
buffer_max_pool_size: buffer_max_pool_size,
|
|
110
|
+
buffer_overflowing_stategy: buffer_overflowing_stategy,
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
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))
|
|
112
|
+
telemetry_flush_interval: telemetry_enable ? telemetry_flush_interval : nil,
|
|
113
|
+
)
|
|
115
114
|
end
|
|
116
115
|
|
|
117
116
|
# yield a new instance to a block and close it when done
|
|
@@ -270,9 +269,9 @@ module Datadog
|
|
|
270
269
|
# @example Report a critical service check status
|
|
271
270
|
# $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
|
|
272
271
|
def service_check(name, status, opts = EMPTY_OPTIONS)
|
|
273
|
-
telemetry.sent(service_checks: 1)
|
|
272
|
+
telemetry.sent(service_checks: 1) if telemetry
|
|
274
273
|
|
|
275
|
-
|
|
274
|
+
forwarder.send_message(serializer.to_service_check(name, status, opts))
|
|
276
275
|
end
|
|
277
276
|
|
|
278
277
|
# 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 +294,20 @@ module Datadog
|
|
|
295
294
|
# @example Report an awful event:
|
|
296
295
|
# $statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent'])
|
|
297
296
|
def event(title, text, opts = EMPTY_OPTIONS)
|
|
298
|
-
telemetry.sent(events: 1)
|
|
297
|
+
telemetry.sent(events: 1) if telemetry
|
|
299
298
|
|
|
300
|
-
|
|
299
|
+
forwarder.send_message(serializer.to_event(title, text, opts))
|
|
301
300
|
end
|
|
302
301
|
|
|
303
|
-
# Send several metrics in the same
|
|
304
|
-
# They will be buffered and flushed when the block finishes
|
|
302
|
+
# Send several metrics in the same packet.
|
|
303
|
+
# They will be buffered and flushed when the block finishes.
|
|
304
|
+
#
|
|
305
|
+
# This method exists for compatibility with v4.x versions, it is not needed
|
|
306
|
+
# anymore since the batching is now automatically done internally.
|
|
307
|
+
# It also means that an automatic flush could occur if the buffer is filled
|
|
308
|
+
# during the execution of the batch block.
|
|
309
|
+
#
|
|
310
|
+
# This method is DEPRECATED and will be removed in future v6.x API.
|
|
305
311
|
#
|
|
306
312
|
# @example Send several metrics in one packet:
|
|
307
313
|
# $statsd.batch do |s|
|
|
@@ -309,19 +315,47 @@ module Datadog
|
|
|
309
315
|
# s.increment('page.views')
|
|
310
316
|
# end
|
|
311
317
|
def batch
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
end
|
|
318
|
+
yield self
|
|
319
|
+
flush(sync: true)
|
|
315
320
|
end
|
|
316
321
|
|
|
317
322
|
# Close the underlying socket
|
|
318
323
|
def close
|
|
319
|
-
|
|
324
|
+
forwarder.close
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def sync_with_outbound_io
|
|
328
|
+
forwarder.sync_with_outbound_io
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Flush the buffer into the connection
|
|
332
|
+
def flush(flush_telemetry: false, sync: false)
|
|
333
|
+
forwarder.flush(flush_telemetry: flush_telemetry, sync: sync)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def telemetry
|
|
337
|
+
forwarder.telemetry
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def host
|
|
341
|
+
forwarder.host
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def port
|
|
345
|
+
forwarder.port
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def socket_path
|
|
349
|
+
forwarder.socket_path
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def transport_type
|
|
353
|
+
forwarder.transport_type
|
|
320
354
|
end
|
|
321
355
|
|
|
322
356
|
private
|
|
323
357
|
attr_reader :serializer
|
|
324
|
-
attr_reader :
|
|
358
|
+
attr_reader :forwarder
|
|
325
359
|
|
|
326
360
|
PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= '2.1.0')
|
|
327
361
|
EMPTY_OPTIONS = {}.freeze
|
|
@@ -337,22 +371,14 @@ module Datadog
|
|
|
337
371
|
end
|
|
338
372
|
|
|
339
373
|
def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
|
|
340
|
-
telemetry.sent(metrics: 1)
|
|
374
|
+
telemetry.sent(metrics: 1) if telemetry
|
|
341
375
|
|
|
342
376
|
sample_rate = opts[:sample_rate] || @sample_rate || 1
|
|
343
377
|
|
|
344
378
|
if sample_rate == 1 || rand <= sample_rate
|
|
345
379
|
full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
|
|
346
380
|
|
|
347
|
-
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
def send_stat(message)
|
|
352
|
-
if @batch.open?
|
|
353
|
-
@batch.add(message)
|
|
354
|
-
else
|
|
355
|
-
@connection.write(message)
|
|
381
|
+
forwarder.send_message(full_stat)
|
|
356
382
|
end
|
|
357
383
|
end
|
|
358
384
|
end
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
module Datadog
|
|
4
4
|
class Statsd
|
|
5
5
|
class Connection
|
|
6
|
-
def initialize(telemetry)
|
|
6
|
+
def initialize(telemetry: nil, logger: nil)
|
|
7
7
|
@telemetry = telemetry
|
|
8
|
+
@logger = logger
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
# Close the underlying socket
|
|
@@ -20,15 +21,11 @@ module Datadog
|
|
|
20
21
|
def write(payload)
|
|
21
22
|
logger.debug { "Statsd: #{payload}" } if logger
|
|
22
23
|
|
|
23
|
-
flush_telemetry = telemetry.flush?
|
|
24
|
-
|
|
25
|
-
payload += telemetry.flush if flush_telemetry
|
|
26
|
-
|
|
27
24
|
send_message(payload)
|
|
28
25
|
|
|
29
|
-
telemetry.
|
|
26
|
+
telemetry.sent(packets: 1, bytes: payload.length) if telemetry
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
true
|
|
32
29
|
rescue StandardError => boom
|
|
33
30
|
# Try once to reconnect if the socket has been closed
|
|
34
31
|
retries ||= 1
|
|
@@ -45,7 +42,7 @@ module Datadog
|
|
|
45
42
|
end
|
|
46
43
|
end
|
|
47
44
|
|
|
48
|
-
telemetry.dropped(packets: 1, bytes: payload.length)
|
|
45
|
+
telemetry.dropped(packets: 1, bytes: payload.length) if telemetry
|
|
49
46
|
logger.error { "Statsd: #{boom.class} #{boom}" } if logger
|
|
50
47
|
nil
|
|
51
48
|
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
class Statsd
|
|
5
|
+
class Forwarder
|
|
6
|
+
attr_reader :telemetry
|
|
7
|
+
attr_reader :transport_type
|
|
8
|
+
|
|
9
|
+
def initialize(
|
|
10
|
+
host: nil,
|
|
11
|
+
port: nil,
|
|
12
|
+
socket_path: nil,
|
|
13
|
+
|
|
14
|
+
buffer_max_payload_size: nil,
|
|
15
|
+
buffer_max_pool_size: nil,
|
|
16
|
+
buffer_overflowing_stategy: :drop,
|
|
17
|
+
|
|
18
|
+
telemetry_flush_interval: nil,
|
|
19
|
+
global_tags: [],
|
|
20
|
+
|
|
21
|
+
logger: nil
|
|
22
|
+
)
|
|
23
|
+
@transport_type = socket_path.nil? ? :udp : :uds
|
|
24
|
+
|
|
25
|
+
if telemetry_flush_interval
|
|
26
|
+
@telemetry = Telemetry.new(telemetry_flush_interval,
|
|
27
|
+
global_tags: global_tags,
|
|
28
|
+
transport_type: transport_type
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
@connection = case transport_type
|
|
33
|
+
when :udp
|
|
34
|
+
UDPConnection.new(host, port, logger: logger, telemetry: telemetry)
|
|
35
|
+
when :uds
|
|
36
|
+
UDSConnection.new(socket_path, logger: logger, telemetry: telemetry)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Initialize buffer
|
|
40
|
+
buffer_max_payload_size ||= (transport_type == :udp ? UDP_DEFAULT_BUFFER_SIZE : UDS_DEFAULT_BUFFER_SIZE)
|
|
41
|
+
|
|
42
|
+
if buffer_max_payload_size <= 0
|
|
43
|
+
raise ArgumentError, 'buffer_max_payload_size cannot be <= 0'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
unless telemetry.nil? || telemetry.would_fit_in?(buffer_max_payload_size)
|
|
47
|
+
raise ArgumentError, "buffer_max_payload_size is not high enough to use telemetry (tags=(#{global_tags.inspect}))"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@buffer = MessageBuffer.new(@connection,
|
|
51
|
+
max_payload_size: buffer_max_payload_size,
|
|
52
|
+
max_pool_size: buffer_max_pool_size || DEFAULT_BUFFER_POOL_SIZE,
|
|
53
|
+
overflowing_stategy: buffer_overflowing_stategy,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
@sender = Sender.new(buffer)
|
|
57
|
+
@sender.start
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def send_message(message)
|
|
61
|
+
sender.add(message)
|
|
62
|
+
|
|
63
|
+
tick_telemetry
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def sync_with_outbound_io
|
|
67
|
+
sender.rendez_vous
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def flush(flush_telemetry: false, sync: false)
|
|
71
|
+
do_flush_telemetry if telemetry && flush_telemetry
|
|
72
|
+
|
|
73
|
+
sender.flush(sync: sync)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def host
|
|
77
|
+
return nil unless transport_type == :udp
|
|
78
|
+
|
|
79
|
+
connection.host
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def port
|
|
83
|
+
return nil unless transport_type == :udp
|
|
84
|
+
|
|
85
|
+
connection.port
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def socket_path
|
|
89
|
+
return nil unless transport_type == :uds
|
|
90
|
+
|
|
91
|
+
connection.socket_path
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def close
|
|
95
|
+
sender.stop
|
|
96
|
+
connection.close
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
attr_reader :buffer
|
|
101
|
+
attr_reader :sender
|
|
102
|
+
attr_reader :connection
|
|
103
|
+
|
|
104
|
+
def do_flush_telemetry
|
|
105
|
+
telemetry_snapshot = telemetry.flush
|
|
106
|
+
telemetry.reset
|
|
107
|
+
|
|
108
|
+
telemetry_snapshot.each do |message|
|
|
109
|
+
sender.add(message)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def tick_telemetry
|
|
114
|
+
return nil unless telemetry
|
|
115
|
+
|
|
116
|
+
do_flush_telemetry if telemetry.should_flush?
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
class Statsd
|
|
5
|
+
class MessageBuffer
|
|
6
|
+
PAYLOAD_SIZE_TOLERANCE = 0.05
|
|
7
|
+
|
|
8
|
+
def initialize(connection,
|
|
9
|
+
max_payload_size: nil,
|
|
10
|
+
max_pool_size: DEFAULT_BUFFER_POOL_SIZE,
|
|
11
|
+
overflowing_stategy: :drop
|
|
12
|
+
)
|
|
13
|
+
raise ArgumentError, 'max_payload_size keyword argument must be provided' unless max_payload_size
|
|
14
|
+
raise ArgumentError, 'max_pool_size keyword argument must be provided' unless max_pool_size
|
|
15
|
+
|
|
16
|
+
@connection = connection
|
|
17
|
+
@max_payload_size = max_payload_size
|
|
18
|
+
@max_pool_size = max_pool_size
|
|
19
|
+
@overflowing_stategy = overflowing_stategy
|
|
20
|
+
|
|
21
|
+
@buffer = String.new
|
|
22
|
+
@message_count = 0
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add(message)
|
|
26
|
+
message_size = message.bytesize
|
|
27
|
+
|
|
28
|
+
return nil unless message_size > 0 # to avoid adding empty messages to the buffer
|
|
29
|
+
return nil unless ensure_sendable!(message_size)
|
|
30
|
+
|
|
31
|
+
flush if should_flush?(message_size)
|
|
32
|
+
|
|
33
|
+
buffer << "\n" unless buffer.empty?
|
|
34
|
+
buffer << message
|
|
35
|
+
|
|
36
|
+
@message_count += 1
|
|
37
|
+
|
|
38
|
+
# flush when we're pretty sure that we won't be able
|
|
39
|
+
# to add another message to the buffer
|
|
40
|
+
flush if preemptive_flush?
|
|
41
|
+
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def flush
|
|
46
|
+
return if buffer.empty?
|
|
47
|
+
|
|
48
|
+
connection.write(buffer)
|
|
49
|
+
|
|
50
|
+
buffer.clear
|
|
51
|
+
@message_count = 0
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
attr :max_payload_size
|
|
56
|
+
attr :max_pool_size
|
|
57
|
+
|
|
58
|
+
attr :overflowing_stategy
|
|
59
|
+
|
|
60
|
+
attr :connection
|
|
61
|
+
attr :buffer
|
|
62
|
+
|
|
63
|
+
def should_flush?(message_size)
|
|
64
|
+
return true if buffer.bytesize + 1 + message_size >= max_payload_size
|
|
65
|
+
|
|
66
|
+
false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def preemptive_flush?
|
|
70
|
+
@message_count == max_pool_size || buffer.bytesize > bytesize_threshold
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def ensure_sendable!(message_size)
|
|
74
|
+
return true if message_size <= max_payload_size
|
|
75
|
+
|
|
76
|
+
if overflowing_stategy == :raise
|
|
77
|
+
raise Error, 'Message too big for payload limit'
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
false
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def bytesize_threshold
|
|
84
|
+
@bytesize_threshold ||= (max_payload_size - PAYLOAD_SIZE_TOLERANCE * max_payload_size).to_i
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
class Statsd
|
|
5
|
+
class Sender
|
|
6
|
+
CLOSEABLE_QUEUES = Queue.instance_methods.include?(:close)
|
|
7
|
+
|
|
8
|
+
def initialize(message_buffer)
|
|
9
|
+
@message_buffer = message_buffer
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def flush(sync: false)
|
|
13
|
+
raise ArgumentError, 'Start sender first' unless message_queue
|
|
14
|
+
|
|
15
|
+
message_queue.push(:flush)
|
|
16
|
+
|
|
17
|
+
rendez_vous if sync
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def rendez_vous
|
|
21
|
+
# Initialize and get the thread's sync queue
|
|
22
|
+
queue = (Thread.current[:statsd_sync_queue] ||= Queue.new)
|
|
23
|
+
# tell sender-thread to notify us in the current
|
|
24
|
+
# thread's queue
|
|
25
|
+
message_queue.push(queue)
|
|
26
|
+
# wait for the sender thread to send a message
|
|
27
|
+
# once the flush is done
|
|
28
|
+
queue.pop
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def add(message)
|
|
32
|
+
raise ArgumentError, 'Start sender first' unless message_queue
|
|
33
|
+
|
|
34
|
+
message_queue << message
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def start
|
|
38
|
+
raise ArgumentError, 'Sender already started' if message_queue
|
|
39
|
+
|
|
40
|
+
# initialize message queue for background thread
|
|
41
|
+
@message_queue = Queue.new
|
|
42
|
+
# start background thread
|
|
43
|
+
@sender_thread = Thread.new(&method(:send_loop))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if CLOSEABLE_QUEUES
|
|
47
|
+
def stop(join_worker: true)
|
|
48
|
+
message_queue = @message_queue
|
|
49
|
+
message_queue.close if message_queue
|
|
50
|
+
|
|
51
|
+
sender_thread = @sender_thread
|
|
52
|
+
sender_thread.join if sender_thread && join_worker
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
def stop(join_worker: true)
|
|
56
|
+
message_queue = @message_queue
|
|
57
|
+
message_queue << :close if message_queue
|
|
58
|
+
|
|
59
|
+
sender_thread = @sender_thread
|
|
60
|
+
sender_thread.join if sender_thread && join_worker
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
attr_reader :message_buffer
|
|
67
|
+
|
|
68
|
+
attr_reader :message_queue
|
|
69
|
+
attr_reader :sender_thread
|
|
70
|
+
|
|
71
|
+
if CLOSEABLE_QUEUES
|
|
72
|
+
def send_loop
|
|
73
|
+
until (message = message_queue.pop).nil? && message_queue.closed?
|
|
74
|
+
# skip if message is nil, e.g. when message_queue
|
|
75
|
+
# is empty and closed
|
|
76
|
+
next unless message
|
|
77
|
+
|
|
78
|
+
case message
|
|
79
|
+
when :flush
|
|
80
|
+
message_buffer.flush
|
|
81
|
+
when Queue
|
|
82
|
+
message.push(:go_on)
|
|
83
|
+
else
|
|
84
|
+
message_buffer.add(message)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
@message_queue = nil
|
|
89
|
+
@sender_thread = nil
|
|
90
|
+
end
|
|
91
|
+
else
|
|
92
|
+
def send_loop
|
|
93
|
+
loop do
|
|
94
|
+
message = message_queue.pop
|
|
95
|
+
|
|
96
|
+
next unless message
|
|
97
|
+
|
|
98
|
+
case message
|
|
99
|
+
when :close
|
|
100
|
+
break
|
|
101
|
+
when :flush
|
|
102
|
+
message_buffer.flush
|
|
103
|
+
when Queue
|
|
104
|
+
message.push(:go_on)
|
|
105
|
+
else
|
|
106
|
+
message_buffer.add(message)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
@message_queue = nil
|
|
111
|
+
@sender_thread = nil
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -13,7 +13,11 @@ module Datadog
|
|
|
13
13
|
|
|
14
14
|
# Convert to tag list and set
|
|
15
15
|
@global_tags = to_tags_list(global_tags)
|
|
16
|
-
|
|
16
|
+
if @global_tags.any?
|
|
17
|
+
@global_tags_formatted = @global_tags.join(',')
|
|
18
|
+
else
|
|
19
|
+
@global_tags_formatted = nil
|
|
20
|
+
end
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
def format(message_tags)
|
|
@@ -11,10 +11,11 @@ module Datadog
|
|
|
11
11
|
attr_reader :bytes_dropped
|
|
12
12
|
attr_reader :packets_sent
|
|
13
13
|
attr_reader :packets_dropped
|
|
14
|
-
attr_reader :estimate_max_size
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
# Rough estimation of maximum telemetry message size without tags
|
|
16
|
+
MAX_TELEMETRY_MESSAGE_SIZE_WT_TAGS = 50 # bytes
|
|
17
|
+
|
|
18
|
+
def initialize(flush_interval, global_tags: [], transport_type: :udp)
|
|
18
19
|
@flush_interval = flush_interval
|
|
19
20
|
@global_tags = global_tags
|
|
20
21
|
@transport_type = transport_type
|
|
@@ -27,15 +28,10 @@ module Datadog
|
|
|
27
28
|
client_version: VERSION,
|
|
28
29
|
client_transport: transport_type,
|
|
29
30
|
).format(global_tags)
|
|
31
|
+
end
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
# 'max_buffer_bytes', we have to adjust with the size of the telemetry
|
|
34
|
-
# (and any tags used). The telemetry payload size will change depending
|
|
35
|
-
# on the actual value of metrics: metrics received, packet dropped,
|
|
36
|
-
# etc. This is why we add a 63bytes margin: 9 bytes for each of the 7
|
|
37
|
-
# telemetry metrics.
|
|
38
|
-
@estimate_max_size = disabled ? 0 : flush.length + 9 * 7
|
|
33
|
+
def would_fit_in?(max_buffer_payload_size)
|
|
34
|
+
MAX_TELEMETRY_MESSAGE_SIZE_WT_TAGS + serialized_tags.size < max_buffer_payload_size
|
|
39
35
|
end
|
|
40
36
|
|
|
41
37
|
def reset
|
|
@@ -63,27 +59,29 @@ module Datadog
|
|
|
63
59
|
@packets_dropped += packets
|
|
64
60
|
end
|
|
65
61
|
|
|
66
|
-
def
|
|
62
|
+
def should_flush?
|
|
67
63
|
@next_flush_time < now_in_s
|
|
68
64
|
end
|
|
69
65
|
|
|
70
66
|
def flush
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
datadog.dogstatsd.client.packets_sent:#{@packets_sent}|#{COUNTER_TYPE}|##{serialized_tags}
|
|
81
|
-
datadog.dogstatsd.client.packets_dropped:#{@packets_dropped}|#{COUNTER_TYPE}|##{serialized_tags})
|
|
67
|
+
[
|
|
68
|
+
sprintf(pattern, 'metrics', @metrics),
|
|
69
|
+
sprintf(pattern, 'events', @events),
|
|
70
|
+
sprintf(pattern, 'service_checks', @service_checks),
|
|
71
|
+
sprintf(pattern, 'bytes_sent', @bytes_sent),
|
|
72
|
+
sprintf(pattern, 'bytes_dropped', @bytes_dropped),
|
|
73
|
+
sprintf(pattern, 'packets_sent', @packets_sent),
|
|
74
|
+
sprintf(pattern, 'packets_dropped', @packets_dropped),
|
|
75
|
+
]
|
|
82
76
|
end
|
|
83
77
|
|
|
84
78
|
private
|
|
85
79
|
attr_reader :serialized_tags
|
|
86
80
|
|
|
81
|
+
def pattern
|
|
82
|
+
@pattern ||= "datadog.dogstatsd.client.%s:%d|#{COUNTER_TYPE}|##{serialized_tags}"
|
|
83
|
+
end
|
|
84
|
+
|
|
87
85
|
if Kernel.const_defined?('Process') && Process.respond_to?(:clock_gettime)
|
|
88
86
|
def now_in_s
|
|
89
87
|
Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
|
|
@@ -14,11 +14,11 @@ module Datadog
|
|
|
14
14
|
# StatsD port. Defaults to 8125.
|
|
15
15
|
attr_reader :port
|
|
16
16
|
|
|
17
|
-
def initialize(host, port,
|
|
18
|
-
super(
|
|
17
|
+
def initialize(host, port, **kwargs)
|
|
18
|
+
super(**kwargs)
|
|
19
|
+
|
|
19
20
|
@host = host || ENV.fetch('DD_AGENT_HOST', DEFAULT_HOST)
|
|
20
21
|
@port = port || ENV.fetch('DD_DOGSTATSD_PORT', DEFAULT_PORT).to_i
|
|
21
|
-
@logger = logger
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
private
|
|
@@ -10,10 +10,10 @@ module Datadog
|
|
|
10
10
|
# DogStatsd unix socket path
|
|
11
11
|
attr_reader :socket_path
|
|
12
12
|
|
|
13
|
-
def initialize(socket_path,
|
|
14
|
-
super(
|
|
13
|
+
def initialize(socket_path, **kwargs)
|
|
14
|
+
super(**kwargs)
|
|
15
|
+
|
|
15
16
|
@socket_path = socket_path
|
|
16
|
-
@logger = logger
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
private
|
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
|
+
version: 5.0.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-
|
|
12
|
+
date: 2021-04-09 00:00:00.000000000 Z
|
|
12
13
|
dependencies: []
|
|
13
|
-
description: A Ruby
|
|
14
|
+
description: A Ruby DogStatsd client
|
|
14
15
|
email: code@datadoghq.com
|
|
15
16
|
executables: []
|
|
16
17
|
extensions: []
|
|
@@ -21,8 +22,10 @@ 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/forwarder.rb
|
|
27
|
+
- lib/datadog/statsd/message_buffer.rb
|
|
28
|
+
- lib/datadog/statsd/sender.rb
|
|
26
29
|
- lib/datadog/statsd/serialization.rb
|
|
27
30
|
- lib/datadog/statsd/serialization/event_serializer.rb
|
|
28
31
|
- lib/datadog/statsd/serialization/serializer.rb
|
|
@@ -38,9 +41,9 @@ licenses:
|
|
|
38
41
|
- MIT
|
|
39
42
|
metadata:
|
|
40
43
|
bug_tracker_uri: https://github.com/DataDog/dogstatsd-ruby/issues
|
|
41
|
-
changelog_uri: https://github.com/DataDog/dogstatsd-ruby/blob/
|
|
42
|
-
documentation_uri: https://www.rubydoc.info/gems/dogstatsd-ruby/
|
|
43
|
-
source_code_uri: https://github.com/DataDog/dogstatsd-ruby/tree/
|
|
44
|
+
changelog_uri: https://github.com/DataDog/dogstatsd-ruby/blob/v5.0.1/CHANGELOG.md
|
|
45
|
+
documentation_uri: https://www.rubydoc.info/gems/dogstatsd-ruby/5.0.1
|
|
46
|
+
source_code_uri: https://github.com/DataDog/dogstatsd-ruby/tree/v5.0.1
|
|
44
47
|
post_install_message:
|
|
45
48
|
rdoc_options: []
|
|
46
49
|
require_paths:
|
data/lib/datadog/statsd/batch.rb
DELETED
|
@@ -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
|