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