dogstatsd-ruby 3.3.0 → 4.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 +34 -5
- data/lib/datadog/statsd.rb +290 -182
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fde2be6b614574c2555c8cbbd2ca16ef33bcd09837502f482f5f6baf582fd67b
|
4
|
+
data.tar.gz: cc1523c5aa78643d93fc891116c46bb5322dda297f1676cd73d201c9beee2a26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c96177f1ea0ab2288712cf9829cb8408687e12ce0f22f06ddbb1433447a3a4eaf05fe13f0fda8746b9f0e8fad71ea3b0b50ba7d8a028e464db334349951c64b
|
7
|
+
data.tar.gz: 11e23d434a7e88925e049484dfbb252d5c78ce12a4a07fd3c09edca57835db30224cba202f86f57352357acdf2073b8c8e93f20bd403975242b201321bef8ecc
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ dogstatsd-ruby
|
|
4
4
|
|
5
5
|
A client for DogStatsD, an extension of the StatsD metric server for Datadog.
|
6
6
|
|
7
|
-
[](http://travis-ci.org/DataDog/dogstatsd-ruby)
|
8
8
|
|
9
9
|
Quick Start Guide
|
10
10
|
-----------------
|
@@ -28,9 +28,10 @@ statsd = Datadog::Statsd.new('localhost', 8125)
|
|
28
28
|
|
29
29
|
# Increment a counter.
|
30
30
|
statsd.increment('page.views')
|
31
|
+
statsd.increment('messages.count', by: 2, tags: ['kind:incoming'])
|
31
32
|
|
32
33
|
# Record a gauge 50% of the time.
|
33
|
-
statsd.gauge('users.online', 123, :
|
34
|
+
statsd.gauge('users.online', 123, sample_rate: 0.5)
|
34
35
|
|
35
36
|
# Sample a histogram
|
36
37
|
statsd.histogram('file.upload.size', 1234)
|
@@ -48,7 +49,15 @@ statsd.batch do |s|
|
|
48
49
|
end
|
49
50
|
|
50
51
|
# Tag a metric.
|
51
|
-
statsd.histogram('query.time', 10, :
|
52
|
+
statsd.histogram('query.time', 10, tags: ['version:1'])
|
53
|
+
|
54
|
+
# Tag a metric by passing in a Hash
|
55
|
+
statsd.histogram('query.time', 10, :tags => {version: 1})
|
56
|
+
|
57
|
+
# Auto-close socket after end of block
|
58
|
+
Datadog::Statsd.open('localhost', 8125) do |s|
|
59
|
+
s.increment('page.views')
|
60
|
+
end
|
52
61
|
```
|
53
62
|
|
54
63
|
You can also post events to your stream. You can tag them, set priority and even aggregate them with other events.
|
@@ -57,14 +66,34 @@ Aggregation in the stream is made on hostname/event_type/source_type/aggregation
|
|
57
66
|
|
58
67
|
``` ruby
|
59
68
|
# Post a simple message
|
60
|
-
statsd.event(
|
69
|
+
statsd.event('There might be a storm tomorrow', 'A friend warned me earlier.')
|
61
70
|
|
62
71
|
# Cry for help
|
63
|
-
statsd.event(
|
72
|
+
statsd.event(
|
73
|
+
'SO MUCH SNOW',
|
74
|
+
"Started yesterday and it won't stop !!",
|
75
|
+
alert_type: 'error',
|
76
|
+
tags: ['urgent', 'endoftheworld']
|
77
|
+
)
|
64
78
|
```
|
65
79
|
|
66
80
|
|
67
81
|
|
82
|
+
Origin detection over UDP
|
83
|
+
-------------
|
84
|
+
Origin detection is a method to detect which pod DogStatsD packets are coming from in order to add the pod's tags to the tag list.
|
85
|
+
|
86
|
+
To enable origin detection over UDP, add the following lines to your application manifest
|
87
|
+
```yaml
|
88
|
+
env:
|
89
|
+
- name: DD_ENTITY_ID
|
90
|
+
valueFrom:
|
91
|
+
fieldRef:
|
92
|
+
fieldPath: metadata.uid
|
93
|
+
```
|
94
|
+
The DogStatsD client attaches an internal tag, `entity_id`. The value of this tag is the content of the `DD_ENTITY_ID` environment variable, which is the pod’s UID.
|
95
|
+
|
96
|
+
|
68
97
|
Documentation
|
69
98
|
-------------
|
70
99
|
|
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,151 @@ 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
|
+
# Close the underlying socket
|
37
|
+
def close
|
38
|
+
@socket && @socket.close
|
39
|
+
end
|
40
|
+
|
41
|
+
def write(message)
|
42
|
+
@logger.debug { "Statsd: #{message}" } if @logger
|
43
|
+
send_message(message)
|
44
|
+
rescue StandardError => boom
|
45
|
+
# Try once to reconnect if the socket has been closed
|
46
|
+
retries ||= 1
|
47
|
+
if retries <= 1 &&
|
48
|
+
(boom.is_a?(Errno::ENOTCONN) or
|
49
|
+
boom.is_a?(Errno::ECONNREFUSED) or
|
50
|
+
boom.is_a?(IOError) && boom.message =~ /closed stream/i)
|
51
|
+
retries += 1
|
52
|
+
begin
|
53
|
+
@socket = connect
|
54
|
+
retry
|
55
|
+
rescue StandardError => e
|
56
|
+
boom = e
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
@logger.error { "Statsd: #{boom.class} #{boom}" } if @logger
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def socket
|
67
|
+
@socket ||= connect
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class UDPConnection < Connection
|
72
|
+
def initialize(host, port, logger)
|
73
|
+
@host = host || ENV.fetch('DD_AGENT_HOST', nil) || DEFAULT_HOST
|
74
|
+
@port = port || ENV.fetch('DD_DOGSTATSD_PORT', nil) || DEFAULT_PORT
|
75
|
+
@logger = logger
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def connect
|
81
|
+
socket = UDPSocket.new
|
82
|
+
socket.connect(@host, @port)
|
83
|
+
socket
|
84
|
+
end
|
85
|
+
|
86
|
+
def send_message(message)
|
87
|
+
socket.send(message, 0)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class UDSConnection < Connection
|
92
|
+
class BadSocketError < StandardError; end
|
93
|
+
|
94
|
+
def initialize(socket_path, logger)
|
95
|
+
@socket_path = socket_path
|
96
|
+
@logger = logger
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def connect
|
102
|
+
socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
|
103
|
+
socket.connect(Socket.pack_sockaddr_un(@socket_path))
|
104
|
+
socket
|
105
|
+
end
|
106
|
+
|
107
|
+
def send_message(message)
|
108
|
+
socket.sendmsg_nonblock(message)
|
109
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENOENT => e
|
110
|
+
@socket = nil
|
111
|
+
raise BadSocketError, "#{e.class}: #{e}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class Batch
|
116
|
+
def initialize(connection, max_buffer_bytes)
|
117
|
+
@connection = connection
|
118
|
+
@max_buffer_bytes = max_buffer_bytes
|
119
|
+
@depth = 0
|
120
|
+
reset
|
121
|
+
end
|
122
|
+
|
123
|
+
def open
|
124
|
+
@depth += 1
|
125
|
+
yield
|
126
|
+
ensure
|
127
|
+
@depth -= 1
|
128
|
+
flush if !open?
|
129
|
+
end
|
130
|
+
|
131
|
+
def open?
|
132
|
+
@depth > 0
|
133
|
+
end
|
134
|
+
|
135
|
+
def add(message)
|
136
|
+
message_bytes = message.bytesize
|
137
|
+
|
138
|
+
unless @buffer_bytes == 0
|
139
|
+
if @buffer_bytes + 1 + message_bytes >= @max_buffer_bytes
|
140
|
+
flush
|
141
|
+
else
|
142
|
+
@buffer << NEW_LINE
|
143
|
+
@buffer_bytes += 1
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
@buffer << message
|
148
|
+
@buffer_bytes += message_bytes
|
149
|
+
end
|
150
|
+
|
151
|
+
def flush
|
152
|
+
return if @buffer_bytes == 0
|
153
|
+
@connection.write @buffer
|
154
|
+
reset
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
def reset
|
160
|
+
@buffer = String.new
|
161
|
+
@buffer_bytes = 0
|
162
|
+
end
|
163
|
+
end
|
24
164
|
|
25
165
|
# Create a dictionary to assign a key to every parameter's name, except for tags (treated differently)
|
26
166
|
# Goal: Simple and fast to add some other parameters
|
@@ -46,78 +186,84 @@ module Datadog
|
|
46
186
|
CRITICAL = 2
|
47
187
|
UNKNOWN = 3
|
48
188
|
|
189
|
+
MAX_EVENT_SIZE = 8 * 1024
|
190
|
+
|
49
191
|
COUNTER_TYPE = 'c'.freeze
|
50
192
|
GAUGE_TYPE = 'g'.freeze
|
51
193
|
HISTOGRAM_TYPE = 'h'.freeze
|
52
194
|
DISTRIBUTION_TYPE = 'd'.freeze
|
53
195
|
TIMING_TYPE = 'ms'.freeze
|
54
196
|
SET_TYPE = 's'.freeze
|
55
|
-
VERSION = "
|
197
|
+
VERSION = "4.5.0".freeze
|
56
198
|
|
57
199
|
# A namespace to prepend to all statsd calls. Defaults to no namespace.
|
58
200
|
attr_reader :namespace
|
59
201
|
|
60
|
-
# StatsD host. Defaults to 127.0.0.1.
|
61
|
-
attr_reader :host
|
62
|
-
|
63
|
-
# StatsD port. Defaults to 8125.
|
64
|
-
attr_reader :port
|
65
|
-
|
66
|
-
# DogStatsd unix socket path. Not used by default.
|
67
|
-
attr_reader :socket_path
|
68
|
-
|
69
202
|
# Global tags to be added to every statsd call. Defaults to no tags.
|
70
203
|
attr_reader :tags
|
71
204
|
|
72
205
|
# Buffer containing the statsd message before they are sent in batch
|
73
206
|
attr_reader :buffer
|
74
207
|
|
75
|
-
# Maximum
|
76
|
-
|
208
|
+
# Maximum buffer size in bytes before it is flushed
|
209
|
+
attr_reader :max_buffer_bytes
|
77
210
|
|
78
|
-
|
79
|
-
|
80
|
-
attr_accessor :logger
|
81
|
-
end
|
211
|
+
# Default sample rate
|
212
|
+
attr_reader :sample_rate
|
82
213
|
|
83
|
-
#
|
84
|
-
|
85
|
-
def self.VERSION
|
86
|
-
VERSION
|
87
|
-
end
|
214
|
+
# Connection
|
215
|
+
attr_reader :connection
|
88
216
|
|
89
217
|
# @param [String] host your statsd host
|
90
218
|
# @param [Integer] port your statsd port
|
91
|
-
# @option
|
92
|
-
# @option
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
219
|
+
# @option [String] namespace set a namespace to be prepended to every metric name
|
220
|
+
# @option [Array<String>|Hash] tags tags to be added to every metric
|
221
|
+
# @option [Logger] logger for debugging
|
222
|
+
# @option [Integer] max_buffer_bytes max bytes to buffer when using #batch
|
223
|
+
# @option [String] socket_path unix socket path
|
224
|
+
# @option [Float] default sample rate if not overridden
|
225
|
+
def initialize(
|
226
|
+
host = nil,
|
227
|
+
port = nil,
|
228
|
+
namespace: nil,
|
229
|
+
tags: nil,
|
230
|
+
max_buffer_bytes: 8192,
|
231
|
+
socket_path: nil,
|
232
|
+
logger: nil,
|
233
|
+
sample_rate: nil
|
234
|
+
)
|
235
|
+
if socket_path.nil?
|
236
|
+
@connection = UDPConnection.new(host, port, logger)
|
237
|
+
else
|
238
|
+
@connection = UDSConnection.new(socket_path, logger)
|
239
|
+
end
|
240
|
+
@logger = logger
|
104
241
|
|
105
|
-
def namespace=(namespace) #:nodoc:
|
106
242
|
@namespace = namespace
|
107
|
-
@prefix = namespace
|
108
|
-
end
|
243
|
+
@prefix = @namespace ? "#{@namespace}.".freeze : nil
|
109
244
|
|
110
|
-
|
111
|
-
@host = host || DEFAULT_HOST
|
112
|
-
end
|
245
|
+
@sample_rate = sample_rate
|
113
246
|
|
114
|
-
|
115
|
-
|
116
|
-
|
247
|
+
unless tags.nil? or tags.is_a? Array or tags.is_a? Hash
|
248
|
+
raise ArgumentError, 'tags must be a Array<String> or a Hash'
|
249
|
+
end
|
117
250
|
|
118
|
-
|
119
|
-
raise ArgumentError, 'tags must be a Array<String>' unless tags.nil? or tags.is_a? Array
|
251
|
+
tags = tag_hash_to_array(tags) if tags.is_a? Hash
|
120
252
|
@tags = (tags || []).compact.map! {|tag| escape_tag_content(tag)}
|
253
|
+
|
254
|
+
# append the entity id to tags if DD_ENTITY_ID env var is not nil
|
255
|
+
@tags << 'dd.internal.entity_id:' + escape_tag_content(ENV.fetch('DD_ENTITY_ID', nil)) unless ENV.fetch('DD_ENTITY_ID', nil).nil?
|
256
|
+
|
257
|
+
@batch = Batch.new @connection, max_buffer_bytes
|
258
|
+
end
|
259
|
+
|
260
|
+
# yield a new instance to a block and close it when done
|
261
|
+
# for short-term use-cases that don't want to close the socket manually
|
262
|
+
def self.open(*args)
|
263
|
+
instance = new(*args)
|
264
|
+
yield instance
|
265
|
+
ensure
|
266
|
+
instance.close
|
121
267
|
end
|
122
268
|
|
123
269
|
# Sends an increment (count = 1) for the given stat to the statsd server.
|
@@ -128,7 +274,7 @@ module Datadog
|
|
128
274
|
# @option opts [Array<String>] :tags An array of tags
|
129
275
|
# @option opts [Numeric] :by increment value, default 1
|
130
276
|
# @see #count
|
131
|
-
def increment(stat, opts=
|
277
|
+
def increment(stat, opts=EMPTY_OPTIONS)
|
132
278
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
133
279
|
incr_value = opts.fetch(:by, 1)
|
134
280
|
count stat, incr_value, opts
|
@@ -142,7 +288,7 @@ module Datadog
|
|
142
288
|
# @option opts [Array<String>] :tags An array of tags
|
143
289
|
# @option opts [Numeric] :by decrement value, default 1
|
144
290
|
# @see #count
|
145
|
-
def decrement(stat, opts=
|
291
|
+
def decrement(stat, opts=EMPTY_OPTIONS)
|
146
292
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
147
293
|
decr_value = - opts.fetch(:by, 1)
|
148
294
|
count stat, decr_value, opts
|
@@ -155,7 +301,7 @@ module Datadog
|
|
155
301
|
# @param [Hash] opts the options to create the metric with
|
156
302
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
157
303
|
# @option opts [Array<String>] :tags An array of tags
|
158
|
-
def count(stat, count, opts=
|
304
|
+
def count(stat, count, opts=EMPTY_OPTIONS)
|
159
305
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
160
306
|
send_stats stat, count, COUNTER_TYPE, opts
|
161
307
|
end
|
@@ -173,7 +319,7 @@ module Datadog
|
|
173
319
|
# @option opts [Array<String>] :tags An array of tags
|
174
320
|
# @example Report the current user count:
|
175
321
|
# $statsd.gauge('user.count', User.count)
|
176
|
-
def gauge(stat, value, opts=
|
322
|
+
def gauge(stat, value, opts=EMPTY_OPTIONS)
|
177
323
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
178
324
|
send_stats stat, value, GAUGE_TYPE, opts
|
179
325
|
end
|
@@ -187,7 +333,7 @@ module Datadog
|
|
187
333
|
# @option opts [Array<String>] :tags An array of tags
|
188
334
|
# @example Report the current user count:
|
189
335
|
# $statsd.histogram('user.count', User.count)
|
190
|
-
def histogram(stat, value, opts=
|
336
|
+
def histogram(stat, value, opts=EMPTY_OPTIONS)
|
191
337
|
send_stats stat, value, HISTOGRAM_TYPE, opts
|
192
338
|
end
|
193
339
|
|
@@ -203,7 +349,7 @@ module Datadog
|
|
203
349
|
# @option opts [Array<String>] :tags An array of tags
|
204
350
|
# @example Report the current user count:
|
205
351
|
# $statsd.distribution('user.count', User.count)
|
206
|
-
def distribution(stat, value, opts=
|
352
|
+
def distribution(stat, value, opts=EMPTY_OPTIONS)
|
207
353
|
send_stats stat, value, DISTRIBUTION_TYPE, opts
|
208
354
|
end
|
209
355
|
|
@@ -217,7 +363,7 @@ module Datadog
|
|
217
363
|
# @param [Hash] opts the options to create the metric with
|
218
364
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
219
365
|
# @option opts [Array<String>] :tags An array of tags
|
220
|
-
def timing(stat, ms, opts=
|
366
|
+
def timing(stat, ms, opts=EMPTY_OPTIONS)
|
221
367
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
222
368
|
send_stats stat, ms, TIMING_TYPE, opts
|
223
369
|
end
|
@@ -235,7 +381,7 @@ module Datadog
|
|
235
381
|
# @see #timing
|
236
382
|
# @example Report the time (in ms) taken to activate an account
|
237
383
|
# $statsd.time('account.activate') { @account.activate! }
|
238
|
-
def time(stat, opts=
|
384
|
+
def time(stat, opts=EMPTY_OPTIONS)
|
239
385
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
240
386
|
start = (PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f)
|
241
387
|
return yield
|
@@ -243,6 +389,7 @@ module Datadog
|
|
243
389
|
finished = (PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f)
|
244
390
|
timing(stat, ((finished - start) * 1000).round, opts)
|
245
391
|
end
|
392
|
+
|
246
393
|
# Sends a value to be tracked as a set to the statsd server.
|
247
394
|
#
|
248
395
|
# @param [String] stat stat name.
|
@@ -252,7 +399,7 @@ module Datadog
|
|
252
399
|
# @option opts [Array<String>] :tags An array of tags
|
253
400
|
# @example Record a unique visitory by id:
|
254
401
|
# $statsd.set('visitors.uniques', User.id)
|
255
|
-
def set(stat, value, opts=
|
402
|
+
def set(stat, value, opts=EMPTY_OPTIONS)
|
256
403
|
opts = {:sample_rate => opts} if opts.is_a? Numeric
|
257
404
|
send_stats stat, value, SET_TYPE, opts
|
258
405
|
end
|
@@ -262,37 +409,14 @@ module Datadog
|
|
262
409
|
# @param [String] name Service check name
|
263
410
|
# @param [String] status Service check status.
|
264
411
|
# @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
|
412
|
+
# @option opts [Integer, String, nil] :timestamp (nil) Assign a timestamp to the service check. Default is now when none
|
413
|
+
# @option opts [String, nil] :hostname (nil) Assign a hostname to the service check.
|
267
414
|
# @option opts [Array<String>, nil] :tags (nil) An array of tags
|
268
415
|
# @option opts [String, nil] :message (nil) A message to associate with this service check status
|
269
416
|
# @example Report a critical service check status
|
270
417
|
# $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}"
|
278
|
-
|
279
|
-
SC_OPT_KEYS.each do |key, shorthand_key|
|
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
|
418
|
+
def service_check(name, status, opts=EMPTY_OPTIONS)
|
419
|
+
send_stat format_service_check(name, status, opts)
|
296
420
|
end
|
297
421
|
|
298
422
|
# 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,9 +426,9 @@ module Datadog
|
|
302
426
|
# it will be grouped with other events that don't have an event type.
|
303
427
|
#
|
304
428
|
# @param [String] title Event title
|
305
|
-
# @param [String] text Event text. Supports
|
429
|
+
# @param [String] text Event text. Supports newlines (+\n+)
|
306
430
|
# @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
|
431
|
+
# @option opts [Integer, String, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
|
308
432
|
# @option opts [String, nil] :hostname (nil) Assign a hostname to the event.
|
309
433
|
# @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others
|
310
434
|
# @option opts [String, nil] :priority ('normal') Can be "normal" or "low"
|
@@ -313,11 +437,8 @@ module Datadog
|
|
313
437
|
# @option opts [Array<String>] :tags tags to be added to every metric
|
314
438
|
# @example Report an awful event:
|
315
439
|
# $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
|
319
|
-
|
320
|
-
send_to_socket event_string
|
440
|
+
def event(title, text, opts=EMPTY_OPTIONS)
|
441
|
+
send_stat format_event(title, text, opts)
|
321
442
|
end
|
322
443
|
|
323
444
|
# Send several metrics in the same UDP Packet
|
@@ -328,24 +449,73 @@ module Datadog
|
|
328
449
|
# s.gauge('users.online',156)
|
329
450
|
# s.increment('page.views')
|
330
451
|
# end
|
331
|
-
def batch
|
332
|
-
@
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
452
|
+
def batch
|
453
|
+
@batch.open { yield self }
|
454
|
+
end
|
455
|
+
|
456
|
+
# Close the underlying socket
|
457
|
+
def close
|
458
|
+
@connection.close
|
459
|
+
end
|
460
|
+
|
461
|
+
private
|
462
|
+
|
463
|
+
NEW_LINE = "\n".freeze
|
464
|
+
ESC_NEW_LINE = "\\n".freeze
|
465
|
+
COMMA = ",".freeze
|
466
|
+
PIPE = "|".freeze
|
467
|
+
DOT = ".".freeze
|
468
|
+
DOUBLE_COLON = "::".freeze
|
469
|
+
UNDERSCORE = "_".freeze
|
470
|
+
PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= "2.1.0")
|
471
|
+
EMPTY_OPTIONS = {}.freeze
|
472
|
+
|
473
|
+
private_constant :NEW_LINE, :ESC_NEW_LINE, :COMMA, :PIPE, :DOT,
|
474
|
+
:DOUBLE_COLON, :UNDERSCORE, :EMPTY_OPTIONS
|
475
|
+
|
476
|
+
def format_service_check(name, status, opts=EMPTY_OPTIONS)
|
477
|
+
sc_string = "_sc|#{name}|#{status}".dup
|
478
|
+
|
479
|
+
SC_OPT_KEYS.each do |key, shorthand_key|
|
480
|
+
next unless opts[key]
|
481
|
+
|
482
|
+
if key == :tags
|
483
|
+
if tags_string = tags_as_string(opts)
|
484
|
+
sc_string << "|##{tags_string}"
|
485
|
+
end
|
486
|
+
elsif key == :message
|
487
|
+
message = remove_pipes(opts[:message])
|
488
|
+
escaped_message = escape_service_check_message(message)
|
489
|
+
sc_string << "|m:#{escaped_message}"
|
490
|
+
else
|
491
|
+
if key == :timestamp && opts[key].is_a?(Integer)
|
492
|
+
value = opts[key]
|
493
|
+
else
|
494
|
+
value = remove_pipes(opts[key])
|
495
|
+
end
|
496
|
+
sc_string << "|#{shorthand_key}#{value}"
|
497
|
+
end
|
498
|
+
end
|
499
|
+
sc_string
|
337
500
|
end
|
338
501
|
|
339
|
-
def format_event(title, text, opts=
|
502
|
+
def format_event(title, text, opts=EMPTY_OPTIONS)
|
340
503
|
escaped_title = escape_event_content(title)
|
341
504
|
escaped_text = escape_event_content(text)
|
342
|
-
event_string_data = "_e{#{escaped_title.
|
505
|
+
event_string_data = "_e{#{escaped_title.bytesize},#{escaped_text.bytesize}}:#{escaped_title}|#{escaped_text}".dup
|
343
506
|
|
344
507
|
# We construct the string to be sent by adding '|key:value' parts to it when needed
|
345
508
|
# All pipes ('|') in the metadata are removed. Title and Text can keep theirs
|
346
509
|
OPTS_KEYS.each do |key, shorthand_key|
|
347
510
|
if key != :tags && opts[key]
|
348
|
-
value
|
511
|
+
# :date_happened is the only key where the value is an Integer
|
512
|
+
# To not break backwards compatibility, we still accept a String
|
513
|
+
if key == :date_happened && opts[key].is_a?(Integer)
|
514
|
+
value = opts[key]
|
515
|
+
# All other keys only have String values
|
516
|
+
else
|
517
|
+
value = remove_pipes(opts[key])
|
518
|
+
end
|
349
519
|
event_string_data << "|#{shorthand_key}:#{value}"
|
350
520
|
end
|
351
521
|
end
|
@@ -355,36 +525,25 @@ module Datadog
|
|
355
525
|
event_string_data << "|##{tags_string}"
|
356
526
|
end
|
357
527
|
|
358
|
-
raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.
|
359
|
-
|
360
|
-
end
|
361
|
-
|
362
|
-
# Close the underlying socket
|
363
|
-
def close()
|
364
|
-
@socket.close
|
528
|
+
raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.bytesize > MAX_EVENT_SIZE
|
529
|
+
event_string_data
|
365
530
|
end
|
366
531
|
|
367
|
-
private
|
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
532
|
def tags_as_string(opts)
|
382
|
-
tag_arr = opts[:tags]
|
383
|
-
|
384
|
-
|
533
|
+
if tag_arr = opts[:tags]
|
534
|
+
tag_arr = tag_hash_to_array(tag_arr) if tag_arr.is_a? Hash
|
535
|
+
tag_arr = tag_arr.map { |tag| escape_tag_content(tag) }
|
536
|
+
tag_arr = tags + tag_arr # @tags are normalized when set, so not need to normalize them again
|
537
|
+
else
|
538
|
+
tag_arr = tags
|
539
|
+
end
|
385
540
|
tag_arr.join(COMMA) unless tag_arr.empty?
|
386
541
|
end
|
387
542
|
|
543
|
+
def tag_hash_to_array(tag_hash)
|
544
|
+
tag_hash.to_a.map {|pair| pair.compact.join(":")}
|
545
|
+
end
|
546
|
+
|
388
547
|
def escape_event_content(msg)
|
389
548
|
msg.gsub NEW_LINE, ESC_NEW_LINE
|
390
549
|
end
|
@@ -403,10 +562,10 @@ module Datadog
|
|
403
562
|
escape_event_content(msg).gsub('m:'.freeze, 'm\:'.freeze)
|
404
563
|
end
|
405
564
|
|
406
|
-
def send_stats(stat, delta, type, opts=
|
407
|
-
sample_rate = opts[:sample_rate] || 1
|
408
|
-
if sample_rate == 1 or rand
|
409
|
-
full_stat = ''
|
565
|
+
def send_stats(stat, delta, type, opts=EMPTY_OPTIONS)
|
566
|
+
sample_rate = opts[:sample_rate] || @sample_rate || 1
|
567
|
+
if sample_rate == 1 or rand <= sample_rate
|
568
|
+
full_stat = ''.dup
|
410
569
|
full_stat << @prefix if @prefix
|
411
570
|
|
412
571
|
stat = stat.is_a?(String) ? stat.dup : stat.to_s
|
@@ -437,62 +596,11 @@ module Datadog
|
|
437
596
|
end
|
438
597
|
|
439
598
|
def send_stat(message)
|
440
|
-
if @
|
441
|
-
@
|
442
|
-
flush_buffer if @buffer.length >= @max_buffer_size
|
599
|
+
if @batch.open?
|
600
|
+
@batch.add message
|
443
601
|
else
|
444
|
-
|
445
|
-
end
|
446
|
-
end
|
447
|
-
|
448
|
-
def flush_buffer
|
449
|
-
return @buffer if @buffer.empty?
|
450
|
-
send_to_socket(@buffer.join(NEW_LINE))
|
451
|
-
@buffer = Array.new
|
452
|
-
end
|
453
|
-
|
454
|
-
def connect_to_socket
|
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)
|
602
|
+
@connection.write(message)
|
461
603
|
end
|
462
|
-
socket
|
463
|
-
end
|
464
|
-
|
465
|
-
def sock
|
466
|
-
@socket ||= connect_to_socket
|
467
|
-
end
|
468
|
-
|
469
|
-
def send_to_socket(message)
|
470
|
-
self.class.logger.debug { "Statsd: #{message}" } if self.class.logger
|
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
|
493
|
-
|
494
|
-
self.class.logger.error { "Statsd: #{boom.class} #{boom}" } if self.class.logger
|
495
|
-
nil
|
496
604
|
end
|
497
605
|
end
|
498
606
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dogstatsd-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.5.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:
|
11
|
+
date: 2019-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A Ruby DogStastd client
|
14
14
|
email: code@datadoghq.com
|
@@ -33,7 +33,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
33
33
|
requirements:
|
34
34
|
- - ">="
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version:
|
36
|
+
version: 2.0.0
|
37
37
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - ">="
|
@@ -41,7 +41,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
41
41
|
version: '0'
|
42
42
|
requirements: []
|
43
43
|
rubyforge_project:
|
44
|
-
rubygems_version: 2.7.
|
44
|
+
rubygems_version: 2.7.6
|
45
45
|
signing_key:
|
46
46
|
specification_version: 4
|
47
47
|
summary: A Ruby DogStatsd client
|