dogstatsd-ruby 5.3.1 → 5.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -1
- data/lib/datadog/statsd/connection.rb +1 -1
- data/lib/datadog/statsd/connection_cfg.rb +76 -0
- data/lib/datadog/statsd/forwarder.rb +24 -13
- data/lib/datadog/statsd/sender.rb +23 -7
- data/lib/datadog/statsd/single_thread_sender.rb +7 -3
- data/lib/datadog/statsd/telemetry.rb +22 -1
- data/lib/datadog/statsd/timer.rb +60 -0
- data/lib/datadog/statsd/udp_connection.rb +4 -7
- data/lib/datadog/statsd/version.rb +1 -1
- data/lib/datadog/statsd.rb +37 -33
- metadata +16 -12
- data/lib/datadog/statsd/threaded_sender.rb +0 -132
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d780ad7a840c021ee3cb31a9e8b512ef7ccff4b82b14be9e300206d3a07cc61e
|
4
|
+
data.tar.gz: 2da798fe3a84f313c5c84655136597c562e3dc1bfaa24f8ba6441ef722c7cc45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f92d7dab8099d571c291e26497020e53087ccdbeecfd84d955778f484112f9b94fb0f4ec7d043b9efabd5d249333168fa44e4fe6e54ba4c9541447b92983ceb
|
7
|
+
data.tar.gz: 8dadc086c84a821d4a1b1824a25ea4861bdf0b6a36cfcf8c50a8d06e19d6853c35974dbefb03fedc9ccd7a3f57ff13a19d206c5eef152c1aa5ffbc1f789da1bc
|
data/README.md
CHANGED
@@ -180,6 +180,15 @@ There are three different kinds of messages:
|
|
180
180
|
|
181
181
|
There is also an implicit message which closes the queue which will cause the sender thread to finish processing and exit.
|
182
182
|
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
statsd = Datadog::Statsd.new('localhost', 8125)
|
186
|
+
```
|
187
|
+
|
188
|
+
The message queue's maximum size (in messages) is given by the `sender_queue_size` argument, and has appropriate defaults for UDP (2048) and UDS (512).
|
189
|
+
|
190
|
+
The `buffer_flush_interval`, if enabled, is implemented with an additional thread which manages the timing of those flushes. This additional thread is used even if `single_thread: true`.
|
191
|
+
|
183
192
|
### Usual workflow
|
184
193
|
|
185
194
|
You push metrics to the statsd client which writes them quickly to the sender message queue. The sender thread receives those message, buffers them and flushes them to the connection when the buffer limit is reached.
|
@@ -204,7 +213,13 @@ When using the `single_thread: true` mode, instances of `Datadog::Statsd` are st
|
|
204
213
|
|
205
214
|
## Versioning
|
206
215
|
|
207
|
-
This Ruby gem is using [Semantic Versioning](https://guides.rubygems.org/patterns/#semantic-versioning) but please note that supported Ruby versions can change in a minor release of this library.
|
216
|
+
This Ruby gem is using [Semantic Versioning](https://guides.rubygems.org/patterns/#semantic-versioning) but please note that supported Ruby versions can change in a minor release of this library.
|
217
|
+
As much as possible, we will add a "future deprecation" message in the minor release preceding the one dropping the support.
|
218
|
+
|
219
|
+
## Ruby Versions
|
220
|
+
|
221
|
+
This gem supports and is tested on Ruby minor versions 2.1 through 3.1.
|
222
|
+
Support for Ruby 2.0 was dropped in version 5.4.0.
|
208
223
|
|
209
224
|
## Credits
|
210
225
|
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Datadog
|
2
|
+
class Statsd
|
3
|
+
class ConnectionCfg
|
4
|
+
attr_reader :host
|
5
|
+
attr_reader :port
|
6
|
+
attr_reader :socket_path
|
7
|
+
attr_reader :transport_type
|
8
|
+
|
9
|
+
def initialize(host: nil, port: nil, socket_path: nil)
|
10
|
+
initialize_with_constructor_args(host: host, port: port, socket_path: socket_path) ||
|
11
|
+
initialize_with_env_vars ||
|
12
|
+
initialize_with_defaults
|
13
|
+
end
|
14
|
+
|
15
|
+
def make_connection(**params)
|
16
|
+
case @transport_type
|
17
|
+
when :udp
|
18
|
+
UDPConnection.new(@host, @port, **params)
|
19
|
+
when :uds
|
20
|
+
UDSConnection.new(@socket_path, **params)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
DEFAULT_HOST = '127.0.0.1'
|
27
|
+
DEFAULT_PORT = 8125
|
28
|
+
|
29
|
+
def initialize_with_constructor_args(host: nil, port: nil, socket_path: nil)
|
30
|
+
try_initialize_with(host: host, port: port, socket_path: socket_path,
|
31
|
+
not_both_error_message:
|
32
|
+
"Both UDP: (host/port #{host}:#{port}) and UDS (socket_path #{socket_path}) " +
|
33
|
+
"constructor arguments were given. Use only one or the other.",
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize_with_env_vars()
|
38
|
+
try_initialize_with(
|
39
|
+
host: ENV['DD_AGENT_HOST'],
|
40
|
+
port: ENV['DD_DOGSTATSD_PORT'] && ENV['DD_DOGSTATSD_PORT'].to_i,
|
41
|
+
socket_path: ENV['DD_DOGSTATSD_SOCKET'],
|
42
|
+
not_both_error_message:
|
43
|
+
"Both UDP (DD_AGENT_HOST/DD_DOGSTATSD_PORT #{ENV['DD_AGENT_HOST']}:#{ENV['DD_DOGSTATSD_PORT']}) " +
|
44
|
+
"and UDS (DD_DOGSTATSD_SOCKET #{ENV['DD_DOGSTATSD_SOCKET']}) environment variables are set. " +
|
45
|
+
"Set only one or the other.",
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize_with_defaults()
|
50
|
+
try_initialize_with(host: DEFAULT_HOST, port: DEFAULT_PORT)
|
51
|
+
end
|
52
|
+
|
53
|
+
def try_initialize_with(host: nil, port: nil, socket_path: nil, not_both_error_message: "")
|
54
|
+
if (host || port) && socket_path
|
55
|
+
raise ArgumentError, not_both_error_message
|
56
|
+
end
|
57
|
+
|
58
|
+
if host || port
|
59
|
+
@host = host || DEFAULT_HOST
|
60
|
+
@port = port || DEFAULT_PORT
|
61
|
+
@socket_path = nil
|
62
|
+
@transport_type = :udp
|
63
|
+
return true
|
64
|
+
elsif socket_path
|
65
|
+
@host = nil
|
66
|
+
@port = nil
|
67
|
+
@socket_path = socket_path
|
68
|
+
@transport_type = :uds
|
69
|
+
return true
|
70
|
+
end
|
71
|
+
|
72
|
+
return false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -7,13 +7,14 @@ module Datadog
|
|
7
7
|
attr_reader :transport_type
|
8
8
|
|
9
9
|
def initialize(
|
10
|
-
|
11
|
-
port: nil,
|
12
|
-
socket_path: nil,
|
10
|
+
connection_cfg: nil,
|
13
11
|
|
14
12
|
buffer_max_payload_size: nil,
|
15
13
|
buffer_max_pool_size: nil,
|
16
14
|
buffer_overflowing_stategy: :drop,
|
15
|
+
buffer_flush_interval: nil,
|
16
|
+
|
17
|
+
sender_queue_size: nil,
|
17
18
|
|
18
19
|
telemetry_flush_interval: nil,
|
19
20
|
global_tags: [],
|
@@ -22,24 +23,20 @@ module Datadog
|
|
22
23
|
|
23
24
|
logger: nil
|
24
25
|
)
|
25
|
-
@transport_type =
|
26
|
+
@transport_type = connection_cfg.transport_type
|
26
27
|
|
27
28
|
if telemetry_flush_interval
|
28
29
|
@telemetry = Telemetry.new(telemetry_flush_interval,
|
29
30
|
global_tags: global_tags,
|
30
|
-
transport_type: transport_type
|
31
|
+
transport_type: @transport_type
|
31
32
|
)
|
32
33
|
end
|
33
34
|
|
34
|
-
@connection =
|
35
|
-
when :udp
|
36
|
-
UDPConnection.new(host, port, logger: logger, telemetry: telemetry)
|
37
|
-
when :uds
|
38
|
-
UDSConnection.new(socket_path, logger: logger, telemetry: telemetry)
|
39
|
-
end
|
35
|
+
@connection = connection_cfg.make_connection(logger: logger, telemetry: telemetry)
|
40
36
|
|
41
37
|
# Initialize buffer
|
42
|
-
buffer_max_payload_size ||= (transport_type == :udp ?
|
38
|
+
buffer_max_payload_size ||= (@transport_type == :udp ?
|
39
|
+
UDP_DEFAULT_BUFFER_SIZE : UDS_DEFAULT_BUFFER_SIZE)
|
43
40
|
|
44
41
|
if buffer_max_payload_size <= 0
|
45
42
|
raise ArgumentError, 'buffer_max_payload_size cannot be <= 0'
|
@@ -54,7 +51,21 @@ module Datadog
|
|
54
51
|
max_pool_size: buffer_max_pool_size || DEFAULT_BUFFER_POOL_SIZE,
|
55
52
|
overflowing_stategy: buffer_overflowing_stategy,
|
56
53
|
)
|
57
|
-
|
54
|
+
|
55
|
+
sender_queue_size ||= (@transport_type == :udp ?
|
56
|
+
UDP_DEFAULT_SENDER_QUEUE_SIZE : UDS_DEFAULT_SENDER_QUEUE_SIZE)
|
57
|
+
|
58
|
+
@sender = single_thread ?
|
59
|
+
SingleThreadSender.new(
|
60
|
+
buffer,
|
61
|
+
logger: logger,
|
62
|
+
flush_interval: buffer_flush_interval) :
|
63
|
+
Sender.new(
|
64
|
+
buffer,
|
65
|
+
logger: logger,
|
66
|
+
flush_interval: buffer_flush_interval,
|
67
|
+
telemetry: @telemetry,
|
68
|
+
queue_size: sender_queue_size)
|
58
69
|
@sender.start
|
59
70
|
end
|
60
71
|
|
@@ -12,10 +12,17 @@ module Datadog
|
|
12
12
|
class Sender
|
13
13
|
CLOSEABLE_QUEUES = Queue.instance_methods.include?(:close)
|
14
14
|
|
15
|
-
def initialize(message_buffer, logger: nil)
|
15
|
+
def initialize(message_buffer, telemetry: nil, queue_size: UDP_DEFAULT_BUFFER_SIZE, logger: nil, flush_interval: nil, queue_class: Queue, thread_class: Thread)
|
16
16
|
@message_buffer = message_buffer
|
17
|
+
@telemetry = telemetry
|
18
|
+
@queue_size = queue_size
|
17
19
|
@logger = logger
|
18
20
|
@mx = Mutex.new
|
21
|
+
@queue_class = queue_class
|
22
|
+
@thread_class = thread_class
|
23
|
+
if flush_interval
|
24
|
+
@flush_timer = Datadog::Statsd::Timer.new(flush_interval) { flush(sync: true) }
|
25
|
+
end
|
19
26
|
end
|
20
27
|
|
21
28
|
def flush(sync: false)
|
@@ -42,7 +49,7 @@ module Datadog
|
|
42
49
|
return unless message_queue
|
43
50
|
|
44
51
|
# Initialize and get the thread's sync queue
|
45
|
-
queue = (
|
52
|
+
queue = (@thread_class.current[:statsd_sync_queue] ||= @queue_class.new)
|
46
53
|
# tell sender-thread to notify us in the current
|
47
54
|
# thread's queue
|
48
55
|
message_queue.push(queue)
|
@@ -68,19 +75,26 @@ module Datadog
|
|
68
75
|
@message_queue = nil
|
69
76
|
message_buffer.reset
|
70
77
|
start
|
78
|
+
@flush_timer.start if @flush_timer && @flush_timer.stop?
|
71
79
|
}
|
72
80
|
end
|
73
81
|
|
74
|
-
message_queue
|
82
|
+
if message_queue.length <= @queue_size
|
83
|
+
message_queue << message
|
84
|
+
else
|
85
|
+
@telemetry.dropped_queue(packets: 1, bytes: message.bytesize) if @telemetry
|
86
|
+
end
|
75
87
|
end
|
76
88
|
|
77
89
|
def start
|
78
90
|
raise ArgumentError, 'Sender already started' if message_queue
|
79
91
|
|
80
92
|
# initialize a new message queue for the background thread
|
81
|
-
@message_queue =
|
93
|
+
@message_queue = @queue_class.new
|
82
94
|
# start background thread
|
83
|
-
@sender_thread =
|
95
|
+
@sender_thread = @thread_class.new(&method(:send_loop))
|
96
|
+
@sender_thread.name = "Statsd Sender" unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
|
97
|
+
@flush_timer.start if @flush_timer
|
84
98
|
end
|
85
99
|
|
86
100
|
if CLOSEABLE_QUEUES
|
@@ -91,6 +105,7 @@ module Datadog
|
|
91
105
|
message_queue = @message_queue
|
92
106
|
message_queue.close if message_queue
|
93
107
|
|
108
|
+
@flush_timer.stop if @flush_timer
|
94
109
|
sender_thread = @sender_thread
|
95
110
|
sender_thread.join if sender_thread && join_worker
|
96
111
|
end
|
@@ -102,6 +117,7 @@ module Datadog
|
|
102
117
|
message_queue = @message_queue
|
103
118
|
message_queue << :close if message_queue
|
104
119
|
|
120
|
+
@flush_timer.stop if @flush_timer
|
105
121
|
sender_thread = @sender_thread
|
106
122
|
sender_thread.join if sender_thread && join_worker
|
107
123
|
end
|
@@ -123,7 +139,7 @@ module Datadog
|
|
123
139
|
case message
|
124
140
|
when :flush
|
125
141
|
message_buffer.flush
|
126
|
-
when
|
142
|
+
when @queue_class
|
127
143
|
message.push(:go_on)
|
128
144
|
else
|
129
145
|
message_buffer.add(message)
|
@@ -145,7 +161,7 @@ module Datadog
|
|
145
161
|
break
|
146
162
|
when :flush
|
147
163
|
message_buffer.flush
|
148
|
-
when
|
164
|
+
when @queue_class
|
149
165
|
message.push(:go_on)
|
150
166
|
else
|
151
167
|
message_buffer.add(message)
|
@@ -7,10 +7,13 @@ module Datadog
|
|
7
7
|
# It is using current Process.PID to check it is the result of a recent fork
|
8
8
|
# and it is reseting the MessageBuffer if that's the case.
|
9
9
|
class SingleThreadSender
|
10
|
-
def initialize(message_buffer, logger: nil)
|
10
|
+
def initialize(message_buffer, logger: nil, flush_interval: nil)
|
11
11
|
@message_buffer = message_buffer
|
12
12
|
@logger = logger
|
13
13
|
@mx = Mutex.new
|
14
|
+
if flush_interval
|
15
|
+
@flush_timer = Datadog::Statsd::Timer.new(flush_interval) { flush }
|
16
|
+
end
|
14
17
|
# store the pid for which this sender has been created
|
15
18
|
update_fork_pid
|
16
19
|
end
|
@@ -21,6 +24,7 @@ module Datadog
|
|
21
24
|
# not send, they belong to the parent process, let's clear the buffer.
|
22
25
|
if forked?
|
23
26
|
@message_buffer.reset
|
27
|
+
@flush_timer.start if @flush_timer && @flush_timer.stop?
|
24
28
|
update_fork_pid
|
25
29
|
end
|
26
30
|
@message_buffer.add(message)
|
@@ -33,12 +37,12 @@ module Datadog
|
|
33
37
|
}
|
34
38
|
end
|
35
39
|
|
36
|
-
# Compatibility with `Sender`
|
37
40
|
def start()
|
41
|
+
@flush_timer.start if @flush_timer
|
38
42
|
end
|
39
43
|
|
40
|
-
# Compatibility with `Sender`
|
41
44
|
def stop()
|
45
|
+
@flush_timer.stop if @flush_timer
|
42
46
|
end
|
43
47
|
|
44
48
|
# Compatibility with `Sender`
|
@@ -9,8 +9,12 @@ module Datadog
|
|
9
9
|
attr_reader :service_checks
|
10
10
|
attr_reader :bytes_sent
|
11
11
|
attr_reader :bytes_dropped
|
12
|
+
attr_reader :bytes_dropped_queue
|
13
|
+
attr_reader :bytes_dropped_writer
|
12
14
|
attr_reader :packets_sent
|
13
15
|
attr_reader :packets_dropped
|
16
|
+
attr_reader :packets_dropped_queue
|
17
|
+
attr_reader :packets_dropped_writer
|
14
18
|
|
15
19
|
# Rough estimation of maximum telemetry message size without tags
|
16
20
|
MAX_TELEMETRY_MESSAGE_SIZE_WT_TAGS = 50 # bytes
|
@@ -40,8 +44,12 @@ module Datadog
|
|
40
44
|
@service_checks = 0
|
41
45
|
@bytes_sent = 0
|
42
46
|
@bytes_dropped = 0
|
47
|
+
@bytes_dropped_queue = 0
|
48
|
+
@bytes_dropped_writer = 0
|
43
49
|
@packets_sent = 0
|
44
50
|
@packets_dropped = 0
|
51
|
+
@packets_dropped_queue = 0
|
52
|
+
@packets_dropped_writer = 0
|
45
53
|
@next_flush_time = now_in_s + @flush_interval
|
46
54
|
end
|
47
55
|
|
@@ -54,9 +62,18 @@ module Datadog
|
|
54
62
|
@packets_sent += packets
|
55
63
|
end
|
56
64
|
|
57
|
-
def
|
65
|
+
def dropped_queue(bytes: 0, packets: 0)
|
58
66
|
@bytes_dropped += bytes
|
67
|
+
@bytes_dropped_queue += bytes
|
59
68
|
@packets_dropped += packets
|
69
|
+
@packets_dropped_queue += packets
|
70
|
+
end
|
71
|
+
|
72
|
+
def dropped_writer(bytes: 0, packets: 0)
|
73
|
+
@bytes_dropped += bytes
|
74
|
+
@bytes_dropped_writer += bytes
|
75
|
+
@packets_dropped += packets
|
76
|
+
@packets_dropped_writer += packets
|
60
77
|
end
|
61
78
|
|
62
79
|
def should_flush?
|
@@ -70,8 +87,12 @@ module Datadog
|
|
70
87
|
sprintf(pattern, 'service_checks', @service_checks),
|
71
88
|
sprintf(pattern, 'bytes_sent', @bytes_sent),
|
72
89
|
sprintf(pattern, 'bytes_dropped', @bytes_dropped),
|
90
|
+
sprintf(pattern, 'bytes_dropped_queue', @bytes_dropped_queue),
|
91
|
+
sprintf(pattern, 'bytes_dropped_writer', @bytes_dropped_writer),
|
73
92
|
sprintf(pattern, 'packets_sent', @packets_sent),
|
74
93
|
sprintf(pattern, 'packets_dropped', @packets_dropped),
|
94
|
+
sprintf(pattern, 'packets_dropped_queue', @packets_dropped_queue),
|
95
|
+
sprintf(pattern, 'packets_dropped_writer', @packets_dropped_writer),
|
75
96
|
]
|
76
97
|
end
|
77
98
|
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
class Statsd
|
5
|
+
class Timer
|
6
|
+
def initialize(interval, &callback)
|
7
|
+
@mx = Mutex.new
|
8
|
+
@cv = ConditionVariable.new
|
9
|
+
@interval = interval
|
10
|
+
@callback = callback
|
11
|
+
@stop = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
return unless stop?
|
16
|
+
|
17
|
+
@stop = false
|
18
|
+
@thread = Thread.new do
|
19
|
+
last_execution_time = current_time
|
20
|
+
@mx.synchronize do
|
21
|
+
until @stop
|
22
|
+
timeout = @interval - (current_time - last_execution_time)
|
23
|
+
@cv.wait(@mx, timeout > 0 ? timeout : 0)
|
24
|
+
last_execution_time = current_time
|
25
|
+
@callback.call
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
@thread.name = 'Statsd Timer' unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
|
30
|
+
end
|
31
|
+
|
32
|
+
def stop
|
33
|
+
return if @thread.nil?
|
34
|
+
|
35
|
+
@stop = true
|
36
|
+
@mx.synchronize do
|
37
|
+
@cv.signal
|
38
|
+
end
|
39
|
+
@thread.join
|
40
|
+
@thread = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def stop?
|
44
|
+
@thread.nil? || @thread.stop?
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
if Process.const_defined?(:CLOCK_MONOTONIC)
|
50
|
+
def current_time
|
51
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
def current_time
|
55
|
+
Time.now
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -5,20 +5,17 @@ require_relative 'connection'
|
|
5
5
|
module Datadog
|
6
6
|
class Statsd
|
7
7
|
class UDPConnection < Connection
|
8
|
-
|
9
|
-
DEFAULT_PORT = 8125
|
10
|
-
|
11
|
-
# StatsD host. Defaults to 127.0.0.1.
|
8
|
+
# StatsD host.
|
12
9
|
attr_reader :host
|
13
10
|
|
14
|
-
# StatsD port.
|
11
|
+
# StatsD port.
|
15
12
|
attr_reader :port
|
16
13
|
|
17
14
|
def initialize(host, port, **kwargs)
|
18
15
|
super(**kwargs)
|
19
16
|
|
20
|
-
@host = host
|
21
|
-
@port = port
|
17
|
+
@host = host
|
18
|
+
@port = port
|
22
19
|
@socket = nil
|
23
20
|
end
|
24
21
|
|
data/lib/datadog/statsd.rb
CHANGED
@@ -5,14 +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/connection_cfg'
|
8
9
|
require_relative 'statsd/message_buffer'
|
9
10
|
require_relative 'statsd/serialization'
|
10
11
|
require_relative 'statsd/sender'
|
11
12
|
require_relative 'statsd/single_thread_sender'
|
12
13
|
require_relative 'statsd/forwarder'
|
13
|
-
|
14
|
-
$deprecation_message_mutex = Mutex.new
|
15
|
-
$deprecation_message_done = false
|
14
|
+
require_relative 'statsd/timer'
|
16
15
|
|
17
16
|
# = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
|
18
17
|
#
|
@@ -43,7 +42,12 @@ module Datadog
|
|
43
42
|
UDP_DEFAULT_BUFFER_SIZE = 1_432
|
44
43
|
UDS_DEFAULT_BUFFER_SIZE = 8_192
|
45
44
|
DEFAULT_BUFFER_POOL_SIZE = Float::INFINITY
|
45
|
+
|
46
|
+
UDP_DEFAULT_SENDER_QUEUE_SIZE = 2048
|
47
|
+
UDS_DEFAULT_SENDER_QUEUE_SIZE = 512
|
48
|
+
|
46
49
|
MAX_EVENT_SIZE = 8 * 1_024
|
50
|
+
|
47
51
|
# minimum flush interval for the telemetry in seconds
|
48
52
|
DEFAULT_TELEMETRY_FLUSH_INTERVAL = 10
|
49
53
|
|
@@ -72,6 +76,8 @@ module Datadog
|
|
72
76
|
# @option [Logger] logger for debugging
|
73
77
|
# @option [Integer] buffer_max_payload_size max bytes to buffer
|
74
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 (multi-thread only)
|
80
|
+
# @option [Numeric] buffer_flush_interval interval in second to flush buffer
|
75
81
|
# @option [String] socket_path unix socket path
|
76
82
|
# @option [Float] default sample rate if not overridden
|
77
83
|
# @option [Boolean] single_thread flushes the metrics on the main thread instead of in a companion thread
|
@@ -87,6 +93,9 @@ module Datadog
|
|
87
93
|
buffer_max_payload_size: nil,
|
88
94
|
buffer_max_pool_size: nil,
|
89
95
|
buffer_overflowing_stategy: :drop,
|
96
|
+
buffer_flush_interval: nil,
|
97
|
+
|
98
|
+
sender_queue_size: nil,
|
90
99
|
|
91
100
|
logger: nil,
|
92
101
|
|
@@ -104,23 +113,12 @@ module Datadog
|
|
104
113
|
@serializer = Serialization::Serializer.new(prefix: @prefix, global_tags: tags)
|
105
114
|
@sample_rate = sample_rate
|
106
115
|
|
107
|
-
# deprecation message for ruby < 2.1.0 users as we will drop support for ruby 2.0
|
108
|
-
# in dogstatsd-ruby 5.4.0
|
109
|
-
# TODO(remy): remove this message and the two global vars used in dogstatd-ruby 5.4.0
|
110
|
-
if RUBY_VERSION < '2.1.0' && $deprecation_message_mutex.try_lock && !$deprecation_message_done
|
111
|
-
if logger != nil
|
112
|
-
logger.warn { "deprecation: dogstatsd-ruby will drop support of Ruby < 2.1.0 in a next minor release" }
|
113
|
-
else
|
114
|
-
puts("warning: deprecation: dogstatsd-ruby will drop support of Ruby < 2.1.0 in a next minor release")
|
115
|
-
end
|
116
|
-
$deprecation_message_done = true
|
117
|
-
$deprecation_message_mutex.unlock
|
118
|
-
end
|
119
|
-
|
120
116
|
@forwarder = Forwarder.new(
|
121
|
-
|
122
|
-
|
123
|
-
|
117
|
+
connection_cfg: ConnectionCfg.new(
|
118
|
+
host: host,
|
119
|
+
port: port,
|
120
|
+
socket_path: socket_path,
|
121
|
+
),
|
124
122
|
|
125
123
|
global_tags: tags,
|
126
124
|
logger: logger,
|
@@ -130,6 +128,9 @@ module Datadog
|
|
130
128
|
buffer_max_payload_size: buffer_max_payload_size,
|
131
129
|
buffer_max_pool_size: buffer_max_pool_size,
|
132
130
|
buffer_overflowing_stategy: buffer_overflowing_stategy,
|
131
|
+
buffer_flush_interval: buffer_flush_interval,
|
132
|
+
|
133
|
+
sender_queue_size: sender_queue_size,
|
133
134
|
|
134
135
|
telemetry_flush_interval: telemetry_enable ? telemetry_flush_interval : nil,
|
135
136
|
)
|
@@ -137,12 +138,13 @@ module Datadog
|
|
137
138
|
|
138
139
|
# yield a new instance to a block and close it when done
|
139
140
|
# for short-term use-cases that don't want to close the socket manually
|
140
|
-
|
141
|
-
|
141
|
+
# TODO: replace with ... once we are on ruby 2.7
|
142
|
+
def self.open(*args, **kwargs)
|
143
|
+
instance = new(*args, **kwargs)
|
142
144
|
|
143
145
|
yield instance
|
144
146
|
ensure
|
145
|
-
instance.close
|
147
|
+
instance.close if instance
|
146
148
|
end
|
147
149
|
|
148
150
|
# Sends an increment (count = 1) for the given stat to the statsd server.
|
@@ -150,6 +152,7 @@ module Datadog
|
|
150
152
|
# @param [String] stat stat name
|
151
153
|
# @param [Hash] opts the options to create the metric with
|
152
154
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
155
|
+
# @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
|
153
156
|
# @option opts [Array<String>] :tags An array of tags
|
154
157
|
# @option opts [Numeric] :by increment value, default 1
|
155
158
|
# @see #count
|
@@ -164,6 +167,7 @@ module Datadog
|
|
164
167
|
# @param [String] stat stat name
|
165
168
|
# @param [Hash] opts the options to create the metric with
|
166
169
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
170
|
+
# @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
|
167
171
|
# @option opts [Array<String>] :tags An array of tags
|
168
172
|
# @option opts [Numeric] :by decrement value, default 1
|
169
173
|
# @see #count
|
@@ -179,13 +183,14 @@ module Datadog
|
|
179
183
|
# @param [Integer] count count
|
180
184
|
# @param [Hash] opts the options to create the metric with
|
181
185
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
186
|
+
# @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
|
182
187
|
# @option opts [Array<String>] :tags An array of tags
|
183
188
|
def count(stat, count, opts = EMPTY_OPTIONS)
|
184
189
|
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
185
190
|
send_stats(stat, count, COUNTER_TYPE, opts)
|
186
191
|
end
|
187
192
|
|
188
|
-
# Sends an
|
193
|
+
# Sends an arbitrary gauge value for the given stat to the statsd server.
|
189
194
|
#
|
190
195
|
# This is useful for recording things like available disk space,
|
191
196
|
# memory usage, and the like, which have different semantics than
|
@@ -195,6 +200,7 @@ module Datadog
|
|
195
200
|
# @param [Numeric] value gauge value.
|
196
201
|
# @param [Hash] opts the options to create the metric with
|
197
202
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
203
|
+
# @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
|
198
204
|
# @option opts [Array<String>] :tags An array of tags
|
199
205
|
# @example Report the current user count:
|
200
206
|
# $statsd.gauge('user.count', User.count)
|
@@ -209,6 +215,7 @@ module Datadog
|
|
209
215
|
# @param [Numeric] value histogram value.
|
210
216
|
# @param [Hash] opts the options to create the metric with
|
211
217
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
218
|
+
# @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
|
212
219
|
# @option opts [Array<String>] :tags An array of tags
|
213
220
|
# @example Report the current user count:
|
214
221
|
# $statsd.histogram('user.count', User.count)
|
@@ -222,6 +229,7 @@ module Datadog
|
|
222
229
|
# @param [Numeric] value distribution value.
|
223
230
|
# @param [Hash] opts the options to create the metric with
|
224
231
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
232
|
+
# @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
|
225
233
|
# @option opts [Array<String>] :tags An array of tags
|
226
234
|
# @example Report the current user count:
|
227
235
|
# $statsd.distribution('user.count', User.count)
|
@@ -238,6 +246,7 @@ module Datadog
|
|
238
246
|
# @param [Integer] ms timing in milliseconds
|
239
247
|
# @param [Hash] opts the options to create the metric with
|
240
248
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
249
|
+
# @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
|
241
250
|
# @option opts [Array<String>] :tags An array of tags
|
242
251
|
def timing(stat, ms, opts = EMPTY_OPTIONS)
|
243
252
|
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
@@ -252,6 +261,7 @@ module Datadog
|
|
252
261
|
# @param [String] stat stat name
|
253
262
|
# @param [Hash] opts the options to create the metric with
|
254
263
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
264
|
+
# @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
|
255
265
|
# @option opts [Array<String>] :tags An array of tags
|
256
266
|
# @yield The operation to be timed
|
257
267
|
# @see #timing
|
@@ -271,6 +281,7 @@ module Datadog
|
|
271
281
|
# @param [Numeric] value set value.
|
272
282
|
# @param [Hash] opts the options to create the metric with
|
273
283
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
284
|
+
# @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
|
274
285
|
# @option opts [Array<String>] :tags An array of tags
|
275
286
|
# @example Record a unique visitory by id:
|
276
287
|
# $statsd.set('visitors.uniques', User.id)
|
@@ -382,17 +393,10 @@ module Datadog
|
|
382
393
|
attr_reader :serializer
|
383
394
|
attr_reader :forwarder
|
384
395
|
|
385
|
-
PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= '2.1.0')
|
386
396
|
EMPTY_OPTIONS = {}.freeze
|
387
397
|
|
388
|
-
|
389
|
-
|
390
|
-
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
391
|
-
end
|
392
|
-
else
|
393
|
-
def now
|
394
|
-
Time.now.to_f
|
395
|
-
end
|
398
|
+
def now
|
399
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
396
400
|
end
|
397
401
|
|
398
402
|
def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
|
@@ -400,7 +404,7 @@ module Datadog
|
|
400
404
|
|
401
405
|
sample_rate = opts[:sample_rate] || @sample_rate || 1
|
402
406
|
|
403
|
-
if sample_rate == 1 || rand <= sample_rate
|
407
|
+
if sample_rate == 1 || opts[:pre_sampled] || rand <= sample_rate
|
404
408
|
full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
|
405
409
|
|
406
410
|
forwarder.send_message(full_stat)
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dogstatsd-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rein Henrichs
|
8
8
|
- Karim Bogtob
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-03-01 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: A Ruby DogStatsd client
|
15
15
|
email: code@datadoghq.com
|
@@ -23,6 +23,7 @@ files:
|
|
23
23
|
- README.md
|
24
24
|
- lib/datadog/statsd.rb
|
25
25
|
- lib/datadog/statsd/connection.rb
|
26
|
+
- lib/datadog/statsd/connection_cfg.rb
|
26
27
|
- lib/datadog/statsd/forwarder.rb
|
27
28
|
- lib/datadog/statsd/message_buffer.rb
|
28
29
|
- lib/datadog/statsd/sender.rb
|
@@ -34,7 +35,7 @@ files:
|
|
34
35
|
- lib/datadog/statsd/serialization/tag_serializer.rb
|
35
36
|
- lib/datadog/statsd/single_thread_sender.rb
|
36
37
|
- lib/datadog/statsd/telemetry.rb
|
37
|
-
- lib/datadog/statsd/
|
38
|
+
- lib/datadog/statsd/timer.rb
|
38
39
|
- lib/datadog/statsd/udp_connection.rb
|
39
40
|
- lib/datadog/statsd/uds_connection.rb
|
40
41
|
- lib/datadog/statsd/version.rb
|
@@ -43,10 +44,14 @@ licenses:
|
|
43
44
|
- MIT
|
44
45
|
metadata:
|
45
46
|
bug_tracker_uri: https://github.com/DataDog/dogstatsd-ruby/issues
|
46
|
-
changelog_uri: https://github.com/DataDog/dogstatsd-ruby/blob/v5.
|
47
|
-
documentation_uri: https://www.rubydoc.info/gems/dogstatsd-ruby/5.
|
48
|
-
source_code_uri: https://github.com/DataDog/dogstatsd-ruby/tree/v5.
|
49
|
-
post_install_message:
|
47
|
+
changelog_uri: https://github.com/DataDog/dogstatsd-ruby/blob/v5.4.0/CHANGELOG.md
|
48
|
+
documentation_uri: https://www.rubydoc.info/gems/dogstatsd-ruby/5.4.0
|
49
|
+
source_code_uri: https://github.com/DataDog/dogstatsd-ruby/tree/v5.4.0
|
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
|
+
|
50
55
|
rdoc_options: []
|
51
56
|
require_paths:
|
52
57
|
- lib
|
@@ -54,16 +59,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
59
|
requirements:
|
55
60
|
- - ">="
|
56
61
|
- !ruby/object:Gem::Version
|
57
|
-
version: 2.
|
62
|
+
version: 2.1.0
|
58
63
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
64
|
requirements:
|
60
65
|
- - ">="
|
61
66
|
- !ruby/object:Gem::Version
|
62
67
|
version: '0'
|
63
68
|
requirements: []
|
64
|
-
|
65
|
-
|
66
|
-
signing_key:
|
69
|
+
rubygems_version: 3.2.22
|
70
|
+
signing_key:
|
67
71
|
specification_version: 4
|
68
72
|
summary: A Ruby DogStatsd client
|
69
73
|
test_files: []
|
@@ -1,132 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Datadog
|
4
|
-
class FlushQueue < Queue
|
5
|
-
end
|
6
|
-
class CloseQueue < Queue
|
7
|
-
end
|
8
|
-
class Statsd
|
9
|
-
# Sender is using a background thread to flush and pack messages
|
10
|
-
# in a `MessageBuffer`.
|
11
|
-
# The communication with this thread is done using a `Queue`.
|
12
|
-
# If the thread is dead, it is starting a new one to avoid having a blocked
|
13
|
-
# Sender with no background thread to communicate with (most of the time,
|
14
|
-
# having a dead background thread means that a fork just happened and that we
|
15
|
-
# are running in the child process).
|
16
|
-
class Sender
|
17
|
-
CLOSEABLE_QUEUES = Queue.instance_methods.include?(:close)
|
18
|
-
|
19
|
-
def initialize(message_buffer, logger: nil)
|
20
|
-
@message_buffer = message_buffer
|
21
|
-
@logger = logger
|
22
|
-
|
23
|
-
# communication and synchronization with the background thread
|
24
|
-
# @mux is also used to not having multiple threads fighting for
|
25
|
-
# closing the Sender or creating a new background thread
|
26
|
-
@channel = Queue.new
|
27
|
-
@mux = Mutex.new
|
28
|
-
|
29
|
-
@is_closed = false
|
30
|
-
|
31
|
-
# start background thread immediately
|
32
|
-
@sender_thread = Thread.new(&method(:send_loop))
|
33
|
-
end
|
34
|
-
|
35
|
-
def flush(sync: false)
|
36
|
-
@mux.synchronize {
|
37
|
-
# we don't want to send a flush action to the bg thread if:
|
38
|
-
# - there is no bg thread running
|
39
|
-
# - the sender has been closed
|
40
|
-
return if !sender_thread.alive? || @is_closed
|
41
|
-
|
42
|
-
if sync
|
43
|
-
# blocking flush
|
44
|
-
blocking_queue = FlushQueue.new
|
45
|
-
channel << blocking_queue
|
46
|
-
blocking_queue.pop # wait for the bg thread to finish its work
|
47
|
-
blocking_queue.close if CLOSEABLE_QUEUES
|
48
|
-
else
|
49
|
-
# asynchronous flush
|
50
|
-
channel << :flush
|
51
|
-
end
|
52
|
-
}
|
53
|
-
end
|
54
|
-
|
55
|
-
def add(message)
|
56
|
-
return if @is_closed # don't send a message to the bg thread if the sender has been closed
|
57
|
-
|
58
|
-
# the bg thread is not running anymore, this is happening if the main process has forked and
|
59
|
-
# we are running in the child, we will spawn a bg thread and reset buffers (containing parents' messages)
|
60
|
-
if !sender_thread.alive?
|
61
|
-
@mux.synchronize {
|
62
|
-
return if @is_closed
|
63
|
-
# test if a call from another thread has already re-created
|
64
|
-
# the background thread before this one acquired the lock
|
65
|
-
break if sender_thread.alive?
|
66
|
-
|
67
|
-
# re-create the channel of communication since we will spawn a new bg thread
|
68
|
-
channel.close if CLOSEABLE_QUEUES
|
69
|
-
@channel = Queue.new
|
70
|
-
message_buffer.reset # don't use messages appended by another fork
|
71
|
-
@sender_thread = Thread.new(&method(:send_loop))
|
72
|
-
}
|
73
|
-
end
|
74
|
-
|
75
|
-
channel << message
|
76
|
-
end
|
77
|
-
|
78
|
-
# Compatibility with `Sender`
|
79
|
-
def start()
|
80
|
-
end
|
81
|
-
|
82
|
-
def stop()
|
83
|
-
return if @is_closed
|
84
|
-
# use this lock to both: not having another thread stopping this instance nor
|
85
|
-
# having a #add call creating a new thread
|
86
|
-
@mux.synchronize {
|
87
|
-
@is_closed = true
|
88
|
-
if sender_thread.alive? # no reasons to stop the bg thread is none is running already
|
89
|
-
blocking_queue = CloseQueue.new
|
90
|
-
channel << blocking_queue
|
91
|
-
blocking_queue.pop # wait for the bg thread to finish its work
|
92
|
-
blocking_queue.close if CLOSEABLE_QUEUES
|
93
|
-
sender_thread.join(3) # wait for completion, timeout after 3 seconds
|
94
|
-
# TODO(remy): should I close `channel` here?
|
95
|
-
end
|
96
|
-
}
|
97
|
-
end
|
98
|
-
|
99
|
-
private
|
100
|
-
|
101
|
-
attr_reader :message_buffer
|
102
|
-
attr_reader :channel
|
103
|
-
attr_reader :mux
|
104
|
-
attr_reader :sender_thread
|
105
|
-
|
106
|
-
def send_loop
|
107
|
-
until (message = channel.pop).nil? && (CLOSEABLE_QUEUES && channel.closed?)
|
108
|
-
# skip if message is nil, e.g. when the channel is empty and closed
|
109
|
-
next unless message
|
110
|
-
|
111
|
-
case message
|
112
|
-
# if a FlushQueue is received, the background thread has to flush the message
|
113
|
-
# buffer and to send an :unblock to let the caller know that it has finished
|
114
|
-
when FlushQueue
|
115
|
-
message_buffer.flush
|
116
|
-
message << :unblock
|
117
|
-
# if a :flush is received, the background thread has to flush asynchronously
|
118
|
-
when :flush
|
119
|
-
message_buffer.flush
|
120
|
-
# if a CloseQueue is received, the background thread has to do a last flush
|
121
|
-
# and to send an :unblock to let the caller know that it has finished
|
122
|
-
when CloseQueue
|
123
|
-
message << :unblock
|
124
|
-
return
|
125
|
-
else
|
126
|
-
message_buffer.add(message)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|