dogstatsd-ruby 4.0.0 → 4.7.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 +45 -63
- data/lib/datadog/statsd.rb +177 -238
- data/lib/datadog/statsd/batch.rb +56 -0
- data/lib/datadog/statsd/connection.rb +62 -0
- data/lib/datadog/statsd/telemetry.rb +65 -0
- data/lib/datadog/statsd/udp_connection.rb +37 -0
- data/lib/datadog/statsd/uds_connection.rb +35 -0
- metadata +14 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57d7660dad73ad8c0c5f37f6d6130474d255beaa467aca03f532e552d0b608e0
|
4
|
+
data.tar.gz: 30c8686d3045e4cfb2ca18c57390f78dbcccba1ff941deb170d89497953d5e84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99214bb827186d3b01568cc59064f7d2a8d043a4a739c7f90a2b013feeeead11ed363937c37d2c29846d101ed886c318a6369c2d5a57292b0c058db81c708813
|
7
|
+
data.tar.gz: c82e6c5e9668804688fd308e091398e05ada88dac8ea78a678ee2da671eaf297ffd95997efaf376506632f65e88b4096a6a9b2590667f772f19c6b020e238827
|
data/README.md
CHANGED
@@ -1,95 +1,77 @@
|
|
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
|
-
|
26
|
-
|
27
|
-
#
|
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
|
42
|
-
|
43
|
-
# Send several metrics at the same time
|
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
|
49
|
-
|
50
|
-
# Tag a metric.
|
51
|
-
statsd.histogram('query.time', 10, :tags => ["version:1"])
|
52
|
-
|
53
|
-
# Auto-close socket after end of block
|
54
|
-
Datadog::Statsd.open('localhost', 8125) do |s|
|
55
|
-
s.increment('page.views')
|
56
|
-
end
|
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')
|
57
32
|
```
|
58
33
|
|
59
|
-
|
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).
|
60
35
|
|
61
|
-
|
36
|
+
### Origin detection over UDP
|
62
37
|
|
63
|
-
|
64
|
-
# Post a simple message
|
65
|
-
statsd.event("There might be a storm tomorrow", "A friend warned me earlier.")
|
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.
|
66
39
|
|
67
|
-
|
68
|
-
|
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
|
69
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.
|
70
49
|
|
50
|
+
## Usage
|
71
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).
|
72
53
|
|
73
|
-
|
74
|
-
-------------
|
54
|
+
### Metrics
|
75
55
|
|
76
|
-
|
77
|
-
[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:
|
78
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)
|
79
63
|
|
80
|
-
|
81
|
-
--------
|
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).
|
82
65
|
|
83
|
-
|
84
|
-
[here](http://github.com/DataDog/dogstatsd-ruby/issues/).
|
66
|
+
### Events
|
85
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.
|
86
69
|
|
87
|
-
|
88
|
-
----------------------------
|
70
|
+
### Service Checks
|
89
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.
|
90
73
|
|
91
|
-
Credits
|
92
|
-
-------
|
74
|
+
## Credits
|
93
75
|
|
94
76
|
dogstatsd-ruby is forked from Rien Henrichs [original Statsd
|
95
77
|
client](https://github.com/reinh/statsd).
|
data/lib/datadog/statsd.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'socket'
|
3
3
|
|
4
|
+
require_relative 'statsd/telemetry'
|
5
|
+
require_relative 'statsd/udp_connection'
|
6
|
+
require_relative 'statsd/uds_connection'
|
7
|
+
require_relative 'statsd/batch'
|
8
|
+
|
4
9
|
# = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
|
5
10
|
#
|
6
11
|
# @example Set up a global Statsd client for a server on localhost:8125
|
@@ -19,168 +24,42 @@ require 'socket'
|
|
19
24
|
# statsd = Datadog::Statsd.new 'localhost', 8125, tags: 'tag1:true'
|
20
25
|
module Datadog
|
21
26
|
class Statsd
|
22
|
-
|
23
|
-
class Connection
|
24
|
-
DEFAULT_HOST = '127.0.0.1'
|
25
|
-
DEFAULT_PORT = 8125
|
26
|
-
|
27
|
-
# StatsD host. Defaults to 127.0.0.1.
|
28
|
-
attr_reader :host
|
29
|
-
|
30
|
-
# StatsD port. Defaults to 8125.
|
31
|
-
attr_reader :port
|
32
|
-
|
33
|
-
# DogStatsd unix socket path. Not used by default.
|
34
|
-
attr_reader :socket_path
|
35
|
-
|
36
|
-
def initialize(host, port, socket_path, logger)
|
37
|
-
@host = host || DEFAULT_HOST
|
38
|
-
@port = port || DEFAULT_PORT
|
39
|
-
@socket_path = socket_path
|
40
|
-
@logger = logger
|
41
|
-
end
|
42
|
-
|
43
|
-
def write(message)
|
44
|
-
@logger.debug { "Statsd: #{message}" } if @logger
|
45
|
-
if @socket_path.nil?
|
46
|
-
socket.send(message, 0)
|
47
|
-
else
|
48
|
-
socket.sendmsg_nonblock(message)
|
49
|
-
end
|
50
|
-
rescue StandardError => boom
|
51
|
-
# Give up on this socket if it looks like it is bad
|
52
|
-
bad_socket = !@socket_path.nil? && (
|
53
|
-
boom.is_a?(Errno::ECONNREFUSED) ||
|
54
|
-
boom.is_a?(Errno::ECONNRESET) ||
|
55
|
-
boom.is_a?(Errno::ENOENT)
|
56
|
-
)
|
57
|
-
if bad_socket
|
58
|
-
@socket = nil
|
59
|
-
return
|
60
|
-
end
|
61
|
-
|
62
|
-
# Try once to reconnect if the socket has been closed
|
63
|
-
retries ||= 1
|
64
|
-
if retries <= 1 && boom.is_a?(IOError) && boom.message =~ /closed stream/i
|
65
|
-
retries += 1
|
66
|
-
begin
|
67
|
-
@socket = connect
|
68
|
-
retry
|
69
|
-
rescue StandardError => e
|
70
|
-
boom = e
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
@logger.error { "Statsd: #{boom.class} #{boom}" } if @logger
|
75
|
-
nil
|
76
|
-
end
|
77
|
-
|
78
|
-
# Close the underlying socket
|
79
|
-
def close
|
80
|
-
@socket && @socket.close
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
|
85
|
-
def socket
|
86
|
-
@socket ||= connect
|
87
|
-
end
|
88
|
-
|
89
|
-
def connect
|
90
|
-
if @socket_path.nil?
|
91
|
-
socket = UDPSocket.new
|
92
|
-
socket.connect(@host, @port)
|
93
|
-
else
|
94
|
-
socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
|
95
|
-
socket.connect(Socket.pack_sockaddr_un(@socket_path))
|
96
|
-
end
|
97
|
-
socket
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
class Batch
|
102
|
-
def initialize(connection, max_buffer_bytes)
|
103
|
-
@connection = connection
|
104
|
-
@max_buffer_bytes = max_buffer_bytes
|
105
|
-
@depth = 0
|
106
|
-
reset
|
107
|
-
end
|
108
|
-
|
109
|
-
def open
|
110
|
-
@depth += 1
|
111
|
-
yield
|
112
|
-
ensure
|
113
|
-
@depth -= 1
|
114
|
-
flush if !open?
|
115
|
-
end
|
116
|
-
|
117
|
-
def open?
|
118
|
-
@depth > 0
|
119
|
-
end
|
120
|
-
|
121
|
-
def add(message)
|
122
|
-
message_bytes = message.bytesize
|
123
|
-
|
124
|
-
unless @buffer_bytes == 0
|
125
|
-
if @buffer_bytes + 1 + message_bytes >= @max_buffer_bytes
|
126
|
-
flush
|
127
|
-
else
|
128
|
-
@buffer << NEW_LINE
|
129
|
-
@buffer_bytes += 1
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
@buffer << message
|
134
|
-
@buffer_bytes += message_bytes
|
135
|
-
end
|
136
|
-
|
137
|
-
def flush
|
138
|
-
return if @buffer_bytes == 0
|
139
|
-
@connection.write @buffer
|
140
|
-
reset
|
141
|
-
end
|
142
|
-
|
143
|
-
private
|
144
|
-
|
145
|
-
def reset
|
146
|
-
@buffer = String.new
|
147
|
-
@buffer_bytes = 0
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
27
|
# Create a dictionary to assign a key to every parameter's name, except for tags (treated differently)
|
152
28
|
# Goal: Simple and fast to add some other parameters
|
153
29
|
OPTS_KEYS = {
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
:
|
159
|
-
|
160
|
-
}
|
30
|
+
date_happened: :d,
|
31
|
+
hostname: :h,
|
32
|
+
aggregation_key: :k,
|
33
|
+
priority: :p,
|
34
|
+
source_type_name: :s,
|
35
|
+
alert_type: :t,
|
36
|
+
}.freeze
|
161
37
|
|
162
38
|
# Service check options
|
163
39
|
SC_OPT_KEYS = {
|
164
|
-
:
|
165
|
-
:
|
166
|
-
:
|
167
|
-
:
|
168
|
-
}
|
169
|
-
|
170
|
-
OK
|
171
|
-
WARNING
|
172
|
-
CRITICAL
|
173
|
-
UNKNOWN
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
40
|
+
timestamp: 'd:',
|
41
|
+
hostname: 'h:',
|
42
|
+
tags: '#',
|
43
|
+
message: 'm:',
|
44
|
+
}.freeze
|
45
|
+
|
46
|
+
OK = 0
|
47
|
+
WARNING = 1
|
48
|
+
CRITICAL = 2
|
49
|
+
UNKNOWN = 3
|
50
|
+
|
51
|
+
DEFAULT_BUFFER_SIZE = 8 * 1_024
|
52
|
+
MAX_EVENT_SIZE = 8 * 1_024
|
53
|
+
# minimum flush interval for the telemetry in seconds
|
54
|
+
DEFAULT_TELEMETRY_FLUSH_INTERVAL = 10
|
55
|
+
|
56
|
+
COUNTER_TYPE = 'c'
|
57
|
+
GAUGE_TYPE = 'g'
|
58
|
+
HISTOGRAM_TYPE = 'h'
|
59
|
+
DISTRIBUTION_TYPE = 'd'
|
60
|
+
TIMING_TYPE = 'ms'
|
61
|
+
SET_TYPE = 's'
|
62
|
+
VERSION = '4.7.0'
|
184
63
|
|
185
64
|
# A namespace to prepend to all statsd calls. Defaults to no namespace.
|
186
65
|
attr_reader :namespace
|
@@ -194,41 +73,73 @@ module Datadog
|
|
194
73
|
# Maximum buffer size in bytes before it is flushed
|
195
74
|
attr_reader :max_buffer_bytes
|
196
75
|
|
76
|
+
# Default sample rate
|
77
|
+
attr_reader :sample_rate
|
78
|
+
|
197
79
|
# Connection
|
198
80
|
attr_reader :connection
|
199
81
|
|
200
82
|
# @param [String] host your statsd host
|
201
83
|
# @param [Integer] port your statsd port
|
202
84
|
# @option [String] namespace set a namespace to be prepended to every metric name
|
203
|
-
# @option [Array<String
|
204
|
-
# @option [
|
85
|
+
# @option [Array<String>|Hash] tags tags to be added to every metric
|
86
|
+
# @option [Logger] logger for debugging
|
205
87
|
# @option [Integer] max_buffer_bytes max bytes to buffer when using #batch
|
206
88
|
# @option [String] socket_path unix socket path
|
89
|
+
# @option [Float] default sample rate if not overridden
|
207
90
|
def initialize(
|
208
91
|
host = nil,
|
209
92
|
port = nil,
|
210
93
|
namespace: nil,
|
211
94
|
tags: nil,
|
212
|
-
max_buffer_bytes:
|
95
|
+
max_buffer_bytes: DEFAULT_BUFFER_SIZE,
|
213
96
|
socket_path: nil,
|
214
|
-
logger: nil
|
97
|
+
logger: nil,
|
98
|
+
sample_rate: nil,
|
99
|
+
disable_telemetry: false,
|
100
|
+
telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL
|
215
101
|
)
|
216
|
-
|
102
|
+
unless tags.nil? || tags.is_a?(Array) || tags.is_a?(Hash)
|
103
|
+
raise ArgumentError, 'tags must be a Array<String> or a Hash'
|
104
|
+
end
|
105
|
+
|
106
|
+
tags = tag_hash_to_array(tags) if tags.is_a?(Hash)
|
107
|
+
@tags = (tags || []).compact.map! do |tag|
|
108
|
+
escape_tag_content(tag)
|
109
|
+
end
|
110
|
+
|
111
|
+
# append the entity id to tags if DD_ENTITY_ID env var is not nil
|
112
|
+
unless ENV.fetch('DD_ENTITY_ID', nil).nil?
|
113
|
+
dd_entity = escape_tag_content(ENV.fetch('DD_ENTITY_ID', nil))
|
114
|
+
@tags << 'dd.internal.entity_id:' + dd_entity
|
115
|
+
end
|
116
|
+
|
117
|
+
# init telemetry
|
118
|
+
transport_type = socket_path.nil? ? 'udp': 'uds'
|
119
|
+
telemetry_tags = (["client:ruby", "client_version:#{VERSION}", "client_transport:#{transport_type}"] + @tags).join(COMMA).freeze
|
120
|
+
@telemetry = Telemetry.new(disable_telemetry, telemetry_tags, telemetry_flush_interval)
|
121
|
+
|
122
|
+
if socket_path.nil?
|
123
|
+
@connection = UDPConnection.new(host, port, logger, @telemetry)
|
124
|
+
else
|
125
|
+
@connection = UDSConnection.new(socket_path, logger, @telemetry)
|
126
|
+
end
|
217
127
|
@logger = logger
|
218
128
|
|
219
129
|
@namespace = namespace
|
220
130
|
@prefix = @namespace ? "#{@namespace}.".freeze : nil
|
221
131
|
|
222
|
-
|
223
|
-
@tags = (tags || []).compact.map! {|tag| escape_tag_content(tag)}
|
132
|
+
@sample_rate = sample_rate
|
224
133
|
|
225
|
-
|
134
|
+
# we reduce max_buffer_bytes by a the rough estimate of the telemetry payload
|
135
|
+
@batch = Batch.new(@connection, (max_buffer_bytes - @telemetry.estimate_max_size))
|
226
136
|
end
|
227
137
|
|
228
138
|
# yield a new instance to a block and close it when done
|
229
139
|
# for short-term use-cases that don't want to close the socket manually
|
230
140
|
def self.open(*args)
|
231
141
|
instance = new(*args)
|
142
|
+
|
232
143
|
yield instance
|
233
144
|
ensure
|
234
145
|
instance.close
|
@@ -242,10 +153,10 @@ module Datadog
|
|
242
153
|
# @option opts [Array<String>] :tags An array of tags
|
243
154
|
# @option opts [Numeric] :by increment value, default 1
|
244
155
|
# @see #count
|
245
|
-
def increment(stat, opts=EMPTY_OPTIONS)
|
246
|
-
opts = {:
|
156
|
+
def increment(stat, opts = EMPTY_OPTIONS)
|
157
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
247
158
|
incr_value = opts.fetch(:by, 1)
|
248
|
-
count
|
159
|
+
count(stat, incr_value, opts)
|
249
160
|
end
|
250
161
|
|
251
162
|
# Sends a decrement (count = -1) for the given stat to the statsd server.
|
@@ -256,10 +167,10 @@ module Datadog
|
|
256
167
|
# @option opts [Array<String>] :tags An array of tags
|
257
168
|
# @option opts [Numeric] :by decrement value, default 1
|
258
169
|
# @see #count
|
259
|
-
def decrement(stat, opts=EMPTY_OPTIONS)
|
260
|
-
opts = {:
|
170
|
+
def decrement(stat, opts = EMPTY_OPTIONS)
|
171
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
261
172
|
decr_value = - opts.fetch(:by, 1)
|
262
|
-
count
|
173
|
+
count(stat, decr_value, opts)
|
263
174
|
end
|
264
175
|
|
265
176
|
# Sends an arbitrary count for the given stat to the statsd server.
|
@@ -269,9 +180,9 @@ module Datadog
|
|
269
180
|
# @param [Hash] opts the options to create the metric with
|
270
181
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
271
182
|
# @option opts [Array<String>] :tags An array of tags
|
272
|
-
def count(stat, count, opts=EMPTY_OPTIONS)
|
273
|
-
opts = {:
|
274
|
-
send_stats
|
183
|
+
def count(stat, count, opts = EMPTY_OPTIONS)
|
184
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
185
|
+
send_stats(stat, count, COUNTER_TYPE, opts)
|
275
186
|
end
|
276
187
|
|
277
188
|
# Sends an arbitary gauge value for the given stat to the statsd server.
|
@@ -287,9 +198,9 @@ module Datadog
|
|
287
198
|
# @option opts [Array<String>] :tags An array of tags
|
288
199
|
# @example Report the current user count:
|
289
200
|
# $statsd.gauge('user.count', User.count)
|
290
|
-
def gauge(stat, value, opts=EMPTY_OPTIONS)
|
291
|
-
opts = {:
|
292
|
-
send_stats
|
201
|
+
def gauge(stat, value, opts = EMPTY_OPTIONS)
|
202
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
203
|
+
send_stats(stat, value, GAUGE_TYPE, opts)
|
293
204
|
end
|
294
205
|
|
295
206
|
# Sends a value to be tracked as a histogram to the statsd server.
|
@@ -301,14 +212,11 @@ module Datadog
|
|
301
212
|
# @option opts [Array<String>] :tags An array of tags
|
302
213
|
# @example Report the current user count:
|
303
214
|
# $statsd.histogram('user.count', User.count)
|
304
|
-
def histogram(stat, value, opts=EMPTY_OPTIONS)
|
305
|
-
send_stats
|
215
|
+
def histogram(stat, value, opts = EMPTY_OPTIONS)
|
216
|
+
send_stats(stat, value, HISTOGRAM_TYPE, opts)
|
306
217
|
end
|
307
218
|
|
308
219
|
# 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
220
|
#
|
313
221
|
# @param [String] stat stat name.
|
314
222
|
# @param [Numeric] value distribution value.
|
@@ -317,8 +225,8 @@ module Datadog
|
|
317
225
|
# @option opts [Array<String>] :tags An array of tags
|
318
226
|
# @example Report the current user count:
|
319
227
|
# $statsd.distribution('user.count', User.count)
|
320
|
-
def distribution(stat, value, opts=EMPTY_OPTIONS)
|
321
|
-
send_stats
|
228
|
+
def distribution(stat, value, opts = EMPTY_OPTIONS)
|
229
|
+
send_stats(stat, value, DISTRIBUTION_TYPE, opts)
|
322
230
|
end
|
323
231
|
|
324
232
|
# Sends a timing (in ms) for the given stat to the statsd server. The
|
@@ -331,9 +239,9 @@ module Datadog
|
|
331
239
|
# @param [Hash] opts the options to create the metric with
|
332
240
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
333
241
|
# @option opts [Array<String>] :tags An array of tags
|
334
|
-
def timing(stat, ms, opts=EMPTY_OPTIONS)
|
335
|
-
opts = {:
|
336
|
-
send_stats
|
242
|
+
def timing(stat, ms, opts = EMPTY_OPTIONS)
|
243
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
244
|
+
send_stats(stat, ms, TIMING_TYPE, opts)
|
337
245
|
end
|
338
246
|
|
339
247
|
# Reports execution time of the provided block using {#timing}.
|
@@ -349,12 +257,21 @@ module Datadog
|
|
349
257
|
# @see #timing
|
350
258
|
# @example Report the time (in ms) taken to activate an account
|
351
259
|
# $statsd.time('account.activate') { @account.activate! }
|
352
|
-
def time(stat, opts=EMPTY_OPTIONS)
|
353
|
-
opts = {:
|
354
|
-
start =
|
355
|
-
|
260
|
+
def time(stat, opts = EMPTY_OPTIONS)
|
261
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
262
|
+
start = if PROCESS_TIME_SUPPORTED
|
263
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) # uncovered
|
264
|
+
else
|
265
|
+
Time.now.to_f # uncovered
|
266
|
+
end
|
267
|
+
yield
|
356
268
|
ensure
|
357
|
-
finished =
|
269
|
+
finished = if PROCESS_TIME_SUPPORTED
|
270
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) # uncovered
|
271
|
+
else
|
272
|
+
Time.now.to_f # uncovered
|
273
|
+
end
|
274
|
+
|
358
275
|
timing(stat, ((finished - start) * 1000).round, opts)
|
359
276
|
end
|
360
277
|
|
@@ -367,9 +284,9 @@ module Datadog
|
|
367
284
|
# @option opts [Array<String>] :tags An array of tags
|
368
285
|
# @example Record a unique visitory by id:
|
369
286
|
# $statsd.set('visitors.uniques', User.id)
|
370
|
-
def set(stat, value, opts=EMPTY_OPTIONS)
|
371
|
-
opts = {:
|
372
|
-
send_stats
|
287
|
+
def set(stat, value, opts = EMPTY_OPTIONS)
|
288
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
289
|
+
send_stats(stat, value, SET_TYPE, opts)
|
373
290
|
end
|
374
291
|
|
375
292
|
# This method allows you to send custom service check statuses.
|
@@ -377,14 +294,15 @@ module Datadog
|
|
377
294
|
# @param [String] name Service check name
|
378
295
|
# @param [String] status Service check status.
|
379
296
|
# @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
|
297
|
+
# @option opts [Integer, String, nil] :timestamp (nil) Assign a timestamp to the service check. Default is now when none
|
298
|
+
# @option opts [String, nil] :hostname (nil) Assign a hostname to the service check.
|
382
299
|
# @option opts [Array<String>, nil] :tags (nil) An array of tags
|
383
300
|
# @option opts [String, nil] :message (nil) A message to associate with this service check status
|
384
301
|
# @example Report a critical service check status
|
385
302
|
# $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
|
386
|
-
def service_check(name, status, opts=EMPTY_OPTIONS)
|
387
|
-
|
303
|
+
def service_check(name, status, opts = EMPTY_OPTIONS)
|
304
|
+
@telemetry.service_checks += 1
|
305
|
+
send_stat(format_service_check(name, status, opts))
|
388
306
|
end
|
389
307
|
|
390
308
|
# 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,9 +312,9 @@ module Datadog
|
|
394
312
|
# it will be grouped with other events that don't have an event type.
|
395
313
|
#
|
396
314
|
# @param [String] title Event title
|
397
|
-
# @param [String] text Event text. Supports
|
315
|
+
# @param [String] text Event text. Supports newlines (+\n+)
|
398
316
|
# @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
|
317
|
+
# @option opts [Integer, String, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
|
400
318
|
# @option opts [String, nil] :hostname (nil) Assign a hostname to the event.
|
401
319
|
# @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others
|
402
320
|
# @option opts [String, nil] :priority ('normal') Can be "normal" or "low"
|
@@ -405,8 +323,9 @@ module Datadog
|
|
405
323
|
# @option opts [Array<String>] :tags tags to be added to every metric
|
406
324
|
# @example Report an awful event:
|
407
325
|
# $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
|
-
|
326
|
+
def event(title, text, opts = EMPTY_OPTIONS)
|
327
|
+
@telemetry.events += 1
|
328
|
+
send_stat(format_event(title, text, opts))
|
410
329
|
end
|
411
330
|
|
412
331
|
# Send several metrics in the same UDP Packet
|
@@ -418,7 +337,9 @@ module Datadog
|
|
418
337
|
# s.increment('page.views')
|
419
338
|
# end
|
420
339
|
def batch
|
421
|
-
@batch.open
|
340
|
+
@batch.open do
|
341
|
+
yield self
|
342
|
+
end
|
422
343
|
end
|
423
344
|
|
424
345
|
# Close the underlying socket
|
@@ -428,20 +349,20 @@ module Datadog
|
|
428
349
|
|
429
350
|
private
|
430
351
|
|
431
|
-
NEW_LINE = "\n"
|
432
|
-
ESC_NEW_LINE =
|
433
|
-
COMMA =
|
434
|
-
PIPE =
|
435
|
-
DOT =
|
436
|
-
DOUBLE_COLON =
|
437
|
-
UNDERSCORE =
|
438
|
-
PROCESS_TIME_SUPPORTED = (RUBY_VERSION >=
|
352
|
+
NEW_LINE = "\n"
|
353
|
+
ESC_NEW_LINE = '\n'
|
354
|
+
COMMA = ','
|
355
|
+
PIPE = '|'
|
356
|
+
DOT = '.'
|
357
|
+
DOUBLE_COLON = '::'
|
358
|
+
UNDERSCORE = '_'
|
359
|
+
PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= '2.1.0')
|
439
360
|
EMPTY_OPTIONS = {}.freeze
|
440
361
|
|
441
362
|
private_constant :NEW_LINE, :ESC_NEW_LINE, :COMMA, :PIPE, :DOT,
|
442
363
|
:DOUBLE_COLON, :UNDERSCORE, :EMPTY_OPTIONS
|
443
364
|
|
444
|
-
def format_service_check(name, status, opts=EMPTY_OPTIONS)
|
365
|
+
def format_service_check(name, status, opts = EMPTY_OPTIONS)
|
445
366
|
sc_string = "_sc|#{name}|#{status}".dup
|
446
367
|
|
447
368
|
SC_OPT_KEYS.each do |key, shorthand_key|
|
@@ -456,23 +377,34 @@ module Datadog
|
|
456
377
|
escaped_message = escape_service_check_message(message)
|
457
378
|
sc_string << "|m:#{escaped_message}"
|
458
379
|
else
|
459
|
-
|
380
|
+
if key == :timestamp && opts[key].is_a?(Integer)
|
381
|
+
value = opts[key]
|
382
|
+
else
|
383
|
+
value = remove_pipes(opts[key])
|
384
|
+
end
|
460
385
|
sc_string << "|#{shorthand_key}#{value}"
|
461
386
|
end
|
462
387
|
end
|
463
388
|
sc_string
|
464
389
|
end
|
465
390
|
|
466
|
-
def format_event(title, text, opts=EMPTY_OPTIONS)
|
391
|
+
def format_event(title, text, opts = EMPTY_OPTIONS)
|
467
392
|
escaped_title = escape_event_content(title)
|
468
393
|
escaped_text = escape_event_content(text)
|
469
|
-
event_string_data = "_e{#{escaped_title.
|
394
|
+
event_string_data = "_e{#{escaped_title.bytesize},#{escaped_text.bytesize}}:#{escaped_title}|#{escaped_text}".dup
|
470
395
|
|
471
396
|
# We construct the string to be sent by adding '|key:value' parts to it when needed
|
472
397
|
# All pipes ('|') in the metadata are removed. Title and Text can keep theirs
|
473
398
|
OPTS_KEYS.each do |key, shorthand_key|
|
474
399
|
if key != :tags && opts[key]
|
475
|
-
value
|
400
|
+
# :date_happened is the only key where the value is an Integer
|
401
|
+
# To not break backwards compatibility, we still accept a String
|
402
|
+
if key == :date_happened && opts[key].is_a?(Integer)
|
403
|
+
value = opts[key]
|
404
|
+
# All other keys only have String values
|
405
|
+
else
|
406
|
+
value = remove_pipes(opts[key])
|
407
|
+
end
|
476
408
|
event_string_data << "|#{shorthand_key}:#{value}"
|
477
409
|
end
|
478
410
|
end
|
@@ -482,13 +414,18 @@ module Datadog
|
|
482
414
|
event_string_data << "|##{tags_string}"
|
483
415
|
end
|
484
416
|
|
485
|
-
|
417
|
+
if event_string_data.bytesize > MAX_EVENT_SIZE
|
418
|
+
raise "Event #{title} payload is too big (more that 8KB), event discarded"
|
419
|
+
end
|
486
420
|
event_string_data
|
487
421
|
end
|
488
422
|
|
489
423
|
def tags_as_string(opts)
|
490
424
|
if tag_arr = opts[:tags]
|
491
|
-
tag_arr = tag_arr
|
425
|
+
tag_arr = tag_hash_to_array(tag_arr) if tag_arr.is_a?(Hash)
|
426
|
+
tag_arr = tag_arr.map do |tag|
|
427
|
+
escape_tag_content(tag)
|
428
|
+
end
|
492
429
|
tag_arr = tags + tag_arr # @tags are normalized when set, so not need to normalize them again
|
493
430
|
else
|
494
431
|
tag_arr = tags
|
@@ -496,63 +433,65 @@ module Datadog
|
|
496
433
|
tag_arr.join(COMMA) unless tag_arr.empty?
|
497
434
|
end
|
498
435
|
|
499
|
-
def
|
500
|
-
|
436
|
+
def tag_hash_to_array(tag_hash)
|
437
|
+
tag_hash.to_a.map do |pair|
|
438
|
+
pair.compact.join(':')
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
def escape_event_content(message)
|
443
|
+
message.gsub(NEW_LINE, ESC_NEW_LINE)
|
501
444
|
end
|
502
445
|
|
503
446
|
def escape_tag_content(tag)
|
504
447
|
tag = remove_pipes(tag.to_s)
|
505
|
-
tag.delete!
|
448
|
+
tag.delete!(COMMA)
|
506
449
|
tag
|
507
450
|
end
|
508
451
|
|
509
|
-
def remove_pipes(
|
510
|
-
|
452
|
+
def remove_pipes(message)
|
453
|
+
message.delete(PIPE)
|
511
454
|
end
|
512
455
|
|
513
|
-
def escape_service_check_message(
|
514
|
-
escape_event_content(
|
456
|
+
def escape_service_check_message(message)
|
457
|
+
escape_event_content(message).gsub('m:', 'm\:')
|
515
458
|
end
|
516
459
|
|
517
|
-
def send_stats(stat, delta, type, opts=EMPTY_OPTIONS)
|
518
|
-
|
519
|
-
|
460
|
+
def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
|
461
|
+
@telemetry.metrics += 1
|
462
|
+
sample_rate = opts[:sample_rate] || @sample_rate || 1
|
463
|
+
if sample_rate == 1 || rand <= sample_rate
|
520
464
|
full_stat = ''.dup
|
521
465
|
full_stat << @prefix if @prefix
|
522
466
|
|
523
467
|
stat = stat.is_a?(String) ? stat.dup : stat.to_s
|
524
468
|
# Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
|
525
469
|
stat.gsub!(DOUBLE_COLON, DOT)
|
526
|
-
stat.tr!(':|@'
|
470
|
+
stat.tr!(':|@', UNDERSCORE)
|
527
471
|
full_stat << stat
|
528
472
|
|
529
|
-
full_stat << ':'
|
473
|
+
full_stat << ':'
|
530
474
|
full_stat << delta.to_s
|
531
475
|
full_stat << PIPE
|
532
476
|
full_stat << type
|
533
477
|
|
534
478
|
unless sample_rate == 1
|
535
479
|
full_stat << PIPE
|
536
|
-
full_stat << '@'
|
480
|
+
full_stat << '@'
|
537
481
|
full_stat << sample_rate.to_s
|
538
482
|
end
|
539
483
|
|
540
484
|
if tags_string = tags_as_string(opts)
|
541
485
|
full_stat << PIPE
|
542
|
-
full_stat << '#'
|
486
|
+
full_stat << '#'
|
543
487
|
full_stat << tags_string
|
544
488
|
end
|
545
|
-
|
546
489
|
send_stat(full_stat)
|
547
490
|
end
|
548
491
|
end
|
549
492
|
|
550
493
|
def send_stat(message)
|
551
|
-
|
552
|
-
@batch.add message
|
553
|
-
else
|
554
|
-
@connection.write(message)
|
555
|
-
end
|
494
|
+
@batch.open? ? @batch.add(message) : @connection.write(message)
|
556
495
|
end
|
557
496
|
end
|
558
497
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
class Statsd
|
5
|
+
class Batch
|
6
|
+
def initialize(connection, max_buffer_bytes)
|
7
|
+
@connection = connection
|
8
|
+
@max_buffer_bytes = max_buffer_bytes
|
9
|
+
@depth = 0
|
10
|
+
reset
|
11
|
+
end
|
12
|
+
|
13
|
+
def open
|
14
|
+
@depth += 1
|
15
|
+
|
16
|
+
yield
|
17
|
+
ensure
|
18
|
+
@depth -= 1
|
19
|
+
flush if !open?
|
20
|
+
end
|
21
|
+
|
22
|
+
def open?
|
23
|
+
@depth > 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def add(message)
|
27
|
+
message_bytes = message.bytesize
|
28
|
+
|
29
|
+
unless @buffer_bytes == 0
|
30
|
+
if @buffer_bytes + 1 + message_bytes >= @max_buffer_bytes
|
31
|
+
flush
|
32
|
+
else
|
33
|
+
@buffer << NEW_LINE
|
34
|
+
@buffer_bytes += 1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
@buffer << message
|
39
|
+
@buffer_bytes += message_bytes
|
40
|
+
end
|
41
|
+
|
42
|
+
def flush
|
43
|
+
return if @buffer_bytes == 0
|
44
|
+
@connection.write(@buffer)
|
45
|
+
reset
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def reset
|
51
|
+
@buffer = String.new
|
52
|
+
@buffer_bytes = 0
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
class Statsd
|
5
|
+
class Connection
|
6
|
+
def initialize(telemetry)
|
7
|
+
@telemetry = telemetry
|
8
|
+
end
|
9
|
+
|
10
|
+
# Close the underlying socket
|
11
|
+
def close
|
12
|
+
@socket && @socket.close
|
13
|
+
end
|
14
|
+
|
15
|
+
def write(payload)
|
16
|
+
logger.debug { "Statsd: #{payload}" } if logger
|
17
|
+
flush_telemetry = @telemetry.flush?
|
18
|
+
if flush_telemetry
|
19
|
+
payload += @telemetry.flush()
|
20
|
+
end
|
21
|
+
|
22
|
+
send_message(payload)
|
23
|
+
|
24
|
+
if flush_telemetry
|
25
|
+
@telemetry.reset
|
26
|
+
end
|
27
|
+
|
28
|
+
telemetry.bytes_sent += payload.length
|
29
|
+
telemetry.packets_sent += 1
|
30
|
+
rescue StandardError => boom
|
31
|
+
# Try once to reconnect if the socket has been closed
|
32
|
+
retries ||= 1
|
33
|
+
if retries <= 1 &&
|
34
|
+
(boom.is_a?(Errno::ENOTCONN) or
|
35
|
+
boom.is_a?(Errno::ECONNREFUSED) or
|
36
|
+
boom.is_a?(IOError) && boom.message =~ /closed stream/i)
|
37
|
+
retries += 1
|
38
|
+
begin
|
39
|
+
@socket = connect
|
40
|
+
retry
|
41
|
+
rescue StandardError => e
|
42
|
+
boom = e
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
telemetry.bytes_dropped += payload.length
|
47
|
+
telemetry.packets_dropped += 1
|
48
|
+
logger.error { "Statsd: #{boom.class} #{boom}" } if logger
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
attr_reader :telemetry
|
55
|
+
attr_reader :logger
|
56
|
+
|
57
|
+
def socket
|
58
|
+
@socket ||= connect
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Datadog
|
5
|
+
class Statsd
|
6
|
+
class Telemetry
|
7
|
+
attr_accessor :metrics
|
8
|
+
attr_accessor :events
|
9
|
+
attr_accessor :service_checks
|
10
|
+
attr_accessor :bytes_sent
|
11
|
+
attr_accessor :bytes_dropped
|
12
|
+
attr_accessor :packets_sent
|
13
|
+
attr_accessor :packets_dropped
|
14
|
+
attr_reader :estimate_max_size
|
15
|
+
|
16
|
+
def initialize(disabled, tags, flush_interval)
|
17
|
+
@disabled = disabled
|
18
|
+
@tags = tags
|
19
|
+
@flush_interval = flush_interval
|
20
|
+
reset
|
21
|
+
|
22
|
+
# estimate_max_size is an estimation or the maximum size of the
|
23
|
+
# telemetry payload. Since we don't want our packet to go over
|
24
|
+
# 'max_buffer_bytes', we have to adjust with the size of the telemetry
|
25
|
+
# (and any tags used). The telemetry payload size will change depending
|
26
|
+
# on the actual value of metrics: metrics received, packet dropped,
|
27
|
+
# etc. This is why we add a 63bytes margin: 9 bytes for each of the 7
|
28
|
+
# telemetry metrics.
|
29
|
+
@estimate_max_size = @disabled ? 0 : flush().length + 9 * 7
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset
|
33
|
+
@metrics = 0
|
34
|
+
@events = 0
|
35
|
+
@service_checks = 0
|
36
|
+
@bytes_sent = 0
|
37
|
+
@bytes_dropped = 0
|
38
|
+
@packets_sent = 0
|
39
|
+
@packets_dropped = 0
|
40
|
+
@next_flush_time = Time.now.to_i + @flush_interval
|
41
|
+
end
|
42
|
+
|
43
|
+
def flush?
|
44
|
+
if @next_flush_time < Time.now.to_i
|
45
|
+
return true
|
46
|
+
end
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
|
50
|
+
def flush
|
51
|
+
return '' if @disabled
|
52
|
+
|
53
|
+
# using shorthand syntax to reduce the garbage collection
|
54
|
+
return %Q(
|
55
|
+
datadog.dogstatsd.client.metrics:#{@metrics}|#{COUNTER_TYPE}|##{@tags}
|
56
|
+
datadog.dogstatsd.client.events:#{@events}|#{COUNTER_TYPE}|##{@tags}
|
57
|
+
datadog.dogstatsd.client.service_checks:#{@service_checks}|#{COUNTER_TYPE}|##{@tags}
|
58
|
+
datadog.dogstatsd.client.bytes_sent:#{@bytes_sent}|#{COUNTER_TYPE}|##{@tags}
|
59
|
+
datadog.dogstatsd.client.bytes_dropped:#{@bytes_dropped}|#{COUNTER_TYPE}|##{@tags}
|
60
|
+
datadog.dogstatsd.client.packets_sent:#{@packets_sent}|#{COUNTER_TYPE}|##{@tags}
|
61
|
+
datadog.dogstatsd.client.packets_dropped:#{@packets_dropped}|#{COUNTER_TYPE}|##{@tags})
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'connection'
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
class Statsd
|
7
|
+
class UDPConnection < Connection
|
8
|
+
DEFAULT_HOST = '127.0.0.1'
|
9
|
+
DEFAULT_PORT = 8125
|
10
|
+
|
11
|
+
# StatsD host. Defaults to 127.0.0.1.
|
12
|
+
attr_reader :host
|
13
|
+
|
14
|
+
# StatsD port. Defaults to 8125.
|
15
|
+
attr_reader :port
|
16
|
+
|
17
|
+
def initialize(host, port, logger, telemetry)
|
18
|
+
super(telemetry)
|
19
|
+
@host = host || ENV.fetch('DD_AGENT_HOST', DEFAULT_HOST)
|
20
|
+
@port = port || ENV.fetch('DD_DOGSTATSD_PORT', DEFAULT_PORT)
|
21
|
+
@logger = logger
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def connect
|
27
|
+
UDPSocket.new.tap do |socket|
|
28
|
+
socket.connect(host, port)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def send_message(message)
|
33
|
+
socket.send(message, 0)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'connection'
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
class Statsd
|
7
|
+
class UDSConnection < Connection
|
8
|
+
class BadSocketError < StandardError; end
|
9
|
+
|
10
|
+
# DogStatsd unix socket path
|
11
|
+
attr_reader :socket_path
|
12
|
+
|
13
|
+
def initialize(socket_path, logger, telemetry)
|
14
|
+
super(telemetry)
|
15
|
+
@socket_path = socket_path
|
16
|
+
@logger = logger
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def connect
|
22
|
+
socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
|
23
|
+
socket.connect(Socket.pack_sockaddr_un(@socket_path))
|
24
|
+
socket
|
25
|
+
end
|
26
|
+
|
27
|
+
def send_message(message)
|
28
|
+
socket.sendmsg_nonblock(message)
|
29
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENOENT => e
|
30
|
+
@socket = nil
|
31
|
+
raise BadSocketError, "#{e.class}: #{e}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
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.
|
4
|
+
version: 4.7.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: 2020-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A Ruby DogStastd client
|
14
14
|
email: code@datadoghq.com
|
@@ -21,10 +21,19 @@ files:
|
|
21
21
|
- LICENSE.txt
|
22
22
|
- README.md
|
23
23
|
- lib/datadog/statsd.rb
|
24
|
-
|
24
|
+
- lib/datadog/statsd/batch.rb
|
25
|
+
- lib/datadog/statsd/connection.rb
|
26
|
+
- lib/datadog/statsd/telemetry.rb
|
27
|
+
- lib/datadog/statsd/udp_connection.rb
|
28
|
+
- lib/datadog/statsd/uds_connection.rb
|
29
|
+
homepage: https://github.com/DataDog/dogstatsd-ruby
|
25
30
|
licenses:
|
26
31
|
- MIT
|
27
|
-
metadata:
|
32
|
+
metadata:
|
33
|
+
bug_tracker_uri: https://github.com/DataDog/dogstatsd-ruby/issues
|
34
|
+
changelog_uri: https://github.com/DataDog/dogstatsd-ruby/blob/v4.7.0/CHANGELOG.md
|
35
|
+
documentation_uri: https://www.rubydoc.info/gems/dogstatsd-ruby/4.7.0
|
36
|
+
source_code_uri: https://github.com/DataDog/dogstatsd-ruby/tree/v4.7.0
|
28
37
|
post_install_message:
|
29
38
|
rdoc_options: []
|
30
39
|
require_paths:
|
@@ -41,7 +50,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
41
50
|
version: '0'
|
42
51
|
requirements: []
|
43
52
|
rubyforge_project:
|
44
|
-
rubygems_version: 2.7.
|
53
|
+
rubygems_version: 2.7.10
|
45
54
|
signing_key:
|
46
55
|
specification_version: 4
|
47
56
|
summary: A Ruby DogStatsd client
|