dogstatsd-ruby 4.3.0 → 4.8.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 +36 -78
- data/lib/datadog/statsd.rb +108 -343
- data/lib/datadog/statsd/batch.rb +56 -0
- data/lib/datadog/statsd/connection.rb +62 -0
- data/lib/datadog/statsd/serialization.rb +15 -0
- data/lib/datadog/statsd/serialization/event_serializer.rb +68 -0
- data/lib/datadog/statsd/serialization/serializer.rb +41 -0
- data/lib/datadog/statsd/serialization/service_check_serializer.rb +61 -0
- data/lib/datadog/statsd/serialization/stat_serializer.rb +62 -0
- data/lib/datadog/statsd/serialization/tag_serializer.rb +91 -0
- data/lib/datadog/statsd/telemetry.rb +98 -0
- data/lib/datadog/statsd/udp_connection.rb +37 -0
- data/lib/datadog/statsd/uds_connection.rb +38 -0
- data/lib/datadog/statsd/version.rb +9 -0
- metadata +21 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7485524dc133a21013d2ab5144511a4ba38740226bdeaf1d3131be99859f33ec
|
4
|
+
data.tar.gz: 6358ca98c85788c904e48ebf3d0d3751372b3ba5baaaf194199fef1f5badbae0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: edf3b324873893d4acfff6a8775ee7d3ca337e416f283f29137abbaed447f8db220d8d09cc00131a0a53b0d4f38555f48d669658812ebe739903dc68a74ab8c3
|
7
|
+
data.tar.gz: 9b5bd953d65e25ad74733175febb8a4a3b4dfee5e2353d1f539684443ddface8f65c9ebde4f88dfd5bd4063b892c2c76350754a112481bfeacb2f454a199dac9
|
data/README.md
CHANGED
@@ -1,86 +1,40 @@
|
|
1
|
+
# dogstatsd-ruby
|
1
2
|
|
2
|
-
dogstatsd-ruby
|
3
|
-
==============
|
3
|
+
A client for DogStatsD, an extension of the StatsD metric server for Datadog. Full API documentation is available in [DogStatsD-ruby rubydoc](https://www.rubydoc.info/github/DataDog/dogstatsd-ruby/master/Datadog/Statsd).
|
4
4
|
|
5
|
-
|
5
|
+
[](http://travis-ci.org/DataDog/dogstatsd-ruby)
|
6
6
|
|
7
|
-
[
|
7
|
+
See [CHANGELOG.md](CHANGELOG.md) for changes. To suggest a feature, report a bug, or general discussion, [open an issue](http://github.com/DataDog/dogstatsd-ruby/issues/).
|
8
8
|
|
9
|
-
|
10
|
-
-----------------
|
9
|
+
## Installation
|
11
10
|
|
12
11
|
First install the library:
|
13
12
|
|
14
|
-
|
13
|
+
```
|
14
|
+
gem install dogstatsd-ruby
|
15
|
+
```
|
16
|
+
|
17
|
+
## Configuration
|
15
18
|
|
16
|
-
|
19
|
+
To instantiate a DogStatsd client:
|
17
20
|
|
18
|
-
```
|
19
|
-
#
|
21
|
+
```ruby
|
22
|
+
# Import the library
|
20
23
|
require 'datadog/statsd'
|
21
24
|
|
22
|
-
# Create a
|
25
|
+
# Create a DogStatsD client instance.
|
23
26
|
statsd = Datadog::Statsd.new('localhost', 8125)
|
24
|
-
|
25
|
-
# you could also create a statsd class if you need a drop in replacement
|
26
|
-
# class Statsd < Datadog::Statsd
|
27
|
-
# end
|
28
|
-
|
29
|
-
# Increment a counter.
|
30
|
-
statsd.increment('page.views')
|
31
|
-
statsd.increment('messages.count', by: 2, tags: ['kind:incoming'])
|
32
|
-
|
33
|
-
# Record a gauge 50% of the time.
|
34
|
-
statsd.gauge('users.online', 123, sample_rate: 0.5)
|
35
|
-
|
36
|
-
# Sample a histogram
|
37
|
-
statsd.histogram('file.upload.size', 1234)
|
38
|
-
|
39
|
-
# Time a block of code
|
40
|
-
statsd.time('page.render') do
|
41
|
-
render_page('home.html')
|
42
|
-
end
|
43
|
-
|
44
|
-
# Send several metrics at the same time
|
45
|
-
# All metrics will be buffered and sent in one packet when the block completes
|
46
|
-
statsd.batch do |s|
|
47
|
-
s.increment('page.views')
|
48
|
-
s.gauge('users.online', 123)
|
49
|
-
end
|
50
|
-
|
51
|
-
# Tag a metric.
|
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
|
61
27
|
```
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
``` ruby
|
68
|
-
# Post a simple message
|
69
|
-
statsd.event('There might be a storm tomorrow', 'A friend warned me earlier.')
|
70
|
-
|
71
|
-
# Cry for help
|
72
|
-
statsd.event(
|
73
|
-
'SO MUCH SNOW',
|
74
|
-
"Started yesterday and it won't stop !!",
|
75
|
-
alert_type: 'error',
|
76
|
-
tags: ['urgent', 'endoftheworld']
|
77
|
-
)
|
28
|
+
Or if you want to connect over Unix Domain Socket:
|
29
|
+
```ruby
|
30
|
+
# Connection over Unix Domain Socket
|
31
|
+
statsd = Datadog::Statsd.new(socket_path: '/path/to/socket/file')
|
78
32
|
```
|
79
33
|
|
34
|
+
Find a list of all the available options for your DogStatsD Client in the [DogStatsD-ruby rubydoc](https://www.rubydoc.info/github/DataDog/dogstatsd-ruby/master/Datadog/Statsd) or in the [Datadog public DogStatsD documentation](https://docs.datadoghq.com/developers/dogstatsd/?tab=ruby#client-instantiation-parameters).
|
80
35
|
|
36
|
+
### Origin detection over UDP
|
81
37
|
|
82
|
-
Origin detection over UDP
|
83
|
-
-------------
|
84
38
|
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
39
|
|
86
40
|
To enable origin detection over UDP, add the following lines to your application manifest
|
@@ -93,27 +47,31 @@ env:
|
|
93
47
|
```
|
94
48
|
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
49
|
|
50
|
+
## Usage
|
51
|
+
|
52
|
+
In order to use DogStatsD metrics, events, and Service Checks the Agent must be [running and available](https://docs.datadoghq.com/developers/dogstatsd/?tab=ruby).
|
96
53
|
|
97
|
-
|
98
|
-
-------------
|
54
|
+
### Metrics
|
99
55
|
|
100
|
-
|
101
|
-
[here](http://www.rubydoc.info/github/DataDog/dogstatsd-ruby/master/frames).
|
56
|
+
After the client is created, you can start sending custom metrics to Datadog. See the dedicated [Metric Submission: DogStatsD documentation](https://docs.datadoghq.com/developers/metrics/dogstatsd_metrics_submission/?tab=ruby) to see how to submit all supported metric types to Datadog with working code examples:
|
102
57
|
|
58
|
+
* [Submit a COUNT metric](https://docs.datadoghq.com/developers/metrics/dogstatsd_metrics_submission/?tab=ruby#count).
|
59
|
+
* [Submit a GAUGE metric](https://docs.datadoghq.com/developers/metrics/dogstatsd_metrics_submission/?tab=ruby#gauge).
|
60
|
+
* [Submit a SET metric](https://docs.datadoghq.com/developers/metrics/dogstatsd_metrics_submission/?tab=ruby#set)
|
61
|
+
* [Submit a HISTOGRAM metric](https://docs.datadoghq.com/developers/metrics/dogstatsd_metrics_submission/?tab=ruby#histogram)
|
62
|
+
* [Submit a DISTRIBUTION metric](https://docs.datadoghq.com/developers/metrics/dogstatsd_metrics_submission/?tab=ruby#distribution)
|
103
63
|
|
104
|
-
|
105
|
-
--------
|
64
|
+
Some options are suppported when submitting metrics, like [applying a Sample Rate to your metrics](https://docs.datadoghq.com/developers/metrics/dogstatsd_metrics_submission/?tab=ruby#metric-submission-options) or [tagging your metrics with your custom tags](https://docs.datadoghq.com/developers/metrics/dogstatsd_metrics_submission/?tab=ruby#metric-tagging). Find all the available functions to report metrics in the [DogStatsD-ruby rubydoc](https://www.rubydoc.info/github/DataDog/dogstatsd-ruby/master/Datadog/Statsd).
|
106
65
|
|
107
|
-
|
108
|
-
[here](http://github.com/DataDog/dogstatsd-ruby/issues/).
|
66
|
+
### Events
|
109
67
|
|
68
|
+
After the client is created, you can start sending events to your Datadog Event Stream. See the dedicated [Event Submission: DogStatsD documentation](https://docs.datadoghq.com/developers/events/dogstatsd/?tab=ruby) to see how to submit an event to Datadog your Event Stream.
|
110
69
|
|
111
|
-
|
112
|
-
----------------------------
|
70
|
+
### Service Checks
|
113
71
|
|
72
|
+
After the client is created, you can start sending Service Checks to Datadog. See the dedicated [Service Check Submission: DogStatsD documentation](https://docs.datadoghq.com/developers/service_checks/dogstatsd_service_checks_submission/?tab=ruby) to see how to submit a Service Check to Datadog.
|
114
73
|
|
115
|
-
Credits
|
116
|
-
-------
|
74
|
+
## Credits
|
117
75
|
|
118
76
|
dogstatsd-ruby is forked from Rien Henrichs [original Statsd
|
119
77
|
client](https://github.com/reinh/statsd).
|
data/lib/datadog/statsd.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
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/batch'
|
9
|
+
require_relative 'statsd/serialization'
|
10
|
+
|
4
11
|
# = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
|
5
12
|
#
|
6
13
|
# @example Set up a global Statsd client for a server on localhost:8125
|
@@ -19,186 +26,30 @@ require 'socket'
|
|
19
26
|
# statsd = Datadog::Statsd.new 'localhost', 8125, tags: 'tag1:true'
|
20
27
|
module Datadog
|
21
28
|
class Statsd
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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 && boom.is_a?(Errno::ENOTCONN) or
|
48
|
-
retries <= 1 && boom.is_a?(IOError) && boom.message =~ /closed stream/i
|
49
|
-
retries += 1
|
50
|
-
begin
|
51
|
-
@socket = connect
|
52
|
-
retry
|
53
|
-
rescue StandardError => e
|
54
|
-
boom = e
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
@logger.error { "Statsd: #{boom.class} #{boom}" } if @logger
|
59
|
-
nil
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
|
64
|
-
def socket
|
65
|
-
@socket ||= connect
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
class UDPConnection < Connection
|
70
|
-
def initialize(host, port, logger)
|
71
|
-
@host = host || ENV.fetch('DD_AGENT_HOST', nil) || DEFAULT_HOST
|
72
|
-
@port = port || ENV.fetch('DD_DOGSTATSD_PORT', nil) || DEFAULT_PORT
|
73
|
-
@logger = logger
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
def connect
|
79
|
-
socket = UDPSocket.new
|
80
|
-
socket.connect(@host, @port)
|
81
|
-
socket
|
82
|
-
end
|
83
|
-
|
84
|
-
def send_message(message)
|
85
|
-
socket.send(message, 0)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
class UDSConnection < Connection
|
90
|
-
class BadSocketError < StandardError; end
|
91
|
-
|
92
|
-
def initialize(socket_path, logger)
|
93
|
-
@socket_path = socket_path
|
94
|
-
@logger = logger
|
95
|
-
end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
def connect
|
100
|
-
socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
|
101
|
-
socket.connect(Socket.pack_sockaddr_un(@socket_path))
|
102
|
-
socket
|
103
|
-
end
|
104
|
-
|
105
|
-
def send_message(message)
|
106
|
-
socket.sendmsg_nonblock(message)
|
107
|
-
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENOENT => e
|
108
|
-
@socket = nil
|
109
|
-
raise BadSocketError, "#{e.class}: #{e}"
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
class Batch
|
114
|
-
def initialize(connection, max_buffer_bytes)
|
115
|
-
@connection = connection
|
116
|
-
@max_buffer_bytes = max_buffer_bytes
|
117
|
-
@depth = 0
|
118
|
-
reset
|
119
|
-
end
|
120
|
-
|
121
|
-
def open
|
122
|
-
@depth += 1
|
123
|
-
yield
|
124
|
-
ensure
|
125
|
-
@depth -= 1
|
126
|
-
flush if !open?
|
127
|
-
end
|
128
|
-
|
129
|
-
def open?
|
130
|
-
@depth > 0
|
131
|
-
end
|
132
|
-
|
133
|
-
def add(message)
|
134
|
-
message_bytes = message.bytesize
|
135
|
-
|
136
|
-
unless @buffer_bytes == 0
|
137
|
-
if @buffer_bytes + 1 + message_bytes >= @max_buffer_bytes
|
138
|
-
flush
|
139
|
-
else
|
140
|
-
@buffer << NEW_LINE
|
141
|
-
@buffer_bytes += 1
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
@buffer << message
|
146
|
-
@buffer_bytes += message_bytes
|
147
|
-
end
|
148
|
-
|
149
|
-
def flush
|
150
|
-
return if @buffer_bytes == 0
|
151
|
-
@connection.write @buffer
|
152
|
-
reset
|
153
|
-
end
|
154
|
-
|
155
|
-
private
|
156
|
-
|
157
|
-
def reset
|
158
|
-
@buffer = String.new
|
159
|
-
@buffer_bytes = 0
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
# Create a dictionary to assign a key to every parameter's name, except for tags (treated differently)
|
164
|
-
# Goal: Simple and fast to add some other parameters
|
165
|
-
OPTS_KEYS = {
|
166
|
-
:date_happened => :d,
|
167
|
-
:hostname => :h,
|
168
|
-
:aggregation_key => :k,
|
169
|
-
:priority => :p,
|
170
|
-
:source_type_name => :s,
|
171
|
-
:alert_type => :t,
|
172
|
-
}
|
173
|
-
|
174
|
-
# Service check options
|
175
|
-
SC_OPT_KEYS = {
|
176
|
-
:timestamp => 'd:'.freeze,
|
177
|
-
:hostname => 'h:'.freeze,
|
178
|
-
:tags => '#'.freeze,
|
179
|
-
:message => 'm:'.freeze,
|
180
|
-
}
|
181
|
-
|
182
|
-
OK = 0
|
183
|
-
WARNING = 1
|
184
|
-
CRITICAL = 2
|
185
|
-
UNKNOWN = 3
|
186
|
-
|
187
|
-
MAX_EVENT_SIZE = 8 * 1024
|
188
|
-
|
189
|
-
COUNTER_TYPE = 'c'.freeze
|
190
|
-
GAUGE_TYPE = 'g'.freeze
|
191
|
-
HISTOGRAM_TYPE = 'h'.freeze
|
192
|
-
DISTRIBUTION_TYPE = 'd'.freeze
|
193
|
-
TIMING_TYPE = 'ms'.freeze
|
194
|
-
SET_TYPE = 's'.freeze
|
195
|
-
VERSION = "4.3.0".freeze
|
29
|
+
OK = 0
|
30
|
+
WARNING = 1
|
31
|
+
CRITICAL = 2
|
32
|
+
UNKNOWN = 3
|
33
|
+
|
34
|
+
DEFAULT_BUFFER_SIZE = 8 * 1_024
|
35
|
+
MAX_EVENT_SIZE = 8 * 1_024
|
36
|
+
# minimum flush interval for the telemetry in seconds
|
37
|
+
DEFAULT_TELEMETRY_FLUSH_INTERVAL = 10
|
38
|
+
|
39
|
+
COUNTER_TYPE = 'c'
|
40
|
+
GAUGE_TYPE = 'g'
|
41
|
+
HISTOGRAM_TYPE = 'h'
|
42
|
+
DISTRIBUTION_TYPE = 'd'
|
43
|
+
TIMING_TYPE = 'ms'
|
44
|
+
SET_TYPE = 's'
|
196
45
|
|
197
46
|
# A namespace to prepend to all statsd calls. Defaults to no namespace.
|
198
47
|
attr_reader :namespace
|
199
48
|
|
200
49
|
# Global tags to be added to every statsd call. Defaults to no tags.
|
201
|
-
|
50
|
+
def tags
|
51
|
+
serializer.global_tags
|
52
|
+
end
|
202
53
|
|
203
54
|
# Buffer containing the statsd message before they are sent in batch
|
204
55
|
attr_reader :buffer
|
@@ -225,40 +76,49 @@ module Datadog
|
|
225
76
|
port = nil,
|
226
77
|
namespace: nil,
|
227
78
|
tags: nil,
|
228
|
-
max_buffer_bytes:
|
79
|
+
max_buffer_bytes: DEFAULT_BUFFER_SIZE,
|
229
80
|
socket_path: nil,
|
230
81
|
logger: nil,
|
231
|
-
sample_rate: nil
|
82
|
+
sample_rate: nil,
|
83
|
+
disable_telemetry: false,
|
84
|
+
telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL
|
232
85
|
)
|
233
|
-
|
234
|
-
|
235
|
-
else
|
236
|
-
@connection = UDSConnection.new(socket_path, logger)
|
86
|
+
unless tags.nil? || tags.is_a?(Array) || tags.is_a?(Hash)
|
87
|
+
raise ArgumentError, 'tags must be a Array<String> or a Hash'
|
237
88
|
end
|
238
|
-
@logger = logger
|
239
89
|
|
240
90
|
@namespace = namespace
|
241
91
|
@prefix = @namespace ? "#{@namespace}.".freeze : nil
|
242
92
|
|
243
|
-
@
|
93
|
+
@serializer = Serialization::Serializer.new(prefix: @prefix, global_tags: tags)
|
244
94
|
|
245
|
-
|
246
|
-
|
247
|
-
|
95
|
+
transport_type = socket_path.nil? ? :udp : :uds
|
96
|
+
|
97
|
+
@telemetry = Telemetry.new(disable_telemetry, telemetry_flush_interval,
|
98
|
+
global_tags: tags,
|
99
|
+
transport_type: transport_type
|
100
|
+
)
|
101
|
+
|
102
|
+
@connection = case transport_type
|
103
|
+
when :udp
|
104
|
+
UDPConnection.new(host, port, logger, telemetry)
|
105
|
+
when :uds
|
106
|
+
UDSConnection.new(socket_path, logger, telemetry)
|
107
|
+
end
|
248
108
|
|
249
|
-
|
250
|
-
@tags = (tags || []).compact.map! {|tag| escape_tag_content(tag)}
|
109
|
+
@logger = logger
|
251
110
|
|
252
|
-
|
253
|
-
@tags << 'dd.internal.entity_id:' + escape_tag_content(ENV.fetch('DD_ENTITY_ID', nil)) unless ENV.fetch('DD_ENTITY_ID', nil).nil?
|
111
|
+
@sample_rate = sample_rate
|
254
112
|
|
255
|
-
|
113
|
+
# we reduce max_buffer_bytes by a the rough estimate of the telemetry payload
|
114
|
+
@batch = Batch.new(connection, (max_buffer_bytes - telemetry.estimate_max_size))
|
256
115
|
end
|
257
116
|
|
258
117
|
# yield a new instance to a block and close it when done
|
259
118
|
# for short-term use-cases that don't want to close the socket manually
|
260
119
|
def self.open(*args)
|
261
120
|
instance = new(*args)
|
121
|
+
|
262
122
|
yield instance
|
263
123
|
ensure
|
264
124
|
instance.close
|
@@ -272,10 +132,10 @@ module Datadog
|
|
272
132
|
# @option opts [Array<String>] :tags An array of tags
|
273
133
|
# @option opts [Numeric] :by increment value, default 1
|
274
134
|
# @see #count
|
275
|
-
def increment(stat, opts=EMPTY_OPTIONS)
|
276
|
-
opts = {:
|
135
|
+
def increment(stat, opts = EMPTY_OPTIONS)
|
136
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
277
137
|
incr_value = opts.fetch(:by, 1)
|
278
|
-
count
|
138
|
+
count(stat, incr_value, opts)
|
279
139
|
end
|
280
140
|
|
281
141
|
# Sends a decrement (count = -1) for the given stat to the statsd server.
|
@@ -286,10 +146,10 @@ module Datadog
|
|
286
146
|
# @option opts [Array<String>] :tags An array of tags
|
287
147
|
# @option opts [Numeric] :by decrement value, default 1
|
288
148
|
# @see #count
|
289
|
-
def decrement(stat, opts=EMPTY_OPTIONS)
|
290
|
-
opts = {:
|
149
|
+
def decrement(stat, opts = EMPTY_OPTIONS)
|
150
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
291
151
|
decr_value = - opts.fetch(:by, 1)
|
292
|
-
count
|
152
|
+
count(stat, decr_value, opts)
|
293
153
|
end
|
294
154
|
|
295
155
|
# Sends an arbitrary count for the given stat to the statsd server.
|
@@ -299,9 +159,9 @@ module Datadog
|
|
299
159
|
# @param [Hash] opts the options to create the metric with
|
300
160
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
301
161
|
# @option opts [Array<String>] :tags An array of tags
|
302
|
-
def count(stat, count, opts=EMPTY_OPTIONS)
|
303
|
-
opts = {:
|
304
|
-
send_stats
|
162
|
+
def count(stat, count, opts = EMPTY_OPTIONS)
|
163
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
164
|
+
send_stats(stat, count, COUNTER_TYPE, opts)
|
305
165
|
end
|
306
166
|
|
307
167
|
# Sends an arbitary gauge value for the given stat to the statsd server.
|
@@ -317,9 +177,9 @@ module Datadog
|
|
317
177
|
# @option opts [Array<String>] :tags An array of tags
|
318
178
|
# @example Report the current user count:
|
319
179
|
# $statsd.gauge('user.count', User.count)
|
320
|
-
def gauge(stat, value, opts=EMPTY_OPTIONS)
|
321
|
-
opts = {:
|
322
|
-
send_stats
|
180
|
+
def gauge(stat, value, opts = EMPTY_OPTIONS)
|
181
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
182
|
+
send_stats(stat, value, GAUGE_TYPE, opts)
|
323
183
|
end
|
324
184
|
|
325
185
|
# Sends a value to be tracked as a histogram to the statsd server.
|
@@ -331,14 +191,11 @@ module Datadog
|
|
331
191
|
# @option opts [Array<String>] :tags An array of tags
|
332
192
|
# @example Report the current user count:
|
333
193
|
# $statsd.histogram('user.count', User.count)
|
334
|
-
def histogram(stat, value, opts=EMPTY_OPTIONS)
|
335
|
-
send_stats
|
194
|
+
def histogram(stat, value, opts = EMPTY_OPTIONS)
|
195
|
+
send_stats(stat, value, HISTOGRAM_TYPE, opts)
|
336
196
|
end
|
337
197
|
|
338
198
|
# Sends a value to be tracked as a distribution to the statsd server.
|
339
|
-
# Note: Distributions are a beta feature of Datadog and not generally
|
340
|
-
# available. Distributions must be specifically enabled for your
|
341
|
-
# organization.
|
342
199
|
#
|
343
200
|
# @param [String] stat stat name.
|
344
201
|
# @param [Numeric] value distribution value.
|
@@ -347,8 +204,8 @@ module Datadog
|
|
347
204
|
# @option opts [Array<String>] :tags An array of tags
|
348
205
|
# @example Report the current user count:
|
349
206
|
# $statsd.distribution('user.count', User.count)
|
350
|
-
def distribution(stat, value, opts=EMPTY_OPTIONS)
|
351
|
-
send_stats
|
207
|
+
def distribution(stat, value, opts = EMPTY_OPTIONS)
|
208
|
+
send_stats(stat, value, DISTRIBUTION_TYPE, opts)
|
352
209
|
end
|
353
210
|
|
354
211
|
# Sends a timing (in ms) for the given stat to the statsd server. The
|
@@ -361,9 +218,9 @@ module Datadog
|
|
361
218
|
# @param [Hash] opts the options to create the metric with
|
362
219
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
363
220
|
# @option opts [Array<String>] :tags An array of tags
|
364
|
-
def timing(stat, ms, opts=EMPTY_OPTIONS)
|
365
|
-
opts = {:
|
366
|
-
send_stats
|
221
|
+
def timing(stat, ms, opts = EMPTY_OPTIONS)
|
222
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
223
|
+
send_stats(stat, ms, TIMING_TYPE, opts)
|
367
224
|
end
|
368
225
|
|
369
226
|
# Reports execution time of the provided block using {#timing}.
|
@@ -379,13 +236,12 @@ module Datadog
|
|
379
236
|
# @see #timing
|
380
237
|
# @example Report the time (in ms) taken to activate an account
|
381
238
|
# $statsd.time('account.activate') { @account.activate! }
|
382
|
-
def time(stat, opts=EMPTY_OPTIONS)
|
383
|
-
opts = {:
|
384
|
-
start =
|
385
|
-
|
239
|
+
def time(stat, opts = EMPTY_OPTIONS)
|
240
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
241
|
+
start = now
|
242
|
+
yield
|
386
243
|
ensure
|
387
|
-
|
388
|
-
timing(stat, ((finished - start) * 1000).round, opts)
|
244
|
+
timing(stat, ((now - start) * 1000).round, opts)
|
389
245
|
end
|
390
246
|
|
391
247
|
# Sends a value to be tracked as a set to the statsd server.
|
@@ -397,9 +253,9 @@ module Datadog
|
|
397
253
|
# @option opts [Array<String>] :tags An array of tags
|
398
254
|
# @example Record a unique visitory by id:
|
399
255
|
# $statsd.set('visitors.uniques', User.id)
|
400
|
-
def set(stat, value, opts=EMPTY_OPTIONS)
|
401
|
-
opts = {:
|
402
|
-
send_stats
|
256
|
+
def set(stat, value, opts = EMPTY_OPTIONS)
|
257
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
258
|
+
send_stats(stat, value, SET_TYPE, opts)
|
403
259
|
end
|
404
260
|
|
405
261
|
# This method allows you to send custom service check statuses.
|
@@ -407,14 +263,16 @@ module Datadog
|
|
407
263
|
# @param [String] name Service check name
|
408
264
|
# @param [String] status Service check status.
|
409
265
|
# @param [Hash] opts the additional data about the service check
|
410
|
-
# @option opts [Integer, nil] :timestamp (nil) Assign a timestamp to the
|
411
|
-
# @option opts [String, nil] :hostname (nil) Assign a hostname to the
|
266
|
+
# @option opts [Integer, String, nil] :timestamp (nil) Assign a timestamp to the service check. Default is now when none
|
267
|
+
# @option opts [String, nil] :hostname (nil) Assign a hostname to the service check.
|
412
268
|
# @option opts [Array<String>, nil] :tags (nil) An array of tags
|
413
269
|
# @option opts [String, nil] :message (nil) A message to associate with this service check status
|
414
270
|
# @example Report a critical service check status
|
415
271
|
# $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
|
416
|
-
def service_check(name, status, opts=EMPTY_OPTIONS)
|
417
|
-
|
272
|
+
def service_check(name, status, opts = EMPTY_OPTIONS)
|
273
|
+
telemetry.sent(service_checks: 1)
|
274
|
+
|
275
|
+
send_stat(serializer.to_service_check(name, status, opts))
|
418
276
|
end
|
419
277
|
|
420
278
|
# This end point allows you to post events to the stream. You can tag them, set priority and even aggregate them with other events.
|
@@ -426,7 +284,7 @@ module Datadog
|
|
426
284
|
# @param [String] title Event title
|
427
285
|
# @param [String] text Event text. Supports newlines (+\n+)
|
428
286
|
# @param [Hash] opts the additional data about the event
|
429
|
-
# @option opts [Integer, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
|
287
|
+
# @option opts [Integer, String, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
|
430
288
|
# @option opts [String, nil] :hostname (nil) Assign a hostname to the event.
|
431
289
|
# @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others
|
432
290
|
# @option opts [String, nil] :priority ('normal') Can be "normal" or "low"
|
@@ -435,8 +293,10 @@ module Datadog
|
|
435
293
|
# @option opts [Array<String>] :tags tags to be added to every metric
|
436
294
|
# @example Report an awful event:
|
437
295
|
# $statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent'])
|
438
|
-
def event(title, text, opts=EMPTY_OPTIONS)
|
439
|
-
|
296
|
+
def event(title, text, opts = EMPTY_OPTIONS)
|
297
|
+
telemetry.sent(events: 1)
|
298
|
+
|
299
|
+
send_stat(serializer.to_event(title, text, opts))
|
440
300
|
end
|
441
301
|
|
442
302
|
# Send several metrics in the same UDP Packet
|
@@ -448,135 +308,40 @@ module Datadog
|
|
448
308
|
# s.increment('page.views')
|
449
309
|
# end
|
450
310
|
def batch
|
451
|
-
@batch.open
|
311
|
+
@batch.open do
|
312
|
+
yield self
|
313
|
+
end
|
452
314
|
end
|
453
315
|
|
454
316
|
# Close the underlying socket
|
455
317
|
def close
|
456
|
-
|
318
|
+
connection.close
|
457
319
|
end
|
458
320
|
|
459
321
|
private
|
322
|
+
attr_reader :serializer
|
323
|
+
attr_reader :telemetry
|
460
324
|
|
461
|
-
|
462
|
-
ESC_NEW_LINE = "\\n".freeze
|
463
|
-
COMMA = ",".freeze
|
464
|
-
PIPE = "|".freeze
|
465
|
-
DOT = ".".freeze
|
466
|
-
DOUBLE_COLON = "::".freeze
|
467
|
-
UNDERSCORE = "_".freeze
|
468
|
-
PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= "2.1.0")
|
325
|
+
PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= '2.1.0')
|
469
326
|
EMPTY_OPTIONS = {}.freeze
|
470
327
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
def format_service_check(name, status, opts=EMPTY_OPTIONS)
|
475
|
-
sc_string = "_sc|#{name}|#{status}".dup
|
476
|
-
|
477
|
-
SC_OPT_KEYS.each do |key, shorthand_key|
|
478
|
-
next unless opts[key]
|
479
|
-
|
480
|
-
if key == :tags
|
481
|
-
if tags_string = tags_as_string(opts)
|
482
|
-
sc_string << "|##{tags_string}"
|
483
|
-
end
|
484
|
-
elsif key == :message
|
485
|
-
message = remove_pipes(opts[:message])
|
486
|
-
escaped_message = escape_service_check_message(message)
|
487
|
-
sc_string << "|m:#{escaped_message}"
|
488
|
-
else
|
489
|
-
value = remove_pipes(opts[key])
|
490
|
-
sc_string << "|#{shorthand_key}#{value}"
|
491
|
-
end
|
492
|
-
end
|
493
|
-
sc_string
|
494
|
-
end
|
495
|
-
|
496
|
-
def format_event(title, text, opts=EMPTY_OPTIONS)
|
497
|
-
escaped_title = escape_event_content(title)
|
498
|
-
escaped_text = escape_event_content(text)
|
499
|
-
event_string_data = "_e{#{escaped_title.bytesize},#{escaped_text.bytesize}}:#{escaped_title}|#{escaped_text}".dup
|
500
|
-
|
501
|
-
# We construct the string to be sent by adding '|key:value' parts to it when needed
|
502
|
-
# All pipes ('|') in the metadata are removed. Title and Text can keep theirs
|
503
|
-
OPTS_KEYS.each do |key, shorthand_key|
|
504
|
-
if key != :tags && opts[key]
|
505
|
-
value = remove_pipes(opts[key])
|
506
|
-
event_string_data << "|#{shorthand_key}:#{value}"
|
507
|
-
end
|
508
|
-
end
|
509
|
-
|
510
|
-
# Tags are joined and added as last part to the string to be sent
|
511
|
-
if tags_string = tags_as_string(opts)
|
512
|
-
event_string_data << "|##{tags_string}"
|
328
|
+
if PROCESS_TIME_SUPPORTED
|
329
|
+
def now
|
330
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
513
331
|
end
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
end
|
518
|
-
|
519
|
-
def tags_as_string(opts)
|
520
|
-
if tag_arr = opts[:tags]
|
521
|
-
tag_arr = tag_hash_to_array(tag_arr) if tag_arr.is_a? Hash
|
522
|
-
tag_arr = tag_arr.map { |tag| escape_tag_content(tag) }
|
523
|
-
tag_arr = tags + tag_arr # @tags are normalized when set, so not need to normalize them again
|
524
|
-
else
|
525
|
-
tag_arr = tags
|
332
|
+
else
|
333
|
+
def now
|
334
|
+
Time.now.to_f
|
526
335
|
end
|
527
|
-
tag_arr.join(COMMA) unless tag_arr.empty?
|
528
336
|
end
|
529
337
|
|
530
|
-
def
|
531
|
-
|
532
|
-
end
|
533
|
-
|
534
|
-
def escape_event_content(msg)
|
535
|
-
msg.gsub NEW_LINE, ESC_NEW_LINE
|
536
|
-
end
|
537
|
-
|
538
|
-
def escape_tag_content(tag)
|
539
|
-
tag = remove_pipes(tag.to_s)
|
540
|
-
tag.delete! COMMA
|
541
|
-
tag
|
542
|
-
end
|
338
|
+
def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
|
339
|
+
telemetry.sent(metrics: 1)
|
543
340
|
|
544
|
-
def remove_pipes(msg)
|
545
|
-
msg.delete PIPE
|
546
|
-
end
|
547
|
-
|
548
|
-
def escape_service_check_message(msg)
|
549
|
-
escape_event_content(msg).gsub('m:'.freeze, 'm\:'.freeze)
|
550
|
-
end
|
551
|
-
|
552
|
-
def send_stats(stat, delta, type, opts=EMPTY_OPTIONS)
|
553
341
|
sample_rate = opts[:sample_rate] || @sample_rate || 1
|
554
|
-
|
555
|
-
|
556
|
-
full_stat
|
557
|
-
|
558
|
-
stat = stat.is_a?(String) ? stat.dup : stat.to_s
|
559
|
-
# Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
|
560
|
-
stat.gsub!(DOUBLE_COLON, DOT)
|
561
|
-
stat.tr!(':|@'.freeze, UNDERSCORE)
|
562
|
-
full_stat << stat
|
563
|
-
|
564
|
-
full_stat << ':'.freeze
|
565
|
-
full_stat << delta.to_s
|
566
|
-
full_stat << PIPE
|
567
|
-
full_stat << type
|
568
|
-
|
569
|
-
unless sample_rate == 1
|
570
|
-
full_stat << PIPE
|
571
|
-
full_stat << '@'.freeze
|
572
|
-
full_stat << sample_rate.to_s
|
573
|
-
end
|
574
|
-
|
575
|
-
if tags_string = tags_as_string(opts)
|
576
|
-
full_stat << PIPE
|
577
|
-
full_stat << '#'.freeze
|
578
|
-
full_stat << tags_string
|
579
|
-
end
|
342
|
+
|
343
|
+
if sample_rate == 1 || rand <= sample_rate
|
344
|
+
full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
|
580
345
|
|
581
346
|
send_stat(full_stat)
|
582
347
|
end
|
@@ -584,7 +349,7 @@ module Datadog
|
|
584
349
|
|
585
350
|
def send_stat(message)
|
586
351
|
if @batch.open?
|
587
|
-
@batch.add
|
352
|
+
@batch.add(message)
|
588
353
|
else
|
589
354
|
@connection.write(message)
|
590
355
|
end
|