dogstatsd-ruby 4.0.0 → 5.5.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.
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+ require 'time'
3
+
4
+ module Datadog
5
+ class Statsd
6
+ class Telemetry
7
+ attr_reader :metrics
8
+ attr_reader :events
9
+ attr_reader :service_checks
10
+ attr_reader :bytes_sent
11
+ attr_reader :bytes_dropped
12
+ attr_reader :bytes_dropped_queue
13
+ attr_reader :bytes_dropped_writer
14
+ attr_reader :packets_sent
15
+ attr_reader :packets_dropped
16
+ attr_reader :packets_dropped_queue
17
+ attr_reader :packets_dropped_writer
18
+
19
+ # Rough estimation of maximum telemetry message size without tags
20
+ MAX_TELEMETRY_MESSAGE_SIZE_WT_TAGS = 50 # bytes
21
+
22
+ def initialize(flush_interval, global_tags: [], transport_type: :udp)
23
+ @flush_interval = flush_interval
24
+ @global_tags = global_tags
25
+ @transport_type = transport_type
26
+ reset
27
+
28
+ # TODO: Karim: I don't know why but telemetry tags are serialized
29
+ # before global tags so by refactoring this, I am keeping the same behavior
30
+ @serialized_tags = Serialization::TagSerializer.new(
31
+ client: 'ruby',
32
+ client_version: VERSION,
33
+ client_transport: transport_type,
34
+ ).format(global_tags)
35
+ end
36
+
37
+ def would_fit_in?(max_buffer_payload_size)
38
+ MAX_TELEMETRY_MESSAGE_SIZE_WT_TAGS + serialized_tags.size < max_buffer_payload_size
39
+ end
40
+
41
+ def reset
42
+ @metrics = 0
43
+ @events = 0
44
+ @service_checks = 0
45
+ @bytes_sent = 0
46
+ @bytes_dropped = 0
47
+ @bytes_dropped_queue = 0
48
+ @bytes_dropped_writer = 0
49
+ @packets_sent = 0
50
+ @packets_dropped = 0
51
+ @packets_dropped_queue = 0
52
+ @packets_dropped_writer = 0
53
+ @next_flush_time = now_in_s + @flush_interval
54
+ end
55
+
56
+ def sent(metrics: 0, events: 0, service_checks: 0, bytes: 0, packets: 0)
57
+ @metrics += metrics
58
+ @events += events
59
+ @service_checks += service_checks
60
+
61
+ @bytes_sent += bytes
62
+ @packets_sent += packets
63
+ end
64
+
65
+ def dropped_queue(bytes: 0, packets: 0)
66
+ @bytes_dropped += bytes
67
+ @bytes_dropped_queue += bytes
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
77
+ end
78
+
79
+ def should_flush?
80
+ @next_flush_time < now_in_s
81
+ end
82
+
83
+ def flush
84
+ [
85
+ sprintf(pattern, 'metrics', @metrics),
86
+ sprintf(pattern, 'events', @events),
87
+ sprintf(pattern, 'service_checks', @service_checks),
88
+ sprintf(pattern, 'bytes_sent', @bytes_sent),
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),
92
+ sprintf(pattern, 'packets_sent', @packets_sent),
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),
96
+ ]
97
+ end
98
+
99
+ private
100
+ attr_reader :serialized_tags
101
+
102
+ def pattern
103
+ @pattern ||= "datadog.dogstatsd.client.%s:%d|#{COUNTER_TYPE}|##{serialized_tags}"
104
+ end
105
+
106
+ if Kernel.const_defined?('Process') && Process.respond_to?(:clock_gettime)
107
+ def now_in_s
108
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
109
+ end
110
+ else
111
+ def now_in_s
112
+ Time.now.to_i
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,61 @@
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
+ @thread = nil
13
+ end
14
+
15
+ def start
16
+ return unless stop?
17
+
18
+ @stop = false
19
+ @thread = Thread.new do
20
+ last_execution_time = current_time
21
+ @mx.synchronize do
22
+ until @stop
23
+ timeout = @interval - (current_time - last_execution_time)
24
+ @cv.wait(@mx, timeout > 0 ? timeout : 0)
25
+ last_execution_time = current_time
26
+ @callback.call
27
+ end
28
+ end
29
+ end
30
+ @thread.name = 'Statsd Timer' unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
31
+ end
32
+
33
+ def stop
34
+ return if @thread.nil?
35
+
36
+ @stop = true
37
+ @mx.synchronize do
38
+ @cv.signal
39
+ end
40
+ @thread.join
41
+ @thread = nil
42
+ end
43
+
44
+ def stop?
45
+ @thread.nil? || @thread.stop?
46
+ end
47
+
48
+ private
49
+
50
+ if Process.const_defined?(:CLOCK_MONOTONIC)
51
+ def current_time
52
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
53
+ end
54
+ else
55
+ def current_time
56
+ Time.now
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'connection'
4
+
5
+ module Datadog
6
+ class Statsd
7
+ class UDPConnection < Connection
8
+ # StatsD host.
9
+ attr_reader :host
10
+
11
+ # StatsD port.
12
+ attr_reader :port
13
+
14
+ def initialize(host, port, **kwargs)
15
+ super(**kwargs)
16
+
17
+ @host = host
18
+ @port = port
19
+ @socket = nil
20
+ end
21
+
22
+ def close
23
+ @socket.close if @socket
24
+ @socket = nil
25
+ end
26
+
27
+ private
28
+
29
+ def connect
30
+ close if @socket
31
+
32
+ @socket = UDPSocket.new
33
+ @socket.connect(host, port)
34
+ end
35
+
36
+ # send_message is writing the message in the socket, it may create the socket if nil
37
+ # It is not thread-safe but since it is called by either the Sender bg thread or the
38
+ # SingleThreadSender (which is using a mutex while Flushing), only one thread must call
39
+ # it at a time.
40
+ def send_message(message)
41
+ connect unless @socket
42
+ @socket.send(message, 0)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'connection'
4
+
5
+ module Datadog
6
+ class Statsd
7
+ class UDSConnection < Connection
8
+ class BadSocketError < StandardError; end
9
+
10
+ # DogStatsd unix socket path
11
+ attr_reader :socket_path
12
+
13
+ def initialize(socket_path, **kwargs)
14
+ super(**kwargs)
15
+
16
+ @socket_path = socket_path
17
+ @socket = nil
18
+ end
19
+
20
+ def close
21
+ @socket.close if @socket
22
+ @socket = nil
23
+ end
24
+
25
+ private
26
+
27
+ def connect
28
+ close if @socket
29
+
30
+ @socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
31
+ @socket.connect(Socket.pack_sockaddr_un(@socket_path))
32
+ end
33
+
34
+ # send_message is writing the message in the socket, it may create the socket if nil
35
+ # It is not thread-safe but since it is called by either the Sender bg thread or the
36
+ # SingleThreadSender (which is using a mutex while Flushing), only one thread must call
37
+ # it at a time.
38
+ def send_message(message)
39
+ connect unless @socket
40
+ @socket.sendmsg_nonblock(message)
41
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENOENT => e
42
+ # TODO: FIXME: This error should be considered as a retryable error in the
43
+ # Connection class. An even better solution would be to make BadSocketError inherit
44
+ # from a specific retryable error class in the Connection class.
45
+ raise BadSocketError, "#{e.class}: #{e}"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'connection'
4
+
5
+ module Datadog
6
+ class Statsd
7
+ VERSION = '5.5.0'
8
+ end
9
+ end