dogstatsd-ruby 3.2.0 → 4.0.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 +5 -0
- data/lib/datadog/statsd.rb +264 -199
- metadata +5 -61
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6d2b1b9e7ec2a48c5305f6acda103d0b7a09787801dd899a38fb665f5389e4f3
|
|
4
|
+
data.tar.gz: 3d974fffb4ace3bc248e69dcdaabb4c556e6783a25d65af98cb65bdccafc52ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 16c82cb62bfd324d5e1dc1c6293fa121b92bd0c88d00474210c5da67f2cb07ae350cc0b717b15739c7a6f09e9021323ba348cec70ec2d0ac66a362f15f81007b
|
|
7
|
+
data.tar.gz: 6e82cef56d746ecf7b10890d397e313c143611b18b4ae65db67eb2f252cb27acacb45de03f6447b7b4ebb1666209806d6b048fb2857c5c8790f1f6729f4ae806
|
data/README.md
CHANGED
|
@@ -49,6 +49,11 @@ end
|
|
|
49
49
|
|
|
50
50
|
# Tag a metric.
|
|
51
51
|
statsd.histogram('query.time', 10, :tags => ["version:1"])
|
|
52
|
+
|
|
53
|
+
# Auto-close socket after end of block
|
|
54
|
+
Datadog::Statsd.open('localhost', 8125) do |s|
|
|
55
|
+
s.increment('page.views')
|
|
56
|
+
end
|
|
52
57
|
```
|
|
53
58
|
|
|
54
59
|
You can also post events to your stream. You can tag them, set priority and even aggregate them with other events.
|
data/lib/datadog/statsd.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'socket'
|
|
2
3
|
|
|
3
4
|
# = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
|
|
@@ -15,12 +16,137 @@ require 'socket'
|
|
|
15
16
|
# statsd = Datadog::Statsd.new 'localhost', 8125, :namespace => 'account'
|
|
16
17
|
# statsd.increment 'activate'
|
|
17
18
|
# @example Create a statsd client with global tags
|
|
18
|
-
# statsd = Datadog::Statsd.new 'localhost', 8125, :
|
|
19
|
+
# statsd = Datadog::Statsd.new 'localhost', 8125, tags: 'tag1:true'
|
|
19
20
|
module Datadog
|
|
20
21
|
class Statsd
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class Batch
|
|
102
|
+
def initialize(connection, max_buffer_bytes)
|
|
103
|
+
@connection = connection
|
|
104
|
+
@max_buffer_bytes = max_buffer_bytes
|
|
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
|
|
123
|
+
|
|
124
|
+
unless @buffer_bytes == 0
|
|
125
|
+
if @buffer_bytes + 1 + message_bytes >= @max_buffer_bytes
|
|
126
|
+
flush
|
|
127
|
+
else
|
|
128
|
+
@buffer << NEW_LINE
|
|
129
|
+
@buffer_bytes += 1
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
@buffer << message
|
|
134
|
+
@buffer_bytes += message_bytes
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def flush
|
|
138
|
+
return if @buffer_bytes == 0
|
|
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
|
|
24
150
|
|
|
25
151
|
# Create a dictionary to assign a key to every parameter's name, except for tags (treated differently)
|
|
26
152
|
# Goal: Simple and fast to add some other parameters
|
|
@@ -46,75 +172,66 @@ module Datadog
|
|
|
46
172
|
CRITICAL = 2
|
|
47
173
|
UNKNOWN = 3
|
|
48
174
|
|
|
175
|
+
MAX_EVENT_SIZE = 8 * 1024
|
|
176
|
+
|
|
49
177
|
COUNTER_TYPE = 'c'.freeze
|
|
50
178
|
GAUGE_TYPE = 'g'.freeze
|
|
51
179
|
HISTOGRAM_TYPE = 'h'.freeze
|
|
180
|
+
DISTRIBUTION_TYPE = 'd'.freeze
|
|
52
181
|
TIMING_TYPE = 'ms'.freeze
|
|
53
182
|
SET_TYPE = 's'.freeze
|
|
183
|
+
VERSION = "4.0.0".freeze
|
|
54
184
|
|
|
55
185
|
# A namespace to prepend to all statsd calls. Defaults to no namespace.
|
|
56
186
|
attr_reader :namespace
|
|
57
187
|
|
|
58
|
-
# StatsD host. Defaults to 127.0.0.1.
|
|
59
|
-
attr_reader :host
|
|
60
|
-
|
|
61
|
-
# StatsD port. Defaults to 8125.
|
|
62
|
-
attr_reader :port
|
|
63
|
-
|
|
64
|
-
# DogStatsd unix socket path. Not used by default.
|
|
65
|
-
attr_reader :socket_path
|
|
66
|
-
|
|
67
188
|
# Global tags to be added to every statsd call. Defaults to no tags.
|
|
68
189
|
attr_reader :tags
|
|
69
190
|
|
|
70
191
|
# Buffer containing the statsd message before they are sent in batch
|
|
71
192
|
attr_reader :buffer
|
|
72
193
|
|
|
73
|
-
# Maximum
|
|
74
|
-
|
|
194
|
+
# Maximum buffer size in bytes before it is flushed
|
|
195
|
+
attr_reader :max_buffer_bytes
|
|
75
196
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
attr_accessor :logger
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Return the current version of the library.
|
|
82
|
-
def self.VERSION
|
|
83
|
-
"3.2.0"
|
|
84
|
-
end
|
|
197
|
+
# Connection
|
|
198
|
+
attr_reader :connection
|
|
85
199
|
|
|
86
200
|
# @param [String] host your statsd host
|
|
87
201
|
# @param [Integer] port your statsd port
|
|
88
|
-
# @option
|
|
89
|
-
# @option
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
202
|
+
# @option [String] namespace set a namespace to be prepended to every metric name
|
|
203
|
+
# @option [Array<String>] tags tags to be added to every metric
|
|
204
|
+
# @option [Loger] logger for debugging
|
|
205
|
+
# @option [Integer] max_buffer_bytes max bytes to buffer when using #batch
|
|
206
|
+
# @option [String] socket_path unix socket path
|
|
207
|
+
def initialize(
|
|
208
|
+
host = nil,
|
|
209
|
+
port = nil,
|
|
210
|
+
namespace: nil,
|
|
211
|
+
tags: nil,
|
|
212
|
+
max_buffer_bytes: 8192,
|
|
213
|
+
socket_path: nil,
|
|
214
|
+
logger: nil
|
|
215
|
+
)
|
|
216
|
+
@connection = Connection.new(host, port, socket_path, logger)
|
|
217
|
+
@logger = logger
|
|
101
218
|
|
|
102
|
-
def namespace=(namespace) #:nodoc:
|
|
103
219
|
@namespace = namespace
|
|
104
|
-
@prefix = namespace
|
|
105
|
-
end
|
|
220
|
+
@prefix = @namespace ? "#{@namespace}.".freeze : nil
|
|
106
221
|
|
|
107
|
-
|
|
108
|
-
@
|
|
109
|
-
end
|
|
222
|
+
raise ArgumentError, 'tags must be a Array<String>' unless tags.nil? or tags.is_a? Array
|
|
223
|
+
@tags = (tags || []).compact.map! {|tag| escape_tag_content(tag)}
|
|
110
224
|
|
|
111
|
-
|
|
112
|
-
@port = port || DEFAULT_PORT
|
|
225
|
+
@batch = Batch.new @connection, max_buffer_bytes
|
|
113
226
|
end
|
|
114
227
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
228
|
+
# yield a new instance to a block and close it when done
|
|
229
|
+
# for short-term use-cases that don't want to close the socket manually
|
|
230
|
+
def self.open(*args)
|
|
231
|
+
instance = new(*args)
|
|
232
|
+
yield instance
|
|
233
|
+
ensure
|
|
234
|
+
instance.close
|
|
118
235
|
end
|
|
119
236
|
|
|
120
237
|
# Sends an increment (count = 1) for the given stat to the statsd server.
|
|
@@ -125,7 +242,7 @@ module Datadog
|
|
|
125
242
|
# @option opts [Array<String>] :tags An array of tags
|
|
126
243
|
# @option opts [Numeric] :by increment value, default 1
|
|
127
244
|
# @see #count
|
|
128
|
-
def increment(stat, opts=
|
|
245
|
+
def increment(stat, opts=EMPTY_OPTIONS)
|
|
129
246
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
|
130
247
|
incr_value = opts.fetch(:by, 1)
|
|
131
248
|
count stat, incr_value, opts
|
|
@@ -139,7 +256,7 @@ module Datadog
|
|
|
139
256
|
# @option opts [Array<String>] :tags An array of tags
|
|
140
257
|
# @option opts [Numeric] :by decrement value, default 1
|
|
141
258
|
# @see #count
|
|
142
|
-
def decrement(stat, opts=
|
|
259
|
+
def decrement(stat, opts=EMPTY_OPTIONS)
|
|
143
260
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
|
144
261
|
decr_value = - opts.fetch(:by, 1)
|
|
145
262
|
count stat, decr_value, opts
|
|
@@ -152,7 +269,7 @@ module Datadog
|
|
|
152
269
|
# @param [Hash] opts the options to create the metric with
|
|
153
270
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
|
154
271
|
# @option opts [Array<String>] :tags An array of tags
|
|
155
|
-
def count(stat, count, opts=
|
|
272
|
+
def count(stat, count, opts=EMPTY_OPTIONS)
|
|
156
273
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
|
157
274
|
send_stats stat, count, COUNTER_TYPE, opts
|
|
158
275
|
end
|
|
@@ -170,7 +287,7 @@ module Datadog
|
|
|
170
287
|
# @option opts [Array<String>] :tags An array of tags
|
|
171
288
|
# @example Report the current user count:
|
|
172
289
|
# $statsd.gauge('user.count', User.count)
|
|
173
|
-
def gauge(stat, value, opts=
|
|
290
|
+
def gauge(stat, value, opts=EMPTY_OPTIONS)
|
|
174
291
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
|
175
292
|
send_stats stat, value, GAUGE_TYPE, opts
|
|
176
293
|
end
|
|
@@ -184,10 +301,26 @@ module Datadog
|
|
|
184
301
|
# @option opts [Array<String>] :tags An array of tags
|
|
185
302
|
# @example Report the current user count:
|
|
186
303
|
# $statsd.histogram('user.count', User.count)
|
|
187
|
-
def histogram(stat, value, opts=
|
|
304
|
+
def histogram(stat, value, opts=EMPTY_OPTIONS)
|
|
188
305
|
send_stats stat, value, HISTOGRAM_TYPE, opts
|
|
189
306
|
end
|
|
190
307
|
|
|
308
|
+
# 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
|
+
#
|
|
313
|
+
# @param [String] stat stat name.
|
|
314
|
+
# @param [Numeric] value distribution value.
|
|
315
|
+
# @param [Hash] opts the options to create the metric with
|
|
316
|
+
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
|
317
|
+
# @option opts [Array<String>] :tags An array of tags
|
|
318
|
+
# @example Report the current user count:
|
|
319
|
+
# $statsd.distribution('user.count', User.count)
|
|
320
|
+
def distribution(stat, value, opts=EMPTY_OPTIONS)
|
|
321
|
+
send_stats stat, value, DISTRIBUTION_TYPE, opts
|
|
322
|
+
end
|
|
323
|
+
|
|
191
324
|
# Sends a timing (in ms) for the given stat to the statsd server. The
|
|
192
325
|
# sample_rate determines what percentage of the time this report is sent. The
|
|
193
326
|
# statsd server then uses the sample_rate to correctly track the average
|
|
@@ -198,7 +331,7 @@ module Datadog
|
|
|
198
331
|
# @param [Hash] opts the options to create the metric with
|
|
199
332
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
|
200
333
|
# @option opts [Array<String>] :tags An array of tags
|
|
201
|
-
def timing(stat, ms, opts=
|
|
334
|
+
def timing(stat, ms, opts=EMPTY_OPTIONS)
|
|
202
335
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
|
203
336
|
send_stats stat, ms, TIMING_TYPE, opts
|
|
204
337
|
end
|
|
@@ -216,13 +349,15 @@ module Datadog
|
|
|
216
349
|
# @see #timing
|
|
217
350
|
# @example Report the time (in ms) taken to activate an account
|
|
218
351
|
# $statsd.time('account.activate') { @account.activate! }
|
|
219
|
-
def time(stat, opts=
|
|
352
|
+
def time(stat, opts=EMPTY_OPTIONS)
|
|
220
353
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
|
221
|
-
start = Time.now
|
|
354
|
+
start = (PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f)
|
|
222
355
|
return yield
|
|
223
356
|
ensure
|
|
224
|
-
|
|
357
|
+
finished = (PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f)
|
|
358
|
+
timing(stat, ((finished - start) * 1000).round, opts)
|
|
225
359
|
end
|
|
360
|
+
|
|
226
361
|
# Sends a value to be tracked as a set to the statsd server.
|
|
227
362
|
#
|
|
228
363
|
# @param [String] stat stat name.
|
|
@@ -232,12 +367,11 @@ module Datadog
|
|
|
232
367
|
# @option opts [Array<String>] :tags An array of tags
|
|
233
368
|
# @example Record a unique visitory by id:
|
|
234
369
|
# $statsd.set('visitors.uniques', User.id)
|
|
235
|
-
def set(stat, value, opts=
|
|
370
|
+
def set(stat, value, opts=EMPTY_OPTIONS)
|
|
236
371
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
|
237
372
|
send_stats stat, value, SET_TYPE, opts
|
|
238
373
|
end
|
|
239
374
|
|
|
240
|
-
|
|
241
375
|
# This method allows you to send custom service check statuses.
|
|
242
376
|
#
|
|
243
377
|
# @param [String] name Service check name
|
|
@@ -249,31 +383,8 @@ module Datadog
|
|
|
249
383
|
# @option opts [String, nil] :message (nil) A message to associate with this service check status
|
|
250
384
|
# @example Report a critical service check status
|
|
251
385
|
# $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
|
|
252
|
-
def service_check(name, status, opts=
|
|
253
|
-
|
|
254
|
-
send_to_socket service_check_string
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
def format_service_check(name, status, opts={})
|
|
258
|
-
sc_string = "_sc|#{name}|#{status}"
|
|
259
|
-
|
|
260
|
-
SC_OPT_KEYS.each do |key, shorthand_key|
|
|
261
|
-
next unless opts[key]
|
|
262
|
-
|
|
263
|
-
if key == :tags
|
|
264
|
-
tags = opts[:tags].map {|tag| escape_tag_content(tag) }
|
|
265
|
-
tags = "#{tags.join(COMMA)}" unless tags.empty?
|
|
266
|
-
sc_string << "|##{tags}"
|
|
267
|
-
elsif key == :message
|
|
268
|
-
message = remove_pipes(opts[:message])
|
|
269
|
-
escaped_message = escape_service_check_message(message)
|
|
270
|
-
sc_string << "|m:#{escaped_message}"
|
|
271
|
-
else
|
|
272
|
-
value = remove_pipes(opts[key])
|
|
273
|
-
sc_string << "|#{shorthand_key}#{value}"
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
return sc_string
|
|
386
|
+
def service_check(name, status, opts=EMPTY_OPTIONS)
|
|
387
|
+
send_stat format_service_check(name, status, opts)
|
|
277
388
|
end
|
|
278
389
|
|
|
279
390
|
# This end point allows you to post events to the stream. You can tag them, set priority and even aggregate them with other events.
|
|
@@ -294,11 +405,8 @@ module Datadog
|
|
|
294
405
|
# @option opts [Array<String>] :tags tags to be added to every metric
|
|
295
406
|
# @example Report an awful event:
|
|
296
407
|
# $statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent'])
|
|
297
|
-
def event(title, text, opts=
|
|
298
|
-
|
|
299
|
-
raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string.length > 8 * 1024
|
|
300
|
-
|
|
301
|
-
send_to_socket event_string
|
|
408
|
+
def event(title, text, opts=EMPTY_OPTIONS)
|
|
409
|
+
send_stat format_event(title, text, opts)
|
|
302
410
|
end
|
|
303
411
|
|
|
304
412
|
# Send several metrics in the same UDP Packet
|
|
@@ -309,18 +417,56 @@ module Datadog
|
|
|
309
417
|
# s.gauge('users.online',156)
|
|
310
418
|
# s.increment('page.views')
|
|
311
419
|
# end
|
|
312
|
-
def batch
|
|
313
|
-
@
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
420
|
+
def batch
|
|
421
|
+
@batch.open { yield self }
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
# Close the underlying socket
|
|
425
|
+
def close
|
|
426
|
+
@connection.close
|
|
318
427
|
end
|
|
319
428
|
|
|
320
|
-
|
|
429
|
+
private
|
|
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
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
def format_event(title, text, opts=EMPTY_OPTIONS)
|
|
321
467
|
escaped_title = escape_event_content(title)
|
|
322
468
|
escaped_text = escape_event_content(text)
|
|
323
|
-
event_string_data = "_e{#{escaped_title.length},#{escaped_text.length}}:#{escaped_title}|#{escaped_text}"
|
|
469
|
+
event_string_data = "_e{#{escaped_title.length},#{escaped_text.length}}:#{escaped_title}|#{escaped_text}".dup
|
|
324
470
|
|
|
325
471
|
# We construct the string to be sent by adding '|key:value' parts to it when needed
|
|
326
472
|
# All pipes ('|') in the metadata are removed. Title and Text can keep theirs
|
|
@@ -332,72 +478,46 @@ module Datadog
|
|
|
332
478
|
end
|
|
333
479
|
|
|
334
480
|
# Tags are joined and added as last part to the string to be sent
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
event_string_data << "|##{full_tags.join(COMMA)}"
|
|
481
|
+
if tags_string = tags_as_string(opts)
|
|
482
|
+
event_string_data << "|##{tags_string}"
|
|
338
483
|
end
|
|
339
484
|
|
|
340
|
-
raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.length >
|
|
341
|
-
|
|
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
|
|
342
487
|
end
|
|
343
488
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
489
|
+
def tags_as_string(opts)
|
|
490
|
+
if tag_arr = opts[:tags]
|
|
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?
|
|
347
497
|
end
|
|
348
498
|
|
|
349
|
-
private
|
|
350
|
-
|
|
351
|
-
NEW_LINE = "\n".freeze
|
|
352
|
-
ESC_NEW_LINE = "\\n".freeze
|
|
353
|
-
COMMA = ",".freeze
|
|
354
|
-
BLANK = "".freeze
|
|
355
|
-
PIPE = "|".freeze
|
|
356
|
-
DOT = ".".freeze
|
|
357
|
-
DOUBLE_COLON = "::".freeze
|
|
358
|
-
UNDERSCORE = "_".freeze
|
|
359
|
-
|
|
360
|
-
private_constant :NEW_LINE, :ESC_NEW_LINE, :COMMA, :BLANK, :PIPE, :DOT,
|
|
361
|
-
:DOUBLE_COLON, :UNDERSCORE
|
|
362
|
-
|
|
363
499
|
def escape_event_content(msg)
|
|
364
500
|
msg.gsub NEW_LINE, ESC_NEW_LINE
|
|
365
501
|
end
|
|
366
502
|
|
|
367
503
|
def escape_tag_content(tag)
|
|
368
|
-
remove_pipes(tag.to_s)
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
def escape_tag_content!(tag)
|
|
372
|
-
tag = tag.to_s
|
|
373
|
-
tag.gsub!(PIPE, BLANK)
|
|
374
|
-
tag.gsub!(COMMA, BLANK)
|
|
504
|
+
tag = remove_pipes(tag.to_s)
|
|
505
|
+
tag.delete! COMMA
|
|
375
506
|
tag
|
|
376
507
|
end
|
|
377
508
|
|
|
378
509
|
def remove_pipes(msg)
|
|
379
|
-
msg.
|
|
510
|
+
msg.delete PIPE
|
|
380
511
|
end
|
|
381
512
|
|
|
382
513
|
def escape_service_check_message(msg)
|
|
383
514
|
escape_event_content(msg).gsub('m:'.freeze, 'm\:'.freeze)
|
|
384
515
|
end
|
|
385
516
|
|
|
386
|
-
def
|
|
387
|
-
timing(stat, ((Time.now - start) * 1000).round, opts)
|
|
388
|
-
end
|
|
389
|
-
|
|
390
|
-
def join_array_to_str(str, array, joiner)
|
|
391
|
-
array.each_with_index do |item, i|
|
|
392
|
-
str << joiner unless i == 0
|
|
393
|
-
str << item
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
def send_stats(stat, delta, type, opts={})
|
|
517
|
+
def send_stats(stat, delta, type, opts=EMPTY_OPTIONS)
|
|
398
518
|
sample_rate = opts[:sample_rate] || 1
|
|
399
519
|
if sample_rate == 1 or rand < sample_rate
|
|
400
|
-
full_stat = ''
|
|
520
|
+
full_stat = ''.dup
|
|
401
521
|
full_stat << @prefix if @prefix
|
|
402
522
|
|
|
403
523
|
stat = stat.is_a?(String) ? stat.dup : stat.to_s
|
|
@@ -417,14 +537,10 @@ module Datadog
|
|
|
417
537
|
full_stat << sample_rate.to_s
|
|
418
538
|
end
|
|
419
539
|
|
|
420
|
-
|
|
421
|
-
tag_arr = opts[:tags].to_a
|
|
422
|
-
tag_arr.map! { |tag| t = tag.to_s.dup; escape_tag_content!(t); t }
|
|
423
|
-
ts = tags.to_a + tag_arr
|
|
424
|
-
unless ts.empty?
|
|
540
|
+
if tags_string = tags_as_string(opts)
|
|
425
541
|
full_stat << PIPE
|
|
426
542
|
full_stat << '#'.freeze
|
|
427
|
-
|
|
543
|
+
full_stat << tags_string
|
|
428
544
|
end
|
|
429
545
|
|
|
430
546
|
send_stat(full_stat)
|
|
@@ -432,62 +548,11 @@ module Datadog
|
|
|
432
548
|
end
|
|
433
549
|
|
|
434
550
|
def send_stat(message)
|
|
435
|
-
if @
|
|
436
|
-
@
|
|
437
|
-
flush_buffer if @buffer.length >= @max_buffer_size
|
|
438
|
-
else
|
|
439
|
-
send_to_socket(message)
|
|
440
|
-
end
|
|
441
|
-
end
|
|
442
|
-
|
|
443
|
-
def flush_buffer
|
|
444
|
-
return @buffer if @buffer.empty?
|
|
445
|
-
send_to_socket(@buffer.join(NEW_LINE))
|
|
446
|
-
@buffer = Array.new
|
|
447
|
-
end
|
|
448
|
-
|
|
449
|
-
def connect_to_socket
|
|
450
|
-
if !@socket_path.nil?
|
|
451
|
-
socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
|
|
452
|
-
socket.connect(Socket.pack_sockaddr_un(@socket_path))
|
|
551
|
+
if @batch.open?
|
|
552
|
+
@batch.add message
|
|
453
553
|
else
|
|
454
|
-
|
|
455
|
-
socket.connect(@host, @port)
|
|
554
|
+
@connection.write(message)
|
|
456
555
|
end
|
|
457
|
-
socket
|
|
458
|
-
end
|
|
459
|
-
|
|
460
|
-
def sock
|
|
461
|
-
@socket ||= connect_to_socket
|
|
462
|
-
end
|
|
463
|
-
|
|
464
|
-
def send_to_socket(message)
|
|
465
|
-
self.class.logger.debug { "Statsd: #{message}" } if self.class.logger
|
|
466
|
-
if @socket_path.nil?
|
|
467
|
-
sock.send(message, 0)
|
|
468
|
-
else
|
|
469
|
-
sock.sendmsg_nonblock(message)
|
|
470
|
-
end
|
|
471
|
-
rescue => boom
|
|
472
|
-
if @socket_path && (boom.is_a?(Errno::ECONNREFUSED) ||
|
|
473
|
-
boom.is_a?(Errno::ECONNRESET) ||
|
|
474
|
-
boom.is_a?(Errno::ENOENT))
|
|
475
|
-
return @socket = nil
|
|
476
|
-
end
|
|
477
|
-
# Try once to reconnect if the socket has been closed
|
|
478
|
-
retries ||= 1
|
|
479
|
-
if retries <= 1 && boom.is_a?(IOError) && boom.message =~ /closed stream/i
|
|
480
|
-
retries += 1
|
|
481
|
-
begin
|
|
482
|
-
@socket = connect_to_socket
|
|
483
|
-
retry
|
|
484
|
-
rescue => e
|
|
485
|
-
boom = e
|
|
486
|
-
end
|
|
487
|
-
end
|
|
488
|
-
|
|
489
|
-
self.class.logger.error { "Statsd: #{boom.class} #{boom}" } if self.class.logger
|
|
490
|
-
nil
|
|
491
556
|
end
|
|
492
557
|
end
|
|
493
558
|
end
|
metadata
CHANGED
|
@@ -1,71 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dogstatsd-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 4.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rein Henrichs
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
-
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: minitest
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - ">="
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0'
|
|
20
|
-
type: :development
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - ">="
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0'
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: yard
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - "~>"
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: 0.6.0
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - "~>"
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: 0.6.0
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: jeweler
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - "~>"
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '1.8'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - "~>"
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '1.8'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: simplecov
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
type: :development
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
11
|
+
date: 2018-08-20 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
69
13
|
description: A Ruby DogStastd client
|
|
70
14
|
email: code@datadoghq.com
|
|
71
15
|
executables: []
|
|
@@ -89,7 +33,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
89
33
|
requirements:
|
|
90
34
|
- - ">="
|
|
91
35
|
- !ruby/object:Gem::Version
|
|
92
|
-
version:
|
|
36
|
+
version: 2.0.0
|
|
93
37
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
38
|
requirements:
|
|
95
39
|
- - ">="
|
|
@@ -97,7 +41,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
97
41
|
version: '0'
|
|
98
42
|
requirements: []
|
|
99
43
|
rubyforge_project:
|
|
100
|
-
rubygems_version: 2.7.
|
|
44
|
+
rubygems_version: 2.7.7
|
|
101
45
|
signing_key:
|
|
102
46
|
specification_version: 4
|
|
103
47
|
summary: A Ruby DogStatsd client
|