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.
- checksums.yaml +4 -4
- data/README.md +186 -57
- data/lib/datadog/statsd/connection.rb +60 -0
- data/lib/datadog/statsd/connection_cfg.rb +76 -0
- data/lib/datadog/statsd/forwarder.rb +133 -0
- data/lib/datadog/statsd/message_buffer.rb +97 -0
- data/lib/datadog/statsd/sender.rb +181 -0
- data/lib/datadog/statsd/serialization/event_serializer.rb +71 -0
- data/lib/datadog/statsd/serialization/serializer.rb +41 -0
- data/lib/datadog/statsd/serialization/service_check_serializer.rb +60 -0
- data/lib/datadog/statsd/serialization/stat_serializer.rb +55 -0
- data/lib/datadog/statsd/serialization/tag_serializer.rb +96 -0
- data/lib/datadog/statsd/serialization.rb +15 -0
- data/lib/datadog/statsd/single_thread_sender.rb +68 -0
- data/lib/datadog/statsd/telemetry.rb +117 -0
- data/lib/datadog/statsd/timer.rb +61 -0
- data/lib/datadog/statsd/udp_connection.rb +46 -0
- data/lib/datadog/statsd/uds_connection.rb +49 -0
- data/lib/datadog/statsd/version.rb +9 -0
- data/lib/datadog/statsd.rb +209 -333
- metadata +38 -11
data/lib/datadog/statsd.rb
CHANGED
@@ -1,6 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'socket'
|
3
3
|
|
4
|
+
require_relative 'statsd/version'
|
5
|
+
require_relative 'statsd/telemetry'
|
6
|
+
require_relative 'statsd/udp_connection'
|
7
|
+
require_relative 'statsd/uds_connection'
|
8
|
+
require_relative 'statsd/connection_cfg'
|
9
|
+
require_relative 'statsd/message_buffer'
|
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'
|
15
|
+
|
4
16
|
# = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
|
5
17
|
#
|
6
18
|
# @example Set up a global Statsd client for a server on localhost:8125
|
@@ -19,219 +31,120 @@ require 'socket'
|
|
19
31
|
# statsd = Datadog::Statsd.new 'localhost', 8125, tags: 'tag1:true'
|
20
32
|
module Datadog
|
21
33
|
class Statsd
|
22
|
-
|
23
|
-
class Connection
|
24
|
-
DEFAULT_HOST = '127.0.0.1'
|
25
|
-
DEFAULT_PORT = 8125
|
26
|
-
|
27
|
-
# StatsD host. Defaults to 127.0.0.1.
|
28
|
-
attr_reader :host
|
29
|
-
|
30
|
-
# StatsD port. Defaults to 8125.
|
31
|
-
attr_reader :port
|
32
|
-
|
33
|
-
# DogStatsd unix socket path. Not used by default.
|
34
|
-
attr_reader :socket_path
|
35
|
-
|
36
|
-
def initialize(host, port, socket_path, logger)
|
37
|
-
@host = host || DEFAULT_HOST
|
38
|
-
@port = port || DEFAULT_PORT
|
39
|
-
@socket_path = socket_path
|
40
|
-
@logger = logger
|
41
|
-
end
|
42
|
-
|
43
|
-
def write(message)
|
44
|
-
@logger.debug { "Statsd: #{message}" } if @logger
|
45
|
-
if @socket_path.nil?
|
46
|
-
socket.send(message, 0)
|
47
|
-
else
|
48
|
-
socket.sendmsg_nonblock(message)
|
49
|
-
end
|
50
|
-
rescue StandardError => boom
|
51
|
-
# Give up on this socket if it looks like it is bad
|
52
|
-
bad_socket = !@socket_path.nil? && (
|
53
|
-
boom.is_a?(Errno::ECONNREFUSED) ||
|
54
|
-
boom.is_a?(Errno::ECONNRESET) ||
|
55
|
-
boom.is_a?(Errno::ENOENT)
|
56
|
-
)
|
57
|
-
if bad_socket
|
58
|
-
@socket = nil
|
59
|
-
return
|
60
|
-
end
|
61
|
-
|
62
|
-
# Try once to reconnect if the socket has been closed
|
63
|
-
retries ||= 1
|
64
|
-
if retries <= 1 && boom.is_a?(IOError) && boom.message =~ /closed stream/i
|
65
|
-
retries += 1
|
66
|
-
begin
|
67
|
-
@socket = connect
|
68
|
-
retry
|
69
|
-
rescue StandardError => e
|
70
|
-
boom = e
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
@logger.error { "Statsd: #{boom.class} #{boom}" } if @logger
|
75
|
-
nil
|
76
|
-
end
|
77
|
-
|
78
|
-
# Close the underlying socket
|
79
|
-
def close
|
80
|
-
@socket && @socket.close
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
|
85
|
-
def socket
|
86
|
-
@socket ||= connect
|
87
|
-
end
|
88
|
-
|
89
|
-
def connect
|
90
|
-
if @socket_path.nil?
|
91
|
-
socket = UDPSocket.new
|
92
|
-
socket.connect(@host, @port)
|
93
|
-
else
|
94
|
-
socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
|
95
|
-
socket.connect(Socket.pack_sockaddr_un(@socket_path))
|
96
|
-
end
|
97
|
-
socket
|
98
|
-
end
|
34
|
+
class Error < StandardError
|
99
35
|
end
|
100
36
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
@depth = 0
|
106
|
-
reset
|
107
|
-
end
|
108
|
-
|
109
|
-
def open
|
110
|
-
@depth += 1
|
111
|
-
yield
|
112
|
-
ensure
|
113
|
-
@depth -= 1
|
114
|
-
flush if !open?
|
115
|
-
end
|
116
|
-
|
117
|
-
def open?
|
118
|
-
@depth > 0
|
119
|
-
end
|
120
|
-
|
121
|
-
def add(message)
|
122
|
-
message_bytes = message.bytesize
|
37
|
+
OK = 0
|
38
|
+
WARNING = 1
|
39
|
+
CRITICAL = 2
|
40
|
+
UNKNOWN = 3
|
123
41
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
else
|
128
|
-
@buffer << NEW_LINE
|
129
|
-
@buffer_bytes += 1
|
130
|
-
end
|
131
|
-
end
|
42
|
+
UDP_DEFAULT_BUFFER_SIZE = 1_432
|
43
|
+
UDS_DEFAULT_BUFFER_SIZE = 8_192
|
44
|
+
DEFAULT_BUFFER_POOL_SIZE = Float::INFINITY
|
132
45
|
|
133
|
-
|
134
|
-
|
135
|
-
end
|
136
|
-
|
137
|
-
def flush
|
138
|
-
return if @buffer_bytes == 0
|
139
|
-
@connection.write @buffer
|
140
|
-
reset
|
141
|
-
end
|
46
|
+
UDP_DEFAULT_SENDER_QUEUE_SIZE = 2048
|
47
|
+
UDS_DEFAULT_SENDER_QUEUE_SIZE = 512
|
142
48
|
|
143
|
-
|
49
|
+
MAX_EVENT_SIZE = 8 * 1_024
|
144
50
|
|
145
|
-
|
146
|
-
|
147
|
-
@buffer_bytes = 0
|
148
|
-
end
|
149
|
-
end
|
51
|
+
# minimum flush interval for the telemetry in seconds
|
52
|
+
DEFAULT_TELEMETRY_FLUSH_INTERVAL = 10
|
150
53
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
:priority => :p,
|
158
|
-
:source_type_name => :s,
|
159
|
-
:alert_type => :t,
|
160
|
-
}
|
161
|
-
|
162
|
-
# Service check options
|
163
|
-
SC_OPT_KEYS = {
|
164
|
-
:timestamp => 'd:'.freeze,
|
165
|
-
:hostname => 'h:'.freeze,
|
166
|
-
:tags => '#'.freeze,
|
167
|
-
:message => 'm:'.freeze,
|
168
|
-
}
|
169
|
-
|
170
|
-
OK = 0
|
171
|
-
WARNING = 1
|
172
|
-
CRITICAL = 2
|
173
|
-
UNKNOWN = 3
|
174
|
-
|
175
|
-
MAX_EVENT_SIZE = 8 * 1024
|
176
|
-
|
177
|
-
COUNTER_TYPE = 'c'.freeze
|
178
|
-
GAUGE_TYPE = 'g'.freeze
|
179
|
-
HISTOGRAM_TYPE = 'h'.freeze
|
180
|
-
DISTRIBUTION_TYPE = 'd'.freeze
|
181
|
-
TIMING_TYPE = 'ms'.freeze
|
182
|
-
SET_TYPE = 's'.freeze
|
183
|
-
VERSION = "4.0.0".freeze
|
54
|
+
COUNTER_TYPE = 'c'
|
55
|
+
GAUGE_TYPE = 'g'
|
56
|
+
HISTOGRAM_TYPE = 'h'
|
57
|
+
DISTRIBUTION_TYPE = 'd'
|
58
|
+
TIMING_TYPE = 'ms'
|
59
|
+
SET_TYPE = 's'
|
184
60
|
|
185
61
|
# A namespace to prepend to all statsd calls. Defaults to no namespace.
|
186
62
|
attr_reader :namespace
|
187
63
|
|
188
64
|
# Global tags to be added to every statsd call. Defaults to no tags.
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
attr_reader :buffer
|
193
|
-
|
194
|
-
# Maximum buffer size in bytes before it is flushed
|
195
|
-
attr_reader :max_buffer_bytes
|
65
|
+
def tags
|
66
|
+
serializer.global_tags
|
67
|
+
end
|
196
68
|
|
197
|
-
#
|
198
|
-
attr_reader :
|
69
|
+
# Default sample rate
|
70
|
+
attr_reader :sample_rate
|
199
71
|
|
200
72
|
# @param [String] host your statsd host
|
201
73
|
# @param [Integer] port your statsd port
|
202
74
|
# @option [String] namespace set a namespace to be prepended to every metric name
|
203
|
-
# @option [Array<String
|
204
|
-
# @option [
|
205
|
-
# @option [Integer]
|
75
|
+
# @option [Array<String>|Hash] tags tags to be added to every metric
|
76
|
+
# @option [Logger] logger for debugging
|
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 (multi-thread only)
|
80
|
+
# @option [Numeric] buffer_flush_interval interval in second to flush buffer
|
206
81
|
# @option [String] socket_path unix socket path
|
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
|
207
84
|
def initialize(
|
208
85
|
host = nil,
|
209
86
|
port = nil,
|
87
|
+
socket_path: nil,
|
88
|
+
|
210
89
|
namespace: nil,
|
211
90
|
tags: nil,
|
212
|
-
|
213
|
-
|
214
|
-
|
91
|
+
sample_rate: nil,
|
92
|
+
|
93
|
+
buffer_max_payload_size: nil,
|
94
|
+
buffer_max_pool_size: nil,
|
95
|
+
buffer_overflowing_stategy: :drop,
|
96
|
+
buffer_flush_interval: nil,
|
97
|
+
|
98
|
+
sender_queue_size: nil,
|
99
|
+
|
100
|
+
logger: nil,
|
101
|
+
|
102
|
+
single_thread: false,
|
103
|
+
|
104
|
+
telemetry_enable: true,
|
105
|
+
telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL
|
215
106
|
)
|
216
|
-
|
217
|
-
|
107
|
+
unless tags.nil? || tags.is_a?(Array) || tags.is_a?(Hash)
|
108
|
+
raise ArgumentError, 'tags must be an array of string tags or a Hash'
|
109
|
+
end
|
218
110
|
|
219
111
|
@namespace = namespace
|
220
112
|
@prefix = @namespace ? "#{@namespace}.".freeze : nil
|
113
|
+
@serializer = Serialization::Serializer.new(prefix: @prefix, global_tags: tags)
|
114
|
+
@sample_rate = sample_rate
|
115
|
+
|
116
|
+
@forwarder = Forwarder.new(
|
117
|
+
connection_cfg: ConnectionCfg.new(
|
118
|
+
host: host,
|
119
|
+
port: port,
|
120
|
+
socket_path: socket_path,
|
121
|
+
),
|
221
122
|
|
222
|
-
|
223
|
-
|
123
|
+
global_tags: tags,
|
124
|
+
logger: logger,
|
224
125
|
|
225
|
-
|
126
|
+
single_thread: single_thread,
|
127
|
+
|
128
|
+
buffer_max_payload_size: buffer_max_payload_size,
|
129
|
+
buffer_max_pool_size: buffer_max_pool_size,
|
130
|
+
buffer_overflowing_stategy: buffer_overflowing_stategy,
|
131
|
+
buffer_flush_interval: buffer_flush_interval,
|
132
|
+
|
133
|
+
sender_queue_size: sender_queue_size,
|
134
|
+
|
135
|
+
telemetry_flush_interval: telemetry_enable ? telemetry_flush_interval : nil,
|
136
|
+
)
|
226
137
|
end
|
227
138
|
|
228
139
|
# yield a new instance to a block and close it when done
|
229
140
|
# for short-term use-cases that don't want to close the socket manually
|
230
|
-
|
231
|
-
|
141
|
+
# TODO: replace with ... once we are on ruby 2.7
|
142
|
+
def self.open(*args, **kwargs)
|
143
|
+
instance = new(*args, **kwargs)
|
144
|
+
|
232
145
|
yield instance
|
233
146
|
ensure
|
234
|
-
instance.close
|
147
|
+
instance.close if instance
|
235
148
|
end
|
236
149
|
|
237
150
|
# Sends an increment (count = 1) for the given stat to the statsd server.
|
@@ -239,13 +152,14 @@ module Datadog
|
|
239
152
|
# @param [String] stat stat name
|
240
153
|
# @param [Hash] opts the options to create the metric with
|
241
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.
|
242
156
|
# @option opts [Array<String>] :tags An array of tags
|
243
157
|
# @option opts [Numeric] :by increment value, default 1
|
244
158
|
# @see #count
|
245
|
-
def increment(stat, opts=EMPTY_OPTIONS)
|
246
|
-
opts = {:
|
159
|
+
def increment(stat, opts = EMPTY_OPTIONS)
|
160
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
247
161
|
incr_value = opts.fetch(:by, 1)
|
248
|
-
count
|
162
|
+
count(stat, incr_value, opts)
|
249
163
|
end
|
250
164
|
|
251
165
|
# Sends a decrement (count = -1) for the given stat to the statsd server.
|
@@ -253,13 +167,14 @@ module Datadog
|
|
253
167
|
# @param [String] stat stat name
|
254
168
|
# @param [Hash] opts the options to create the metric with
|
255
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.
|
256
171
|
# @option opts [Array<String>] :tags An array of tags
|
257
172
|
# @option opts [Numeric] :by decrement value, default 1
|
258
173
|
# @see #count
|
259
|
-
def decrement(stat, opts=EMPTY_OPTIONS)
|
260
|
-
opts = {:
|
174
|
+
def decrement(stat, opts = EMPTY_OPTIONS)
|
175
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
261
176
|
decr_value = - opts.fetch(:by, 1)
|
262
|
-
count
|
177
|
+
count(stat, decr_value, opts)
|
263
178
|
end
|
264
179
|
|
265
180
|
# Sends an arbitrary count for the given stat to the statsd server.
|
@@ -268,13 +183,14 @@ module Datadog
|
|
268
183
|
# @param [Integer] count count
|
269
184
|
# @param [Hash] opts the options to create the metric with
|
270
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.
|
271
187
|
# @option opts [Array<String>] :tags An array of tags
|
272
|
-
def count(stat, count, opts=EMPTY_OPTIONS)
|
273
|
-
opts = {:
|
274
|
-
send_stats
|
188
|
+
def count(stat, count, opts = EMPTY_OPTIONS)
|
189
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
190
|
+
send_stats(stat, count, COUNTER_TYPE, opts)
|
275
191
|
end
|
276
192
|
|
277
|
-
# Sends an
|
193
|
+
# Sends an arbitrary gauge value for the given stat to the statsd server.
|
278
194
|
#
|
279
195
|
# This is useful for recording things like available disk space,
|
280
196
|
# memory usage, and the like, which have different semantics than
|
@@ -284,12 +200,13 @@ module Datadog
|
|
284
200
|
# @param [Numeric] value gauge value.
|
285
201
|
# @param [Hash] opts the options to create the metric with
|
286
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.
|
287
204
|
# @option opts [Array<String>] :tags An array of tags
|
288
205
|
# @example Report the current user count:
|
289
206
|
# $statsd.gauge('user.count', User.count)
|
290
|
-
def gauge(stat, value, opts=EMPTY_OPTIONS)
|
291
|
-
opts = {:
|
292
|
-
send_stats
|
207
|
+
def gauge(stat, value, opts = EMPTY_OPTIONS)
|
208
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
209
|
+
send_stats(stat, value, GAUGE_TYPE, opts)
|
293
210
|
end
|
294
211
|
|
295
212
|
# Sends a value to be tracked as a histogram to the statsd server.
|
@@ -298,27 +215,46 @@ module Datadog
|
|
298
215
|
# @param [Numeric] value histogram value.
|
299
216
|
# @param [Hash] opts the options to create the metric with
|
300
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.
|
301
219
|
# @option opts [Array<String>] :tags An array of tags
|
302
220
|
# @example Report the current user count:
|
303
221
|
# $statsd.histogram('user.count', User.count)
|
304
|
-
def histogram(stat, value, opts=EMPTY_OPTIONS)
|
305
|
-
send_stats
|
222
|
+
def histogram(stat, value, opts = EMPTY_OPTIONS)
|
223
|
+
send_stats(stat, value, HISTOGRAM_TYPE, opts)
|
306
224
|
end
|
307
225
|
|
308
226
|
# Sends a value to be tracked as a distribution to the statsd server.
|
309
|
-
# Note: Distributions are a beta feature of Datadog and not generally
|
310
|
-
# available. Distributions must be specifically enabled for your
|
311
|
-
# organization.
|
312
227
|
#
|
313
228
|
# @param [String] stat stat name.
|
314
229
|
# @param [Numeric] value distribution value.
|
315
230
|
# @param [Hash] opts the options to create the metric with
|
316
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.
|
317
233
|
# @option opts [Array<String>] :tags An array of tags
|
318
234
|
# @example Report the current user count:
|
319
235
|
# $statsd.distribution('user.count', User.count)
|
320
|
-
def distribution(stat, value, opts=EMPTY_OPTIONS)
|
321
|
-
send_stats
|
236
|
+
def distribution(stat, value, opts = EMPTY_OPTIONS)
|
237
|
+
send_stats(stat, value, DISTRIBUTION_TYPE, opts)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Reports execution time of the provided block as a distribution.
|
241
|
+
#
|
242
|
+
# If the block fails, the stat is still reported, then the error
|
243
|
+
# is reraised
|
244
|
+
#
|
245
|
+
# @param [String] stat stat name.
|
246
|
+
# @param [Numeric] value distribution value.
|
247
|
+
# @param [Hash] opts the options to create the metric with
|
248
|
+
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
249
|
+
# @option opts [Array<String>] :tags An array of tags
|
250
|
+
# @example Report the time (in ms) taken to activate an account
|
251
|
+
# $statsd.distribution_time('account.activate') { @account.activate! }
|
252
|
+
def distribution_time(stat, opts = EMPTY_OPTIONS)
|
253
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
254
|
+
start = now
|
255
|
+
yield
|
256
|
+
ensure
|
257
|
+
distribution(stat, ((now - start) * 1000).round, opts)
|
322
258
|
end
|
323
259
|
|
324
260
|
# Sends a timing (in ms) for the given stat to the statsd server. The
|
@@ -330,10 +266,11 @@ module Datadog
|
|
330
266
|
# @param [Integer] ms timing in milliseconds
|
331
267
|
# @param [Hash] opts the options to create the metric with
|
332
268
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
269
|
+
# @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
|
333
270
|
# @option opts [Array<String>] :tags An array of tags
|
334
|
-
def timing(stat, ms, opts=EMPTY_OPTIONS)
|
335
|
-
opts = {:
|
336
|
-
send_stats
|
271
|
+
def timing(stat, ms, opts = EMPTY_OPTIONS)
|
272
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
273
|
+
send_stats(stat, ms, TIMING_TYPE, opts)
|
337
274
|
end
|
338
275
|
|
339
276
|
# Reports execution time of the provided block using {#timing}.
|
@@ -344,18 +281,18 @@ module Datadog
|
|
344
281
|
# @param [String] stat stat name
|
345
282
|
# @param [Hash] opts the options to create the metric with
|
346
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.
|
347
285
|
# @option opts [Array<String>] :tags An array of tags
|
348
286
|
# @yield The operation to be timed
|
349
287
|
# @see #timing
|
350
288
|
# @example Report the time (in ms) taken to activate an account
|
351
289
|
# $statsd.time('account.activate') { @account.activate! }
|
352
|
-
def time(stat, opts=EMPTY_OPTIONS)
|
353
|
-
opts = {:
|
354
|
-
start =
|
355
|
-
|
290
|
+
def time(stat, opts = EMPTY_OPTIONS)
|
291
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
292
|
+
start = now
|
293
|
+
yield
|
356
294
|
ensure
|
357
|
-
|
358
|
-
timing(stat, ((finished - start) * 1000).round, opts)
|
295
|
+
timing(stat, ((now - start) * 1000).round, opts)
|
359
296
|
end
|
360
297
|
|
361
298
|
# Sends a value to be tracked as a set to the statsd server.
|
@@ -364,12 +301,13 @@ module Datadog
|
|
364
301
|
# @param [Numeric] value set value.
|
365
302
|
# @param [Hash] opts the options to create the metric with
|
366
303
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
304
|
+
# @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn't perform sampling.
|
367
305
|
# @option opts [Array<String>] :tags An array of tags
|
368
306
|
# @example Record a unique visitory by id:
|
369
307
|
# $statsd.set('visitors.uniques', User.id)
|
370
|
-
def set(stat, value, opts=EMPTY_OPTIONS)
|
371
|
-
opts = {:
|
372
|
-
send_stats
|
308
|
+
def set(stat, value, opts = EMPTY_OPTIONS)
|
309
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
310
|
+
send_stats(stat, value, SET_TYPE, opts)
|
373
311
|
end
|
374
312
|
|
375
313
|
# This method allows you to send custom service check statuses.
|
@@ -377,14 +315,16 @@ module Datadog
|
|
377
315
|
# @param [String] name Service check name
|
378
316
|
# @param [String] status Service check status.
|
379
317
|
# @param [Hash] opts the additional data about the service check
|
380
|
-
# @option opts [Integer, nil] :timestamp (nil) Assign a timestamp to the
|
381
|
-
# @option opts [String, nil] :hostname (nil) Assign a hostname to the
|
318
|
+
# @option opts [Integer, String, nil] :timestamp (nil) Assign a timestamp to the service check. Default is now when none
|
319
|
+
# @option opts [String, nil] :hostname (nil) Assign a hostname to the service check.
|
382
320
|
# @option opts [Array<String>, nil] :tags (nil) An array of tags
|
383
321
|
# @option opts [String, nil] :message (nil) A message to associate with this service check status
|
384
322
|
# @example Report a critical service check status
|
385
323
|
# $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
|
386
|
-
def service_check(name, status, opts=EMPTY_OPTIONS)
|
387
|
-
|
324
|
+
def service_check(name, status, opts = EMPTY_OPTIONS)
|
325
|
+
telemetry.sent(service_checks: 1) if telemetry
|
326
|
+
|
327
|
+
forwarder.send_message(serializer.to_service_check(name, status, opts))
|
388
328
|
end
|
389
329
|
|
390
330
|
# This end point allows you to post events to the stream. You can tag them, set priority and even aggregate them with other events.
|
@@ -394,23 +334,33 @@ module Datadog
|
|
394
334
|
# it will be grouped with other events that don't have an event type.
|
395
335
|
#
|
396
336
|
# @param [String] title Event title
|
397
|
-
# @param [String] text Event text. Supports
|
337
|
+
# @param [String] text Event text. Supports newlines (+\n+)
|
398
338
|
# @param [Hash] opts the additional data about the event
|
399
|
-
# @option opts [Integer, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
|
339
|
+
# @option opts [Integer, String, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
|
400
340
|
# @option opts [String, nil] :hostname (nil) Assign a hostname to the event.
|
401
341
|
# @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others
|
402
342
|
# @option opts [String, nil] :priority ('normal') Can be "normal" or "low"
|
403
343
|
# @option opts [String, nil] :source_type_name (nil) Assign a source type to the event
|
404
344
|
# @option opts [String, nil] :alert_type ('info') Can be "error", "warning", "info" or "success".
|
345
|
+
# @option opts [Boolean, false] :truncate_if_too_long (false) Truncate the event if it is too long
|
405
346
|
# @option opts [Array<String>] :tags tags to be added to every metric
|
406
347
|
# @example Report an awful event:
|
407
348
|
# $statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent'])
|
408
|
-
def event(title, text, opts=EMPTY_OPTIONS)
|
409
|
-
|
349
|
+
def event(title, text, opts = EMPTY_OPTIONS)
|
350
|
+
telemetry.sent(events: 1) if telemetry
|
351
|
+
|
352
|
+
forwarder.send_message(serializer.to_event(title, text, opts))
|
410
353
|
end
|
411
354
|
|
412
|
-
# Send several metrics in the same
|
413
|
-
# They will be buffered and flushed when the block finishes
|
355
|
+
# Send several metrics in the same packet.
|
356
|
+
# They will be buffered and flushed when the block finishes.
|
357
|
+
#
|
358
|
+
# This method exists for compatibility with v4.x versions, it is not needed
|
359
|
+
# anymore since the batching is now automatically done internally.
|
360
|
+
# It also means that an automatic flush could occur if the buffer is filled
|
361
|
+
# during the execution of the batch block.
|
362
|
+
#
|
363
|
+
# This method is DEPRECATED and will be removed in future v6.x API.
|
414
364
|
#
|
415
365
|
# @example Send several metrics in one packet:
|
416
366
|
# $statsd.batch do |s|
|
@@ -418,140 +368,66 @@ module Datadog
|
|
418
368
|
# s.increment('page.views')
|
419
369
|
# end
|
420
370
|
def batch
|
421
|
-
|
371
|
+
yield self
|
372
|
+
flush(sync: true)
|
422
373
|
end
|
423
374
|
|
424
375
|
# Close the underlying socket
|
425
|
-
|
426
|
-
|
376
|
+
#
|
377
|
+
# @param [Boolean, true] flush Should we flush the metrics before closing
|
378
|
+
def close(flush: true)
|
379
|
+
flush(sync: true) if flush
|
380
|
+
forwarder.close
|
427
381
|
end
|
428
382
|
|
429
|
-
|
430
|
-
|
431
|
-
NEW_LINE = "\n".freeze
|
432
|
-
ESC_NEW_LINE = "\\n".freeze
|
433
|
-
COMMA = ",".freeze
|
434
|
-
PIPE = "|".freeze
|
435
|
-
DOT = ".".freeze
|
436
|
-
DOUBLE_COLON = "::".freeze
|
437
|
-
UNDERSCORE = "_".freeze
|
438
|
-
PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= "2.1.0")
|
439
|
-
EMPTY_OPTIONS = {}.freeze
|
440
|
-
|
441
|
-
private_constant :NEW_LINE, :ESC_NEW_LINE, :COMMA, :PIPE, :DOT,
|
442
|
-
:DOUBLE_COLON, :UNDERSCORE, :EMPTY_OPTIONS
|
443
|
-
|
444
|
-
def format_service_check(name, status, opts=EMPTY_OPTIONS)
|
445
|
-
sc_string = "_sc|#{name}|#{status}".dup
|
446
|
-
|
447
|
-
SC_OPT_KEYS.each do |key, shorthand_key|
|
448
|
-
next unless opts[key]
|
449
|
-
|
450
|
-
if key == :tags
|
451
|
-
if tags_string = tags_as_string(opts)
|
452
|
-
sc_string << "|##{tags_string}"
|
453
|
-
end
|
454
|
-
elsif key == :message
|
455
|
-
message = remove_pipes(opts[:message])
|
456
|
-
escaped_message = escape_service_check_message(message)
|
457
|
-
sc_string << "|m:#{escaped_message}"
|
458
|
-
else
|
459
|
-
value = remove_pipes(opts[key])
|
460
|
-
sc_string << "|#{shorthand_key}#{value}"
|
461
|
-
end
|
462
|
-
end
|
463
|
-
sc_string
|
383
|
+
def sync_with_outbound_io
|
384
|
+
forwarder.sync_with_outbound_io
|
464
385
|
end
|
465
386
|
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
event_string_data = "_e{#{escaped_title.length},#{escaped_text.length}}:#{escaped_title}|#{escaped_text}".dup
|
470
|
-
|
471
|
-
# We construct the string to be sent by adding '|key:value' parts to it when needed
|
472
|
-
# All pipes ('|') in the metadata are removed. Title and Text can keep theirs
|
473
|
-
OPTS_KEYS.each do |key, shorthand_key|
|
474
|
-
if key != :tags && opts[key]
|
475
|
-
value = remove_pipes(opts[key])
|
476
|
-
event_string_data << "|#{shorthand_key}:#{value}"
|
477
|
-
end
|
478
|
-
end
|
479
|
-
|
480
|
-
# Tags are joined and added as last part to the string to be sent
|
481
|
-
if tags_string = tags_as_string(opts)
|
482
|
-
event_string_data << "|##{tags_string}"
|
483
|
-
end
|
484
|
-
|
485
|
-
raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.length > MAX_EVENT_SIZE
|
486
|
-
event_string_data
|
387
|
+
# Flush the buffer into the connection
|
388
|
+
def flush(flush_telemetry: false, sync: false)
|
389
|
+
forwarder.flush(flush_telemetry: flush_telemetry, sync: sync)
|
487
390
|
end
|
488
391
|
|
489
|
-
def
|
490
|
-
|
491
|
-
tag_arr = tag_arr.map { |tag| escape_tag_content(tag) }
|
492
|
-
tag_arr = tags + tag_arr # @tags are normalized when set, so not need to normalize them again
|
493
|
-
else
|
494
|
-
tag_arr = tags
|
495
|
-
end
|
496
|
-
tag_arr.join(COMMA) unless tag_arr.empty?
|
392
|
+
def telemetry
|
393
|
+
forwarder.telemetry
|
497
394
|
end
|
498
395
|
|
499
|
-
def
|
500
|
-
|
396
|
+
def host
|
397
|
+
forwarder.host
|
501
398
|
end
|
502
399
|
|
503
|
-
def
|
504
|
-
|
505
|
-
tag.delete! COMMA
|
506
|
-
tag
|
400
|
+
def port
|
401
|
+
forwarder.port
|
507
402
|
end
|
508
403
|
|
509
|
-
def
|
510
|
-
|
404
|
+
def socket_path
|
405
|
+
forwarder.socket_path
|
511
406
|
end
|
512
407
|
|
513
|
-
def
|
514
|
-
|
408
|
+
def transport_type
|
409
|
+
forwarder.transport_type
|
515
410
|
end
|
516
411
|
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
stat.gsub!(DOUBLE_COLON, DOT)
|
526
|
-
stat.tr!(':|@'.freeze, UNDERSCORE)
|
527
|
-
full_stat << stat
|
528
|
-
|
529
|
-
full_stat << ':'.freeze
|
530
|
-
full_stat << delta.to_s
|
531
|
-
full_stat << PIPE
|
532
|
-
full_stat << type
|
533
|
-
|
534
|
-
unless sample_rate == 1
|
535
|
-
full_stat << PIPE
|
536
|
-
full_stat << '@'.freeze
|
537
|
-
full_stat << sample_rate.to_s
|
538
|
-
end
|
539
|
-
|
540
|
-
if tags_string = tags_as_string(opts)
|
541
|
-
full_stat << PIPE
|
542
|
-
full_stat << '#'.freeze
|
543
|
-
full_stat << tags_string
|
544
|
-
end
|
545
|
-
|
546
|
-
send_stat(full_stat)
|
547
|
-
end
|
412
|
+
private
|
413
|
+
attr_reader :serializer
|
414
|
+
attr_reader :forwarder
|
415
|
+
|
416
|
+
EMPTY_OPTIONS = {}.freeze
|
417
|
+
|
418
|
+
def now
|
419
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
548
420
|
end
|
549
421
|
|
550
|
-
def
|
551
|
-
if
|
552
|
-
|
553
|
-
|
554
|
-
|
422
|
+
def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
|
423
|
+
telemetry.sent(metrics: 1) if telemetry
|
424
|
+
|
425
|
+
sample_rate = opts[:sample_rate] || @sample_rate || 1
|
426
|
+
|
427
|
+
if sample_rate == 1 || opts[:pre_sampled] || rand <= sample_rate
|
428
|
+
full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
|
429
|
+
|
430
|
+
forwarder.send_message(full_stat)
|
555
431
|
end
|
556
432
|
end
|
557
433
|
end
|