dogstatsd-ruby 4.4.0 → 4.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +36 -78
- data/lib/datadog/statsd.rb +105 -351
- 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 +67 -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 +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 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9657b1d27b4367d96457d57faaed215c8ed07343d5d0803549b9078bd9713fbe
|
4
|
+
data.tar.gz: ad7c7fb2638790a3fe0300c23d81e87c33dc0fa9f7d496bc164e148e67e18992
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6b5a0217129bdb2bb2e7da14711395775b2d8897f74ffd8bd9925dc6a5b8dd3503fe7ee8427e1840617197de251d5b46abe99284cf8df41928ad9b69399c814
|
7
|
+
data.tar.gz: c129849337907f82ddbeba16484371e18aa1f155d5fac46b1a62be31b178ceddcb3c47d04bf6ce59b1ea485770050918377dec4422d78f8b2c0d0e894794182f
|
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
|
+
[![Build Status](https://secure.travis-ci.org/DataDog/dogstatsd-ruby.svg)](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.4.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.
|
@@ -413,8 +269,10 @@ module Datadog
|
|
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.
|
@@ -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,146 +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
|
-
if key == :timestamp && opts[key].is_a?(Integer)
|
490
|
-
value = opts[key]
|
491
|
-
else
|
492
|
-
value = remove_pipes(opts[key])
|
493
|
-
end
|
494
|
-
sc_string << "|#{shorthand_key}#{value}"
|
495
|
-
end
|
496
|
-
end
|
497
|
-
sc_string
|
498
|
-
end
|
499
|
-
|
500
|
-
def format_event(title, text, opts=EMPTY_OPTIONS)
|
501
|
-
escaped_title = escape_event_content(title)
|
502
|
-
escaped_text = escape_event_content(text)
|
503
|
-
event_string_data = "_e{#{escaped_title.bytesize},#{escaped_text.bytesize}}:#{escaped_title}|#{escaped_text}".dup
|
504
|
-
|
505
|
-
# We construct the string to be sent by adding '|key:value' parts to it when needed
|
506
|
-
# All pipes ('|') in the metadata are removed. Title and Text can keep theirs
|
507
|
-
OPTS_KEYS.each do |key, shorthand_key|
|
508
|
-
if key != :tags && opts[key]
|
509
|
-
# :date_happened is the only key where the value is an Integer
|
510
|
-
# To not break backwards compatibility, we still accept a String
|
511
|
-
if key == :date_happened && opts[key].is_a?(Integer)
|
512
|
-
value = opts[key]
|
513
|
-
# All other keys only have String values
|
514
|
-
else
|
515
|
-
value = remove_pipes(opts[key])
|
516
|
-
end
|
517
|
-
event_string_data << "|#{shorthand_key}:#{value}"
|
518
|
-
end
|
519
|
-
end
|
520
|
-
|
521
|
-
# Tags are joined and added as last part to the string to be sent
|
522
|
-
if tags_string = tags_as_string(opts)
|
523
|
-
event_string_data << "|##{tags_string}"
|
328
|
+
if PROCESS_TIME_SUPPORTED
|
329
|
+
def now
|
330
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
524
331
|
end
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
end
|
529
|
-
|
530
|
-
def tags_as_string(opts)
|
531
|
-
if tag_arr = opts[:tags]
|
532
|
-
tag_arr = tag_hash_to_array(tag_arr) if tag_arr.is_a? Hash
|
533
|
-
tag_arr = tag_arr.map { |tag| escape_tag_content(tag) }
|
534
|
-
tag_arr = tags + tag_arr # @tags are normalized when set, so not need to normalize them again
|
535
|
-
else
|
536
|
-
tag_arr = tags
|
332
|
+
else
|
333
|
+
def now
|
334
|
+
Time.now.to_f
|
537
335
|
end
|
538
|
-
tag_arr.join(COMMA) unless tag_arr.empty?
|
539
336
|
end
|
540
337
|
|
541
|
-
def
|
542
|
-
|
543
|
-
end
|
544
|
-
|
545
|
-
def escape_event_content(msg)
|
546
|
-
msg.gsub NEW_LINE, ESC_NEW_LINE
|
547
|
-
end
|
548
|
-
|
549
|
-
def escape_tag_content(tag)
|
550
|
-
tag = remove_pipes(tag.to_s)
|
551
|
-
tag.delete! COMMA
|
552
|
-
tag
|
553
|
-
end
|
338
|
+
def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
|
339
|
+
telemetry.sent(metrics: 1)
|
554
340
|
|
555
|
-
def remove_pipes(msg)
|
556
|
-
msg.delete PIPE
|
557
|
-
end
|
558
|
-
|
559
|
-
def escape_service_check_message(msg)
|
560
|
-
escape_event_content(msg).gsub('m:'.freeze, 'm\:'.freeze)
|
561
|
-
end
|
562
|
-
|
563
|
-
def send_stats(stat, delta, type, opts=EMPTY_OPTIONS)
|
564
341
|
sample_rate = opts[:sample_rate] || @sample_rate || 1
|
565
|
-
|
566
|
-
|
567
|
-
full_stat
|
568
|
-
|
569
|
-
stat = stat.is_a?(String) ? stat.dup : stat.to_s
|
570
|
-
# Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
|
571
|
-
stat.gsub!(DOUBLE_COLON, DOT)
|
572
|
-
stat.tr!(':|@'.freeze, UNDERSCORE)
|
573
|
-
full_stat << stat
|
574
|
-
|
575
|
-
full_stat << ':'.freeze
|
576
|
-
full_stat << delta.to_s
|
577
|
-
full_stat << PIPE
|
578
|
-
full_stat << type
|
579
|
-
|
580
|
-
unless sample_rate == 1
|
581
|
-
full_stat << PIPE
|
582
|
-
full_stat << '@'.freeze
|
583
|
-
full_stat << sample_rate.to_s
|
584
|
-
end
|
585
|
-
|
586
|
-
if tags_string = tags_as_string(opts)
|
587
|
-
full_stat << PIPE
|
588
|
-
full_stat << '#'.freeze
|
589
|
-
full_stat << tags_string
|
590
|
-
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)
|
591
345
|
|
592
346
|
send_stat(full_stat)
|
593
347
|
end
|
@@ -595,7 +349,7 @@ module Datadog
|
|
595
349
|
|
596
350
|
def send_stat(message)
|
597
351
|
if @batch.open?
|
598
|
-
@batch.add
|
352
|
+
@batch.add(message)
|
599
353
|
else
|
600
354
|
@connection.write(message)
|
601
355
|
end
|