dogstatsd-ruby 4.0.0 → 4.8.3
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 +56 -61
- data/lib/datadog/statsd.rb +121 -320
- 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 +71 -0
- data/lib/datadog/statsd/serialization/serializer.rb +41 -0
- data/lib/datadog/statsd/serialization/service_check_serializer.rb +60 -0
- data/lib/datadog/statsd/serialization/stat_serializer.rb +55 -0
- data/lib/datadog/statsd/serialization/tag_serializer.rb +92 -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 +25 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc2f6d365efc8cf25456fb9dbd5ad526abe0ec61f2b63e9b79346c767aa3a3b0
|
4
|
+
data.tar.gz: 84a029741390b63cf0540de60a872c6b0820bf6ad4c1619cea120d71bc38c3bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 769645a11d455fceb1b0c4d7d718005d869c62546cd1c255f0a45b9da51a300500dfe2c1a4e601248f1e8ef6ce1e40c15ebaca55d6e55092babef1c153ccdcf0
|
7
|
+
data.tar.gz: 1429d390a2c52758f957dd4766cf30db5039986527982f3188fcc44d7c058431d9f85180b1af7e5dae8f82db31325115b0ea4efb1fced82d7585924b0702fd0c
|
data/README.md
CHANGED
@@ -1,95 +1,90 @@
|
|
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)
|
27
|
+
```
|
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')
|
32
|
+
```
|
24
33
|
|
25
|
-
|
26
|
-
# class Statsd < Datadog::Statsd
|
27
|
-
# end
|
28
|
-
|
29
|
-
# Increment a counter.
|
30
|
-
statsd.increment('page.views')
|
31
|
-
|
32
|
-
# Record a gauge 50% of the time.
|
33
|
-
statsd.gauge('users.online', 123, :sample_rate=>0.5)
|
34
|
-
|
35
|
-
# Sample a histogram
|
36
|
-
statsd.histogram('file.upload.size', 1234)
|
37
|
-
|
38
|
-
# Time a block of code
|
39
|
-
statsd.time('page.render') do
|
40
|
-
render_page('home.html')
|
41
|
-
end
|
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).
|
42
35
|
|
43
|
-
|
44
|
-
# All metrics will be buffered and sent in one packet when the block completes
|
45
|
-
statsd.batch do |s|
|
46
|
-
s.increment('page.views')
|
47
|
-
s.gauge('users.online', 123)
|
48
|
-
end
|
36
|
+
### Origin detection over UDP
|
49
37
|
|
50
|
-
|
51
|
-
statsd.histogram('query.time', 10, :tags => ["version:1"])
|
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.
|
52
39
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
40
|
+
To enable origin detection over UDP, add the following lines to your application manifest
|
41
|
+
```yaml
|
42
|
+
env:
|
43
|
+
- name: DD_ENTITY_ID
|
44
|
+
valueFrom:
|
45
|
+
fieldRef:
|
46
|
+
fieldPath: metadata.uid
|
57
47
|
```
|
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.
|
58
49
|
|
59
|
-
|
50
|
+
## Usage
|
60
51
|
|
61
|
-
|
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).
|
62
53
|
|
63
|
-
|
64
|
-
# Post a simple message
|
65
|
-
statsd.event("There might be a storm tomorrow", "A friend warned me earlier.")
|
66
|
-
|
67
|
-
# Cry for help
|
68
|
-
statsd.event("SO MUCH SNOW", "Started yesterday and it won't stop !!", :alert_type => "error", :tags => ["urgent", "endoftheworld"])
|
69
|
-
```
|
54
|
+
### Metrics
|
70
55
|
|
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:
|
71
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)
|
72
63
|
|
73
|
-
|
74
|
-
-------------
|
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).
|
75
65
|
|
76
|
-
|
77
|
-
[here](http://www.rubydoc.info/github/DataDog/dogstatsd-ruby/master/frames).
|
66
|
+
### Events
|
78
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.
|
79
69
|
|
80
|
-
|
81
|
-
--------
|
70
|
+
### Service Checks
|
82
71
|
|
83
|
-
|
84
|
-
[here](http://github.com/DataDog/dogstatsd-ruby/issues/).
|
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.
|
85
73
|
|
74
|
+
### Maximum packets size in high-throughput scenarios
|
86
75
|
|
87
|
-
|
88
|
-
|
76
|
+
In order to have the most efficient use of this library in high-throughput scenarios,
|
77
|
+
default values for the maximum packets size have already been set for both UDS (8192 bytes)
|
78
|
+
and UDP (1432 bytes) in order to have the best usage of the underlying network.
|
79
|
+
However, if you perfectly know your network and you know that a different value for the maximum packets
|
80
|
+
size should be used, you can set it with the parameter `buffer_max_payload_size`. Example:
|
89
81
|
|
82
|
+
```ruby
|
83
|
+
# Create a DogStatsD client instance.
|
84
|
+
statsd = Datadog::Statsd.new('localhost', 8125, buffer_max_payload_size: 4096)
|
85
|
+
```
|
90
86
|
|
91
|
-
Credits
|
92
|
-
-------
|
87
|
+
## Credits
|
93
88
|
|
94
89
|
dogstatsd-ruby is forked from Rien Henrichs [original Statsd
|
95
90
|
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,174 +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
|
-
@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
|
150
|
-
|
151
|
-
# Create a dictionary to assign a key to every parameter's name, except for tags (treated differently)
|
152
|
-
# Goal: Simple and fast to add some other parameters
|
153
|
-
OPTS_KEYS = {
|
154
|
-
:date_happened => :d,
|
155
|
-
:hostname => :h,
|
156
|
-
:aggregation_key => :k,
|
157
|
-
:priority => :p,
|
158
|
-
:source_type_name => :s,
|
159
|
-
:alert_type => :t,
|
160
|
-
}
|
161
|
-
|
162
|
-
# Service check options
|
163
|
-
SC_OPT_KEYS = {
|
164
|
-
:timestamp => 'd:'.freeze,
|
165
|
-
:hostname => 'h:'.freeze,
|
166
|
-
:tags => '#'.freeze,
|
167
|
-
:message => 'm:'.freeze,
|
168
|
-
}
|
169
|
-
|
170
|
-
OK = 0
|
171
|
-
WARNING = 1
|
172
|
-
CRITICAL = 2
|
173
|
-
UNKNOWN = 3
|
174
|
-
|
175
|
-
MAX_EVENT_SIZE = 8 * 1024
|
176
|
-
|
177
|
-
COUNTER_TYPE = 'c'.freeze
|
178
|
-
GAUGE_TYPE = 'g'.freeze
|
179
|
-
HISTOGRAM_TYPE = 'h'.freeze
|
180
|
-
DISTRIBUTION_TYPE = 'd'.freeze
|
181
|
-
TIMING_TYPE = 'ms'.freeze
|
182
|
-
SET_TYPE = 's'.freeze
|
183
|
-
VERSION = "4.0.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'
|
184
45
|
|
185
46
|
# A namespace to prepend to all statsd calls. Defaults to no namespace.
|
186
47
|
attr_reader :namespace
|
187
48
|
|
188
49
|
# Global tags to be added to every statsd call. Defaults to no tags.
|
189
|
-
|
50
|
+
def tags
|
51
|
+
serializer.global_tags
|
52
|
+
end
|
190
53
|
|
191
54
|
# Buffer containing the statsd message before they are sent in batch
|
192
55
|
attr_reader :buffer
|
@@ -194,41 +57,68 @@ module Datadog
|
|
194
57
|
# Maximum buffer size in bytes before it is flushed
|
195
58
|
attr_reader :max_buffer_bytes
|
196
59
|
|
60
|
+
# Default sample rate
|
61
|
+
attr_reader :sample_rate
|
62
|
+
|
197
63
|
# Connection
|
198
64
|
attr_reader :connection
|
199
65
|
|
200
66
|
# @param [String] host your statsd host
|
201
67
|
# @param [Integer] port your statsd port
|
202
68
|
# @option [String] namespace set a namespace to be prepended to every metric name
|
203
|
-
# @option [Array<String
|
204
|
-
# @option [
|
69
|
+
# @option [Array<String>|Hash] tags tags to be added to every metric
|
70
|
+
# @option [Logger] logger for debugging
|
205
71
|
# @option [Integer] max_buffer_bytes max bytes to buffer when using #batch
|
206
72
|
# @option [String] socket_path unix socket path
|
73
|
+
# @option [Float] default sample rate if not overridden
|
207
74
|
def initialize(
|
208
75
|
host = nil,
|
209
76
|
port = nil,
|
210
77
|
namespace: nil,
|
211
78
|
tags: nil,
|
212
|
-
max_buffer_bytes:
|
79
|
+
max_buffer_bytes: DEFAULT_BUFFER_SIZE,
|
213
80
|
socket_path: nil,
|
214
|
-
logger: nil
|
81
|
+
logger: nil,
|
82
|
+
sample_rate: nil,
|
83
|
+
disable_telemetry: false,
|
84
|
+
telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL
|
215
85
|
)
|
216
|
-
|
217
|
-
|
86
|
+
unless tags.nil? || tags.is_a?(Array) || tags.is_a?(Hash)
|
87
|
+
raise ArgumentError, 'tags must be a Array<String> or a Hash'
|
88
|
+
end
|
218
89
|
|
219
90
|
@namespace = namespace
|
220
91
|
@prefix = @namespace ? "#{@namespace}.".freeze : nil
|
221
92
|
|
222
|
-
|
223
|
-
|
93
|
+
@serializer = Serialization::Serializer.new(prefix: @prefix, global_tags: tags)
|
94
|
+
|
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
|
+
)
|
224
101
|
|
225
|
-
@
|
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
|
108
|
+
|
109
|
+
@logger = logger
|
110
|
+
|
111
|
+
@sample_rate = sample_rate
|
112
|
+
|
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))
|
226
115
|
end
|
227
116
|
|
228
117
|
# yield a new instance to a block and close it when done
|
229
118
|
# for short-term use-cases that don't want to close the socket manually
|
230
119
|
def self.open(*args)
|
231
120
|
instance = new(*args)
|
121
|
+
|
232
122
|
yield instance
|
233
123
|
ensure
|
234
124
|
instance.close
|
@@ -242,10 +132,10 @@ module Datadog
|
|
242
132
|
# @option opts [Array<String>] :tags An array of tags
|
243
133
|
# @option opts [Numeric] :by increment value, default 1
|
244
134
|
# @see #count
|
245
|
-
def increment(stat, opts=EMPTY_OPTIONS)
|
246
|
-
opts = {:
|
135
|
+
def increment(stat, opts = EMPTY_OPTIONS)
|
136
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
247
137
|
incr_value = opts.fetch(:by, 1)
|
248
|
-
count
|
138
|
+
count(stat, incr_value, opts)
|
249
139
|
end
|
250
140
|
|
251
141
|
# Sends a decrement (count = -1) for the given stat to the statsd server.
|
@@ -256,10 +146,10 @@ module Datadog
|
|
256
146
|
# @option opts [Array<String>] :tags An array of tags
|
257
147
|
# @option opts [Numeric] :by decrement value, default 1
|
258
148
|
# @see #count
|
259
|
-
def decrement(stat, opts=EMPTY_OPTIONS)
|
260
|
-
opts = {:
|
149
|
+
def decrement(stat, opts = EMPTY_OPTIONS)
|
150
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
261
151
|
decr_value = - opts.fetch(:by, 1)
|
262
|
-
count
|
152
|
+
count(stat, decr_value, opts)
|
263
153
|
end
|
264
154
|
|
265
155
|
# Sends an arbitrary count for the given stat to the statsd server.
|
@@ -269,9 +159,9 @@ module Datadog
|
|
269
159
|
# @param [Hash] opts the options to create the metric with
|
270
160
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
271
161
|
# @option opts [Array<String>] :tags An array of tags
|
272
|
-
def count(stat, count, opts=EMPTY_OPTIONS)
|
273
|
-
opts = {:
|
274
|
-
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)
|
275
165
|
end
|
276
166
|
|
277
167
|
# Sends an arbitary gauge value for the given stat to the statsd server.
|
@@ -287,9 +177,9 @@ module Datadog
|
|
287
177
|
# @option opts [Array<String>] :tags An array of tags
|
288
178
|
# @example Report the current user count:
|
289
179
|
# $statsd.gauge('user.count', User.count)
|
290
|
-
def gauge(stat, value, opts=EMPTY_OPTIONS)
|
291
|
-
opts = {:
|
292
|
-
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)
|
293
183
|
end
|
294
184
|
|
295
185
|
# Sends a value to be tracked as a histogram to the statsd server.
|
@@ -301,14 +191,11 @@ module Datadog
|
|
301
191
|
# @option opts [Array<String>] :tags An array of tags
|
302
192
|
# @example Report the current user count:
|
303
193
|
# $statsd.histogram('user.count', User.count)
|
304
|
-
def histogram(stat, value, opts=EMPTY_OPTIONS)
|
305
|
-
send_stats
|
194
|
+
def histogram(stat, value, opts = EMPTY_OPTIONS)
|
195
|
+
send_stats(stat, value, HISTOGRAM_TYPE, opts)
|
306
196
|
end
|
307
197
|
|
308
198
|
# 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
199
|
#
|
313
200
|
# @param [String] stat stat name.
|
314
201
|
# @param [Numeric] value distribution value.
|
@@ -317,8 +204,8 @@ module Datadog
|
|
317
204
|
# @option opts [Array<String>] :tags An array of tags
|
318
205
|
# @example Report the current user count:
|
319
206
|
# $statsd.distribution('user.count', User.count)
|
320
|
-
def distribution(stat, value, opts=EMPTY_OPTIONS)
|
321
|
-
send_stats
|
207
|
+
def distribution(stat, value, opts = EMPTY_OPTIONS)
|
208
|
+
send_stats(stat, value, DISTRIBUTION_TYPE, opts)
|
322
209
|
end
|
323
210
|
|
324
211
|
# Sends a timing (in ms) for the given stat to the statsd server. The
|
@@ -331,9 +218,9 @@ module Datadog
|
|
331
218
|
# @param [Hash] opts the options to create the metric with
|
332
219
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
333
220
|
# @option opts [Array<String>] :tags An array of tags
|
334
|
-
def timing(stat, ms, opts=EMPTY_OPTIONS)
|
335
|
-
opts = {:
|
336
|
-
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)
|
337
224
|
end
|
338
225
|
|
339
226
|
# Reports execution time of the provided block using {#timing}.
|
@@ -349,13 +236,12 @@ module Datadog
|
|
349
236
|
# @see #timing
|
350
237
|
# @example Report the time (in ms) taken to activate an account
|
351
238
|
# $statsd.time('account.activate') { @account.activate! }
|
352
|
-
def time(stat, opts=EMPTY_OPTIONS)
|
353
|
-
opts = {:
|
354
|
-
start =
|
355
|
-
|
239
|
+
def time(stat, opts = EMPTY_OPTIONS)
|
240
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
241
|
+
start = now
|
242
|
+
yield
|
356
243
|
ensure
|
357
|
-
|
358
|
-
timing(stat, ((finished - start) * 1000).round, opts)
|
244
|
+
timing(stat, ((now - start) * 1000).round, opts)
|
359
245
|
end
|
360
246
|
|
361
247
|
# Sends a value to be tracked as a set to the statsd server.
|
@@ -367,9 +253,9 @@ module Datadog
|
|
367
253
|
# @option opts [Array<String>] :tags An array of tags
|
368
254
|
# @example Record a unique visitory by id:
|
369
255
|
# $statsd.set('visitors.uniques', User.id)
|
370
|
-
def set(stat, value, opts=EMPTY_OPTIONS)
|
371
|
-
opts = {:
|
372
|
-
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)
|
373
259
|
end
|
374
260
|
|
375
261
|
# This method allows you to send custom service check statuses.
|
@@ -377,14 +263,16 @@ module Datadog
|
|
377
263
|
# @param [String] name Service check name
|
378
264
|
# @param [String] status Service check status.
|
379
265
|
# @param [Hash] opts the additional data about the service check
|
380
|
-
# @option opts [Integer, nil] :timestamp (nil) Assign a timestamp to the
|
381
|
-
# @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.
|
382
268
|
# @option opts [Array<String>, nil] :tags (nil) An array of tags
|
383
269
|
# @option opts [String, nil] :message (nil) A message to associate with this service check status
|
384
270
|
# @example Report a critical service check status
|
385
271
|
# $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
|
386
|
-
def service_check(name, status, opts=EMPTY_OPTIONS)
|
387
|
-
|
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))
|
388
276
|
end
|
389
277
|
|
390
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.
|
@@ -394,19 +282,22 @@ module Datadog
|
|
394
282
|
# it will be grouped with other events that don't have an event type.
|
395
283
|
#
|
396
284
|
# @param [String] title Event title
|
397
|
-
# @param [String] text Event text. Supports
|
285
|
+
# @param [String] text Event text. Supports newlines (+\n+)
|
398
286
|
# @param [Hash] opts the additional data about the event
|
399
|
-
# @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
|
400
288
|
# @option opts [String, nil] :hostname (nil) Assign a hostname to the event.
|
401
289
|
# @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others
|
402
290
|
# @option opts [String, nil] :priority ('normal') Can be "normal" or "low"
|
403
291
|
# @option opts [String, nil] :source_type_name (nil) Assign a source type to the event
|
404
292
|
# @option opts [String, nil] :alert_type ('info') Can be "error", "warning", "info" or "success".
|
293
|
+
# @option opts [Boolean, false] :truncate_if_too_long (false) Truncate the event if it is too long
|
405
294
|
# @option opts [Array<String>] :tags tags to be added to every metric
|
406
295
|
# @example Report an awful event:
|
407
296
|
# $statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent'])
|
408
|
-
def event(title, text, opts=EMPTY_OPTIONS)
|
409
|
-
|
297
|
+
def event(title, text, opts = EMPTY_OPTIONS)
|
298
|
+
telemetry.sent(events: 1)
|
299
|
+
|
300
|
+
send_stat(serializer.to_event(title, text, opts))
|
410
301
|
end
|
411
302
|
|
412
303
|
# Send several metrics in the same UDP Packet
|
@@ -418,130 +309,40 @@ module Datadog
|
|
418
309
|
# s.increment('page.views')
|
419
310
|
# end
|
420
311
|
def batch
|
421
|
-
@batch.open
|
312
|
+
@batch.open do
|
313
|
+
yield self
|
314
|
+
end
|
422
315
|
end
|
423
316
|
|
424
317
|
# Close the underlying socket
|
425
318
|
def close
|
426
|
-
|
319
|
+
connection.close
|
427
320
|
end
|
428
321
|
|
429
322
|
private
|
323
|
+
attr_reader :serializer
|
324
|
+
attr_reader :telemetry
|
430
325
|
|
431
|
-
|
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")
|
326
|
+
PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= '2.1.0')
|
439
327
|
EMPTY_OPTIONS = {}.freeze
|
440
328
|
|
441
|
-
|
442
|
-
|
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
|
329
|
+
if PROCESS_TIME_SUPPORTED
|
330
|
+
def now
|
331
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
462
332
|
end
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
def format_event(title, text, opts=EMPTY_OPTIONS)
|
467
|
-
escaped_title = escape_event_content(title)
|
468
|
-
escaped_text = escape_event_content(text)
|
469
|
-
event_string_data = "_e{#{escaped_title.length},#{escaped_text.length}}:#{escaped_title}|#{escaped_text}".dup
|
470
|
-
|
471
|
-
# We construct the string to be sent by adding '|key:value' parts to it when needed
|
472
|
-
# All pipes ('|') in the metadata are removed. Title and Text can keep theirs
|
473
|
-
OPTS_KEYS.each do |key, shorthand_key|
|
474
|
-
if key != :tags && opts[key]
|
475
|
-
value = remove_pipes(opts[key])
|
476
|
-
event_string_data << "|#{shorthand_key}:#{value}"
|
477
|
-
end
|
478
|
-
end
|
479
|
-
|
480
|
-
# Tags are joined and added as last part to the string to be sent
|
481
|
-
if tags_string = tags_as_string(opts)
|
482
|
-
event_string_data << "|##{tags_string}"
|
333
|
+
else
|
334
|
+
def now
|
335
|
+
Time.now.to_f
|
483
336
|
end
|
484
|
-
|
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
|
487
337
|
end
|
488
338
|
|
489
|
-
def
|
490
|
-
|
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?
|
497
|
-
end
|
339
|
+
def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
|
340
|
+
telemetry.sent(metrics: 1)
|
498
341
|
|
499
|
-
|
500
|
-
msg.gsub NEW_LINE, ESC_NEW_LINE
|
501
|
-
end
|
502
|
-
|
503
|
-
def escape_tag_content(tag)
|
504
|
-
tag = remove_pipes(tag.to_s)
|
505
|
-
tag.delete! COMMA
|
506
|
-
tag
|
507
|
-
end
|
508
|
-
|
509
|
-
def remove_pipes(msg)
|
510
|
-
msg.delete PIPE
|
511
|
-
end
|
512
|
-
|
513
|
-
def escape_service_check_message(msg)
|
514
|
-
escape_event_content(msg).gsub('m:'.freeze, 'm\:'.freeze)
|
515
|
-
end
|
342
|
+
sample_rate = opts[:sample_rate] || @sample_rate || 1
|
516
343
|
|
517
|
-
|
518
|
-
|
519
|
-
if sample_rate == 1 or rand < sample_rate
|
520
|
-
full_stat = ''.dup
|
521
|
-
full_stat << @prefix if @prefix
|
522
|
-
|
523
|
-
stat = stat.is_a?(String) ? stat.dup : stat.to_s
|
524
|
-
# Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
|
525
|
-
stat.gsub!(DOUBLE_COLON, DOT)
|
526
|
-
stat.tr!(':|@'.freeze, UNDERSCORE)
|
527
|
-
full_stat << stat
|
528
|
-
|
529
|
-
full_stat << ':'.freeze
|
530
|
-
full_stat << delta.to_s
|
531
|
-
full_stat << PIPE
|
532
|
-
full_stat << type
|
533
|
-
|
534
|
-
unless sample_rate == 1
|
535
|
-
full_stat << PIPE
|
536
|
-
full_stat << '@'.freeze
|
537
|
-
full_stat << sample_rate.to_s
|
538
|
-
end
|
539
|
-
|
540
|
-
if tags_string = tags_as_string(opts)
|
541
|
-
full_stat << PIPE
|
542
|
-
full_stat << '#'.freeze
|
543
|
-
full_stat << tags_string
|
544
|
-
end
|
344
|
+
if sample_rate == 1 || rand <= sample_rate
|
345
|
+
full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
|
545
346
|
|
546
347
|
send_stat(full_stat)
|
547
348
|
end
|
@@ -549,7 +350,7 @@ module Datadog
|
|
549
350
|
|
550
351
|
def send_stat(message)
|
551
352
|
if @batch.open?
|
552
|
-
@batch.add
|
353
|
+
@batch.add(message)
|
553
354
|
else
|
554
355
|
@connection.write(message)
|
555
356
|
end
|