dogstatsd-ruby 4.0.0 → 5.3.3
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 +182 -57
- data/lib/datadog/statsd/connection.rb +60 -0
- data/lib/datadog/statsd/connection_cfg.rb +76 -0
- data/lib/datadog/statsd/forwarder.rb +131 -0
- data/lib/datadog/statsd/message_buffer.rb +97 -0
- data/lib/datadog/statsd/sender.rb +177 -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 +66 -0
- data/lib/datadog/statsd/telemetry.rb +117 -0
- data/lib/datadog/statsd/timer.rb +60 -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 +195 -326
- metadata +33 -8
data/lib/datadog/statsd.rb
CHANGED
@@ -1,6 +1,21 @@
|
|
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
|
+
|
16
|
+
$deprecation_message_mutex = Mutex.new
|
17
|
+
$deprecation_message_done = false
|
18
|
+
|
4
19
|
# = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
|
5
20
|
#
|
6
21
|
# @example Set up a global Statsd client for a server on localhost:8125
|
@@ -19,216 +34,129 @@ require 'socket'
|
|
19
34
|
# statsd = Datadog::Statsd.new 'localhost', 8125, tags: 'tag1:true'
|
20
35
|
module Datadog
|
21
36
|
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
|
37
|
+
class Error < StandardError
|
99
38
|
end
|
100
39
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
@depth = 0
|
106
|
-
reset
|
107
|
-
end
|
40
|
+
OK = 0
|
41
|
+
WARNING = 1
|
42
|
+
CRITICAL = 2
|
43
|
+
UNKNOWN = 3
|
108
44
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
ensure
|
113
|
-
@depth -= 1
|
114
|
-
flush if !open?
|
115
|
-
end
|
45
|
+
UDP_DEFAULT_BUFFER_SIZE = 1_432
|
46
|
+
UDS_DEFAULT_BUFFER_SIZE = 8_192
|
47
|
+
DEFAULT_BUFFER_POOL_SIZE = Float::INFINITY
|
116
48
|
|
117
|
-
|
118
|
-
|
119
|
-
end
|
49
|
+
UDP_DEFAULT_SENDER_QUEUE_SIZE = 2048
|
50
|
+
UDS_DEFAULT_SENDER_QUEUE_SIZE = 512
|
120
51
|
|
121
|
-
|
122
|
-
message_bytes = message.bytesize
|
52
|
+
MAX_EVENT_SIZE = 8 * 1_024
|
123
53
|
|
124
|
-
|
125
|
-
|
126
|
-
flush
|
127
|
-
else
|
128
|
-
@buffer << NEW_LINE
|
129
|
-
@buffer_bytes += 1
|
130
|
-
end
|
131
|
-
end
|
54
|
+
# minimum flush interval for the telemetry in seconds
|
55
|
+
DEFAULT_TELEMETRY_FLUSH_INTERVAL = 10
|
132
56
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
@connection.write @buffer
|
140
|
-
reset
|
141
|
-
end
|
142
|
-
|
143
|
-
private
|
144
|
-
|
145
|
-
def reset
|
146
|
-
@buffer = String.new
|
147
|
-
@buffer_bytes = 0
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
# Create a dictionary to assign a key to every parameter's name, except for tags (treated differently)
|
152
|
-
# Goal: Simple and fast to add some other parameters
|
153
|
-
OPTS_KEYS = {
|
154
|
-
:date_happened => :d,
|
155
|
-
:hostname => :h,
|
156
|
-
:aggregation_key => :k,
|
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
|
57
|
+
COUNTER_TYPE = 'c'
|
58
|
+
GAUGE_TYPE = 'g'
|
59
|
+
HISTOGRAM_TYPE = 'h'
|
60
|
+
DISTRIBUTION_TYPE = 'd'
|
61
|
+
TIMING_TYPE = 'ms'
|
62
|
+
SET_TYPE = 's'
|
184
63
|
|
185
64
|
# A namespace to prepend to all statsd calls. Defaults to no namespace.
|
186
65
|
attr_reader :namespace
|
187
66
|
|
188
67
|
# 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
|
68
|
+
def tags
|
69
|
+
serializer.global_tags
|
70
|
+
end
|
196
71
|
|
197
|
-
#
|
198
|
-
attr_reader :
|
72
|
+
# Default sample rate
|
73
|
+
attr_reader :sample_rate
|
199
74
|
|
200
75
|
# @param [String] host your statsd host
|
201
76
|
# @param [Integer] port your statsd port
|
202
77
|
# @option [String] namespace set a namespace to be prepended to every metric name
|
203
|
-
# @option [Array<String
|
204
|
-
# @option [
|
205
|
-
# @option [Integer]
|
78
|
+
# @option [Array<String>|Hash] tags tags to be added to every metric
|
79
|
+
# @option [Logger] logger for debugging
|
80
|
+
# @option [Integer] buffer_max_payload_size max bytes to buffer
|
81
|
+
# @option [Integer] buffer_max_pool_size max messages to buffer
|
82
|
+
# @option [Integer] sender_queue_size size of the sender queue in number of buffers (multi-thread only)
|
83
|
+
# @option [Numeric] buffer_flush_interval interval in second to flush buffer
|
206
84
|
# @option [String] socket_path unix socket path
|
85
|
+
# @option [Float] default sample rate if not overridden
|
86
|
+
# @option [Boolean] single_thread flushes the metrics on the main thread instead of in a companion thread
|
207
87
|
def initialize(
|
208
88
|
host = nil,
|
209
89
|
port = nil,
|
90
|
+
socket_path: nil,
|
91
|
+
|
210
92
|
namespace: nil,
|
211
93
|
tags: nil,
|
212
|
-
|
213
|
-
|
214
|
-
|
94
|
+
sample_rate: nil,
|
95
|
+
|
96
|
+
buffer_max_payload_size: nil,
|
97
|
+
buffer_max_pool_size: nil,
|
98
|
+
buffer_overflowing_stategy: :drop,
|
99
|
+
buffer_flush_interval: nil,
|
100
|
+
|
101
|
+
sender_queue_size: nil,
|
102
|
+
|
103
|
+
logger: nil,
|
104
|
+
|
105
|
+
single_thread: false,
|
106
|
+
|
107
|
+
telemetry_enable: true,
|
108
|
+
telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL
|
215
109
|
)
|
216
|
-
|
217
|
-
|
110
|
+
unless tags.nil? || tags.is_a?(Array) || tags.is_a?(Hash)
|
111
|
+
raise ArgumentError, 'tags must be an array of string tags or a Hash'
|
112
|
+
end
|
218
113
|
|
219
114
|
@namespace = namespace
|
220
115
|
@prefix = @namespace ? "#{@namespace}.".freeze : nil
|
116
|
+
@serializer = Serialization::Serializer.new(prefix: @prefix, global_tags: tags)
|
117
|
+
@sample_rate = sample_rate
|
118
|
+
|
119
|
+
# deprecation message for ruby < 2.1.0 users as we will drop support for ruby 2.0
|
120
|
+
# in dogstatsd-ruby 5.4.0
|
121
|
+
# TODO(remy): remove this message and the two global vars used in dogstatd-ruby 5.4.0
|
122
|
+
if RUBY_VERSION < '2.1.0' && $deprecation_message_mutex.try_lock && !$deprecation_message_done
|
123
|
+
if logger != nil
|
124
|
+
logger.warn { "deprecation: dogstatsd-ruby will drop support of Ruby < 2.1.0 in a next minor release" }
|
125
|
+
else
|
126
|
+
puts("warning: deprecation: dogstatsd-ruby will drop support of Ruby < 2.1.0 in a next minor release")
|
127
|
+
end
|
128
|
+
$deprecation_message_done = true
|
129
|
+
$deprecation_message_mutex.unlock
|
130
|
+
end
|
131
|
+
|
132
|
+
@forwarder = Forwarder.new(
|
133
|
+
connection_cfg: ConnectionCfg.new(
|
134
|
+
host: host,
|
135
|
+
port: port,
|
136
|
+
socket_path: socket_path,
|
137
|
+
),
|
138
|
+
|
139
|
+
global_tags: tags,
|
140
|
+
logger: logger,
|
141
|
+
|
142
|
+
single_thread: single_thread,
|
221
143
|
|
222
|
-
|
223
|
-
|
144
|
+
buffer_max_payload_size: buffer_max_payload_size,
|
145
|
+
buffer_max_pool_size: buffer_max_pool_size,
|
146
|
+
buffer_overflowing_stategy: buffer_overflowing_stategy,
|
147
|
+
buffer_flush_interval: buffer_flush_interval,
|
224
148
|
|
225
|
-
|
149
|
+
sender_queue_size: sender_queue_size,
|
150
|
+
|
151
|
+
telemetry_flush_interval: telemetry_enable ? telemetry_flush_interval : nil,
|
152
|
+
)
|
226
153
|
end
|
227
154
|
|
228
155
|
# yield a new instance to a block and close it when done
|
229
156
|
# for short-term use-cases that don't want to close the socket manually
|
230
157
|
def self.open(*args)
|
231
158
|
instance = new(*args)
|
159
|
+
|
232
160
|
yield instance
|
233
161
|
ensure
|
234
162
|
instance.close
|
@@ -242,10 +170,10 @@ module Datadog
|
|
242
170
|
# @option opts [Array<String>] :tags An array of tags
|
243
171
|
# @option opts [Numeric] :by increment value, default 1
|
244
172
|
# @see #count
|
245
|
-
def increment(stat, opts=EMPTY_OPTIONS)
|
246
|
-
opts = {:
|
173
|
+
def increment(stat, opts = EMPTY_OPTIONS)
|
174
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
247
175
|
incr_value = opts.fetch(:by, 1)
|
248
|
-
count
|
176
|
+
count(stat, incr_value, opts)
|
249
177
|
end
|
250
178
|
|
251
179
|
# Sends a decrement (count = -1) for the given stat to the statsd server.
|
@@ -256,10 +184,10 @@ module Datadog
|
|
256
184
|
# @option opts [Array<String>] :tags An array of tags
|
257
185
|
# @option opts [Numeric] :by decrement value, default 1
|
258
186
|
# @see #count
|
259
|
-
def decrement(stat, opts=EMPTY_OPTIONS)
|
260
|
-
opts = {:
|
187
|
+
def decrement(stat, opts = EMPTY_OPTIONS)
|
188
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
261
189
|
decr_value = - opts.fetch(:by, 1)
|
262
|
-
count
|
190
|
+
count(stat, decr_value, opts)
|
263
191
|
end
|
264
192
|
|
265
193
|
# Sends an arbitrary count for the given stat to the statsd server.
|
@@ -269,9 +197,9 @@ module Datadog
|
|
269
197
|
# @param [Hash] opts the options to create the metric with
|
270
198
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
271
199
|
# @option opts [Array<String>] :tags An array of tags
|
272
|
-
def count(stat, count, opts=EMPTY_OPTIONS)
|
273
|
-
opts = {:
|
274
|
-
send_stats
|
200
|
+
def count(stat, count, opts = EMPTY_OPTIONS)
|
201
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
202
|
+
send_stats(stat, count, COUNTER_TYPE, opts)
|
275
203
|
end
|
276
204
|
|
277
205
|
# Sends an arbitary gauge value for the given stat to the statsd server.
|
@@ -287,9 +215,9 @@ module Datadog
|
|
287
215
|
# @option opts [Array<String>] :tags An array of tags
|
288
216
|
# @example Report the current user count:
|
289
217
|
# $statsd.gauge('user.count', User.count)
|
290
|
-
def gauge(stat, value, opts=EMPTY_OPTIONS)
|
291
|
-
opts = {:
|
292
|
-
send_stats
|
218
|
+
def gauge(stat, value, opts = EMPTY_OPTIONS)
|
219
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
220
|
+
send_stats(stat, value, GAUGE_TYPE, opts)
|
293
221
|
end
|
294
222
|
|
295
223
|
# Sends a value to be tracked as a histogram to the statsd server.
|
@@ -301,14 +229,11 @@ module Datadog
|
|
301
229
|
# @option opts [Array<String>] :tags An array of tags
|
302
230
|
# @example Report the current user count:
|
303
231
|
# $statsd.histogram('user.count', User.count)
|
304
|
-
def histogram(stat, value, opts=EMPTY_OPTIONS)
|
305
|
-
send_stats
|
232
|
+
def histogram(stat, value, opts = EMPTY_OPTIONS)
|
233
|
+
send_stats(stat, value, HISTOGRAM_TYPE, opts)
|
306
234
|
end
|
307
235
|
|
308
236
|
# 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
237
|
#
|
313
238
|
# @param [String] stat stat name.
|
314
239
|
# @param [Numeric] value distribution value.
|
@@ -317,8 +242,8 @@ module Datadog
|
|
317
242
|
# @option opts [Array<String>] :tags An array of tags
|
318
243
|
# @example Report the current user count:
|
319
244
|
# $statsd.distribution('user.count', User.count)
|
320
|
-
def distribution(stat, value, opts=EMPTY_OPTIONS)
|
321
|
-
send_stats
|
245
|
+
def distribution(stat, value, opts = EMPTY_OPTIONS)
|
246
|
+
send_stats(stat, value, DISTRIBUTION_TYPE, opts)
|
322
247
|
end
|
323
248
|
|
324
249
|
# Sends a timing (in ms) for the given stat to the statsd server. The
|
@@ -331,9 +256,9 @@ module Datadog
|
|
331
256
|
# @param [Hash] opts the options to create the metric with
|
332
257
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
333
258
|
# @option opts [Array<String>] :tags An array of tags
|
334
|
-
def timing(stat, ms, opts=EMPTY_OPTIONS)
|
335
|
-
opts = {:
|
336
|
-
send_stats
|
259
|
+
def timing(stat, ms, opts = EMPTY_OPTIONS)
|
260
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
261
|
+
send_stats(stat, ms, TIMING_TYPE, opts)
|
337
262
|
end
|
338
263
|
|
339
264
|
# Reports execution time of the provided block using {#timing}.
|
@@ -349,13 +274,12 @@ module Datadog
|
|
349
274
|
# @see #timing
|
350
275
|
# @example Report the time (in ms) taken to activate an account
|
351
276
|
# $statsd.time('account.activate') { @account.activate! }
|
352
|
-
def time(stat, opts=EMPTY_OPTIONS)
|
353
|
-
opts = {:
|
354
|
-
start =
|
355
|
-
|
277
|
+
def time(stat, opts = EMPTY_OPTIONS)
|
278
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
279
|
+
start = now
|
280
|
+
yield
|
356
281
|
ensure
|
357
|
-
|
358
|
-
timing(stat, ((finished - start) * 1000).round, opts)
|
282
|
+
timing(stat, ((now - start) * 1000).round, opts)
|
359
283
|
end
|
360
284
|
|
361
285
|
# Sends a value to be tracked as a set to the statsd server.
|
@@ -367,9 +291,9 @@ module Datadog
|
|
367
291
|
# @option opts [Array<String>] :tags An array of tags
|
368
292
|
# @example Record a unique visitory by id:
|
369
293
|
# $statsd.set('visitors.uniques', User.id)
|
370
|
-
def set(stat, value, opts=EMPTY_OPTIONS)
|
371
|
-
opts = {:
|
372
|
-
send_stats
|
294
|
+
def set(stat, value, opts = EMPTY_OPTIONS)
|
295
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
296
|
+
send_stats(stat, value, SET_TYPE, opts)
|
373
297
|
end
|
374
298
|
|
375
299
|
# This method allows you to send custom service check statuses.
|
@@ -377,14 +301,16 @@ module Datadog
|
|
377
301
|
# @param [String] name Service check name
|
378
302
|
# @param [String] status Service check status.
|
379
303
|
# @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
|
304
|
+
# @option opts [Integer, String, nil] :timestamp (nil) Assign a timestamp to the service check. Default is now when none
|
305
|
+
# @option opts [String, nil] :hostname (nil) Assign a hostname to the service check.
|
382
306
|
# @option opts [Array<String>, nil] :tags (nil) An array of tags
|
383
307
|
# @option opts [String, nil] :message (nil) A message to associate with this service check status
|
384
308
|
# @example Report a critical service check status
|
385
309
|
# $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
|
386
|
-
def service_check(name, status, opts=EMPTY_OPTIONS)
|
387
|
-
|
310
|
+
def service_check(name, status, opts = EMPTY_OPTIONS)
|
311
|
+
telemetry.sent(service_checks: 1) if telemetry
|
312
|
+
|
313
|
+
forwarder.send_message(serializer.to_service_check(name, status, opts))
|
388
314
|
end
|
389
315
|
|
390
316
|
# 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 +320,33 @@ module Datadog
|
|
394
320
|
# it will be grouped with other events that don't have an event type.
|
395
321
|
#
|
396
322
|
# @param [String] title Event title
|
397
|
-
# @param [String] text Event text. Supports
|
323
|
+
# @param [String] text Event text. Supports newlines (+\n+)
|
398
324
|
# @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
|
325
|
+
# @option opts [Integer, String, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
|
400
326
|
# @option opts [String, nil] :hostname (nil) Assign a hostname to the event.
|
401
327
|
# @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others
|
402
328
|
# @option opts [String, nil] :priority ('normal') Can be "normal" or "low"
|
403
329
|
# @option opts [String, nil] :source_type_name (nil) Assign a source type to the event
|
404
330
|
# @option opts [String, nil] :alert_type ('info') Can be "error", "warning", "info" or "success".
|
331
|
+
# @option opts [Boolean, false] :truncate_if_too_long (false) Truncate the event if it is too long
|
405
332
|
# @option opts [Array<String>] :tags tags to be added to every metric
|
406
333
|
# @example Report an awful event:
|
407
334
|
# $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
|
-
|
335
|
+
def event(title, text, opts = EMPTY_OPTIONS)
|
336
|
+
telemetry.sent(events: 1) if telemetry
|
337
|
+
|
338
|
+
forwarder.send_message(serializer.to_event(title, text, opts))
|
410
339
|
end
|
411
340
|
|
412
|
-
# Send several metrics in the same
|
413
|
-
# They will be buffered and flushed when the block finishes
|
341
|
+
# Send several metrics in the same packet.
|
342
|
+
# They will be buffered and flushed when the block finishes.
|
343
|
+
#
|
344
|
+
# This method exists for compatibility with v4.x versions, it is not needed
|
345
|
+
# anymore since the batching is now automatically done internally.
|
346
|
+
# It also means that an automatic flush could occur if the buffer is filled
|
347
|
+
# during the execution of the batch block.
|
348
|
+
#
|
349
|
+
# This method is DEPRECATED and will be removed in future v6.x API.
|
414
350
|
#
|
415
351
|
# @example Send several metrics in one packet:
|
416
352
|
# $statsd.batch do |s|
|
@@ -418,140 +354,73 @@ module Datadog
|
|
418
354
|
# s.increment('page.views')
|
419
355
|
# end
|
420
356
|
def batch
|
421
|
-
|
357
|
+
yield self
|
358
|
+
flush(sync: true)
|
422
359
|
end
|
423
360
|
|
424
361
|
# Close the underlying socket
|
425
|
-
|
426
|
-
|
362
|
+
#
|
363
|
+
# @param [Boolean, true] flush Should we flush the metrics before closing
|
364
|
+
def close(flush: true)
|
365
|
+
flush(sync: true) if flush
|
366
|
+
forwarder.close
|
427
367
|
end
|
428
368
|
|
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
|
369
|
+
def sync_with_outbound_io
|
370
|
+
forwarder.sync_with_outbound_io
|
464
371
|
end
|
465
372
|
|
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
|
373
|
+
# Flush the buffer into the connection
|
374
|
+
def flush(flush_telemetry: false, sync: false)
|
375
|
+
forwarder.flush(flush_telemetry: flush_telemetry, sync: sync)
|
487
376
|
end
|
488
377
|
|
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?
|
378
|
+
def telemetry
|
379
|
+
forwarder.telemetry
|
497
380
|
end
|
498
381
|
|
499
|
-
def
|
500
|
-
|
382
|
+
def host
|
383
|
+
forwarder.host
|
501
384
|
end
|
502
385
|
|
503
|
-
def
|
504
|
-
|
505
|
-
tag.delete! COMMA
|
506
|
-
tag
|
386
|
+
def port
|
387
|
+
forwarder.port
|
507
388
|
end
|
508
389
|
|
509
|
-
def
|
510
|
-
|
390
|
+
def socket_path
|
391
|
+
forwarder.socket_path
|
511
392
|
end
|
512
393
|
|
513
|
-
def
|
514
|
-
|
394
|
+
def transport_type
|
395
|
+
forwarder.transport_type
|
515
396
|
end
|
516
397
|
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
full_stat = ''.dup
|
521
|
-
full_stat << @prefix if @prefix
|
522
|
-
|
523
|
-
stat = stat.is_a?(String) ? stat.dup : stat.to_s
|
524
|
-
# Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
|
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
|
398
|
+
private
|
399
|
+
attr_reader :serializer
|
400
|
+
attr_reader :forwarder
|
539
401
|
|
540
|
-
|
541
|
-
|
542
|
-
full_stat << '#'.freeze
|
543
|
-
full_stat << tags_string
|
544
|
-
end
|
402
|
+
PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= '2.1.0')
|
403
|
+
EMPTY_OPTIONS = {}.freeze
|
545
404
|
|
546
|
-
|
405
|
+
if PROCESS_TIME_SUPPORTED
|
406
|
+
def now
|
407
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
408
|
+
end
|
409
|
+
else
|
410
|
+
def now
|
411
|
+
Time.now.to_f
|
547
412
|
end
|
548
413
|
end
|
549
414
|
|
550
|
-
def
|
551
|
-
if
|
552
|
-
|
553
|
-
|
554
|
-
|
415
|
+
def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
|
416
|
+
telemetry.sent(metrics: 1) if telemetry
|
417
|
+
|
418
|
+
sample_rate = opts[:sample_rate] || @sample_rate || 1
|
419
|
+
|
420
|
+
if sample_rate == 1 || rand <= sample_rate
|
421
|
+
full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
|
422
|
+
|
423
|
+
forwarder.send_message(full_stat)
|
555
424
|
end
|
556
425
|
end
|
557
426
|
end
|