dogstatsd-ruby 4.2.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 -242
- 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 +15 -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,169 +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 || ENV.fetch('DD_AGENT_HOST', nil) || DEFAULT_HOST
|
38
|
-
@port = port || ENV.fetch('DD_DOGSTATSD_PORT', nil) || 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?(Errno::ENOTCONN) or
|
65
|
-
retries <= 1 && boom.is_a?(IOError) && boom.message =~ /closed stream/i
|
66
|
-
retries += 1
|
67
|
-
begin
|
68
|
-
@socket = connect
|
69
|
-
retry
|
70
|
-
rescue StandardError => e
|
71
|
-
boom = e
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
@logger.error { "Statsd: #{boom.class} #{boom}" } if @logger
|
76
|
-
nil
|
77
|
-
end
|
78
|
-
|
79
|
-
# Close the underlying socket
|
80
|
-
def close
|
81
|
-
@socket && @socket.close
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
def socket
|
87
|
-
@socket ||= connect
|
88
|
-
end
|
89
|
-
|
90
|
-
def connect
|
91
|
-
if @socket_path.nil?
|
92
|
-
socket = UDPSocket.new
|
93
|
-
socket.connect(@host, @port)
|
94
|
-
else
|
95
|
-
socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
|
96
|
-
socket.connect(Socket.pack_sockaddr_un(@socket_path))
|
97
|
-
end
|
98
|
-
socket
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
class Batch
|
103
|
-
def initialize(connection, max_buffer_bytes)
|
104
|
-
@connection = connection
|
105
|
-
@max_buffer_bytes = max_buffer_bytes
|
106
|
-
@depth = 0
|
107
|
-
reset
|
108
|
-
end
|
109
|
-
|
110
|
-
def open
|
111
|
-
@depth += 1
|
112
|
-
yield
|
113
|
-
ensure
|
114
|
-
@depth -= 1
|
115
|
-
flush if !open?
|
116
|
-
end
|
117
|
-
|
118
|
-
def open?
|
119
|
-
@depth > 0
|
120
|
-
end
|
121
|
-
|
122
|
-
def add(message)
|
123
|
-
message_bytes = message.bytesize
|
124
|
-
|
125
|
-
unless @buffer_bytes == 0
|
126
|
-
if @buffer_bytes + 1 + message_bytes >= @max_buffer_bytes
|
127
|
-
flush
|
128
|
-
else
|
129
|
-
@buffer << NEW_LINE
|
130
|
-
@buffer_bytes += 1
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
@buffer << message
|
135
|
-
@buffer_bytes += message_bytes
|
136
|
-
end
|
137
|
-
|
138
|
-
def flush
|
139
|
-
return if @buffer_bytes == 0
|
140
|
-
@connection.write @buffer
|
141
|
-
reset
|
142
|
-
end
|
143
|
-
|
144
|
-
private
|
145
|
-
|
146
|
-
def reset
|
147
|
-
@buffer = String.new
|
148
|
-
@buffer_bytes = 0
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
27
|
# Create a dictionary to assign a key to every parameter's name, except for tags (treated differently)
|
153
28
|
# Goal: Simple and fast to add some other parameters
|
154
29
|
OPTS_KEYS = {
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
:
|
160
|
-
|
161
|
-
}
|
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
|
162
37
|
|
163
38
|
# Service check options
|
164
39
|
SC_OPT_KEYS = {
|
165
|
-
:
|
166
|
-
:
|
167
|
-
:
|
168
|
-
:
|
169
|
-
}
|
170
|
-
|
171
|
-
OK
|
172
|
-
WARNING
|
173
|
-
CRITICAL
|
174
|
-
UNKNOWN
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
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'
|
185
63
|
|
186
64
|
# A namespace to prepend to all statsd calls. Defaults to no namespace.
|
187
65
|
attr_reader :namespace
|
@@ -195,44 +73,73 @@ module Datadog
|
|
195
73
|
# Maximum buffer size in bytes before it is flushed
|
196
74
|
attr_reader :max_buffer_bytes
|
197
75
|
|
76
|
+
# Default sample rate
|
77
|
+
attr_reader :sample_rate
|
78
|
+
|
198
79
|
# Connection
|
199
80
|
attr_reader :connection
|
200
81
|
|
201
82
|
# @param [String] host your statsd host
|
202
83
|
# @param [Integer] port your statsd port
|
203
84
|
# @option [String] namespace set a namespace to be prepended to every metric name
|
204
|
-
# @option [Array<String
|
205
|
-
# @option [
|
85
|
+
# @option [Array<String>|Hash] tags tags to be added to every metric
|
86
|
+
# @option [Logger] logger for debugging
|
206
87
|
# @option [Integer] max_buffer_bytes max bytes to buffer when using #batch
|
207
88
|
# @option [String] socket_path unix socket path
|
89
|
+
# @option [Float] default sample rate if not overridden
|
208
90
|
def initialize(
|
209
91
|
host = nil,
|
210
92
|
port = nil,
|
211
93
|
namespace: nil,
|
212
94
|
tags: nil,
|
213
|
-
max_buffer_bytes:
|
95
|
+
max_buffer_bytes: DEFAULT_BUFFER_SIZE,
|
214
96
|
socket_path: nil,
|
215
|
-
logger: nil
|
97
|
+
logger: nil,
|
98
|
+
sample_rate: nil,
|
99
|
+
disable_telemetry: false,
|
100
|
+
telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL
|
216
101
|
)
|
217
|
-
|
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
|
218
127
|
@logger = logger
|
219
128
|
|
220
129
|
@namespace = namespace
|
221
130
|
@prefix = @namespace ? "#{@namespace}.".freeze : nil
|
222
131
|
|
223
|
-
|
224
|
-
@tags = (tags || []).compact.map! {|tag| escape_tag_content(tag)}
|
225
|
-
|
226
|
-
# append the entity id to tags if DD_ENTITY_ID env var is not nil
|
227
|
-
@tags << 'dd.internal.entity_id:' + escape_tag_content(ENV.fetch('DD_ENTITY_ID', nil)) unless ENV.fetch('DD_ENTITY_ID', nil).nil?
|
132
|
+
@sample_rate = sample_rate
|
228
133
|
|
229
|
-
|
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))
|
230
136
|
end
|
231
137
|
|
232
138
|
# yield a new instance to a block and close it when done
|
233
139
|
# for short-term use-cases that don't want to close the socket manually
|
234
140
|
def self.open(*args)
|
235
141
|
instance = new(*args)
|
142
|
+
|
236
143
|
yield instance
|
237
144
|
ensure
|
238
145
|
instance.close
|
@@ -246,10 +153,10 @@ module Datadog
|
|
246
153
|
# @option opts [Array<String>] :tags An array of tags
|
247
154
|
# @option opts [Numeric] :by increment value, default 1
|
248
155
|
# @see #count
|
249
|
-
def increment(stat, opts=EMPTY_OPTIONS)
|
250
|
-
opts = {:
|
156
|
+
def increment(stat, opts = EMPTY_OPTIONS)
|
157
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
251
158
|
incr_value = opts.fetch(:by, 1)
|
252
|
-
count
|
159
|
+
count(stat, incr_value, opts)
|
253
160
|
end
|
254
161
|
|
255
162
|
# Sends a decrement (count = -1) for the given stat to the statsd server.
|
@@ -260,10 +167,10 @@ module Datadog
|
|
260
167
|
# @option opts [Array<String>] :tags An array of tags
|
261
168
|
# @option opts [Numeric] :by decrement value, default 1
|
262
169
|
# @see #count
|
263
|
-
def decrement(stat, opts=EMPTY_OPTIONS)
|
264
|
-
opts = {:
|
170
|
+
def decrement(stat, opts = EMPTY_OPTIONS)
|
171
|
+
opts = { sample_rate: opts } if opts.is_a?(Numeric)
|
265
172
|
decr_value = - opts.fetch(:by, 1)
|
266
|
-
count
|
173
|
+
count(stat, decr_value, opts)
|
267
174
|
end
|
268
175
|
|
269
176
|
# Sends an arbitrary count for the given stat to the statsd server.
|
@@ -273,9 +180,9 @@ module Datadog
|
|
273
180
|
# @param [Hash] opts the options to create the metric with
|
274
181
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
275
182
|
# @option opts [Array<String>] :tags An array of tags
|
276
|
-
def count(stat, count, opts=EMPTY_OPTIONS)
|
277
|
-
opts = {:
|
278
|
-
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)
|
279
186
|
end
|
280
187
|
|
281
188
|
# Sends an arbitary gauge value for the given stat to the statsd server.
|
@@ -291,9 +198,9 @@ module Datadog
|
|
291
198
|
# @option opts [Array<String>] :tags An array of tags
|
292
199
|
# @example Report the current user count:
|
293
200
|
# $statsd.gauge('user.count', User.count)
|
294
|
-
def gauge(stat, value, opts=EMPTY_OPTIONS)
|
295
|
-
opts = {:
|
296
|
-
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)
|
297
204
|
end
|
298
205
|
|
299
206
|
# Sends a value to be tracked as a histogram to the statsd server.
|
@@ -305,14 +212,11 @@ module Datadog
|
|
305
212
|
# @option opts [Array<String>] :tags An array of tags
|
306
213
|
# @example Report the current user count:
|
307
214
|
# $statsd.histogram('user.count', User.count)
|
308
|
-
def histogram(stat, value, opts=EMPTY_OPTIONS)
|
309
|
-
send_stats
|
215
|
+
def histogram(stat, value, opts = EMPTY_OPTIONS)
|
216
|
+
send_stats(stat, value, HISTOGRAM_TYPE, opts)
|
310
217
|
end
|
311
218
|
|
312
219
|
# Sends a value to be tracked as a distribution to the statsd server.
|
313
|
-
# Note: Distributions are a beta feature of Datadog and not generally
|
314
|
-
# available. Distributions must be specifically enabled for your
|
315
|
-
# organization.
|
316
220
|
#
|
317
221
|
# @param [String] stat stat name.
|
318
222
|
# @param [Numeric] value distribution value.
|
@@ -321,8 +225,8 @@ module Datadog
|
|
321
225
|
# @option opts [Array<String>] :tags An array of tags
|
322
226
|
# @example Report the current user count:
|
323
227
|
# $statsd.distribution('user.count', User.count)
|
324
|
-
def distribution(stat, value, opts=EMPTY_OPTIONS)
|
325
|
-
send_stats
|
228
|
+
def distribution(stat, value, opts = EMPTY_OPTIONS)
|
229
|
+
send_stats(stat, value, DISTRIBUTION_TYPE, opts)
|
326
230
|
end
|
327
231
|
|
328
232
|
# Sends a timing (in ms) for the given stat to the statsd server. The
|
@@ -335,9 +239,9 @@ module Datadog
|
|
335
239
|
# @param [Hash] opts the options to create the metric with
|
336
240
|
# @option opts [Numeric] :sample_rate sample rate, 1 for always
|
337
241
|
# @option opts [Array<String>] :tags An array of tags
|
338
|
-
def timing(stat, ms, opts=EMPTY_OPTIONS)
|
339
|
-
opts = {:
|
340
|
-
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)
|
341
245
|
end
|
342
246
|
|
343
247
|
# Reports execution time of the provided block using {#timing}.
|
@@ -353,12 +257,21 @@ module Datadog
|
|
353
257
|
# @see #timing
|
354
258
|
# @example Report the time (in ms) taken to activate an account
|
355
259
|
# $statsd.time('account.activate') { @account.activate! }
|
356
|
-
def time(stat, opts=EMPTY_OPTIONS)
|
357
|
-
opts = {:
|
358
|
-
start =
|
359
|
-
|
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
|
360
268
|
ensure
|
361
|
-
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
|
+
|
362
275
|
timing(stat, ((finished - start) * 1000).round, opts)
|
363
276
|
end
|
364
277
|
|
@@ -371,9 +284,9 @@ module Datadog
|
|
371
284
|
# @option opts [Array<String>] :tags An array of tags
|
372
285
|
# @example Record a unique visitory by id:
|
373
286
|
# $statsd.set('visitors.uniques', User.id)
|
374
|
-
def set(stat, value, opts=EMPTY_OPTIONS)
|
375
|
-
opts = {:
|
376
|
-
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)
|
377
290
|
end
|
378
291
|
|
379
292
|
# This method allows you to send custom service check statuses.
|
@@ -381,14 +294,15 @@ module Datadog
|
|
381
294
|
# @param [String] name Service check name
|
382
295
|
# @param [String] status Service check status.
|
383
296
|
# @param [Hash] opts the additional data about the service check
|
384
|
-
# @option opts [Integer, nil] :timestamp (nil) Assign a timestamp to the
|
385
|
-
# @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.
|
386
299
|
# @option opts [Array<String>, nil] :tags (nil) An array of tags
|
387
300
|
# @option opts [String, nil] :message (nil) A message to associate with this service check status
|
388
301
|
# @example Report a critical service check status
|
389
302
|
# $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
|
390
|
-
def service_check(name, status, opts=EMPTY_OPTIONS)
|
391
|
-
|
303
|
+
def service_check(name, status, opts = EMPTY_OPTIONS)
|
304
|
+
@telemetry.service_checks += 1
|
305
|
+
send_stat(format_service_check(name, status, opts))
|
392
306
|
end
|
393
307
|
|
394
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.
|
@@ -398,9 +312,9 @@ module Datadog
|
|
398
312
|
# it will be grouped with other events that don't have an event type.
|
399
313
|
#
|
400
314
|
# @param [String] title Event title
|
401
|
-
# @param [String] text Event text. Supports
|
315
|
+
# @param [String] text Event text. Supports newlines (+\n+)
|
402
316
|
# @param [Hash] opts the additional data about the event
|
403
|
-
# @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
|
404
318
|
# @option opts [String, nil] :hostname (nil) Assign a hostname to the event.
|
405
319
|
# @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others
|
406
320
|
# @option opts [String, nil] :priority ('normal') Can be "normal" or "low"
|
@@ -409,8 +323,9 @@ module Datadog
|
|
409
323
|
# @option opts [Array<String>] :tags tags to be added to every metric
|
410
324
|
# @example Report an awful event:
|
411
325
|
# $statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent'])
|
412
|
-
def event(title, text, opts=EMPTY_OPTIONS)
|
413
|
-
|
326
|
+
def event(title, text, opts = EMPTY_OPTIONS)
|
327
|
+
@telemetry.events += 1
|
328
|
+
send_stat(format_event(title, text, opts))
|
414
329
|
end
|
415
330
|
|
416
331
|
# Send several metrics in the same UDP Packet
|
@@ -422,7 +337,9 @@ module Datadog
|
|
422
337
|
# s.increment('page.views')
|
423
338
|
# end
|
424
339
|
def batch
|
425
|
-
@batch.open
|
340
|
+
@batch.open do
|
341
|
+
yield self
|
342
|
+
end
|
426
343
|
end
|
427
344
|
|
428
345
|
# Close the underlying socket
|
@@ -432,20 +349,20 @@ module Datadog
|
|
432
349
|
|
433
350
|
private
|
434
351
|
|
435
|
-
NEW_LINE = "\n"
|
436
|
-
ESC_NEW_LINE =
|
437
|
-
COMMA =
|
438
|
-
PIPE =
|
439
|
-
DOT =
|
440
|
-
DOUBLE_COLON =
|
441
|
-
UNDERSCORE =
|
442
|
-
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')
|
443
360
|
EMPTY_OPTIONS = {}.freeze
|
444
361
|
|
445
362
|
private_constant :NEW_LINE, :ESC_NEW_LINE, :COMMA, :PIPE, :DOT,
|
446
363
|
:DOUBLE_COLON, :UNDERSCORE, :EMPTY_OPTIONS
|
447
364
|
|
448
|
-
def format_service_check(name, status, opts=EMPTY_OPTIONS)
|
365
|
+
def format_service_check(name, status, opts = EMPTY_OPTIONS)
|
449
366
|
sc_string = "_sc|#{name}|#{status}".dup
|
450
367
|
|
451
368
|
SC_OPT_KEYS.each do |key, shorthand_key|
|
@@ -460,23 +377,34 @@ module Datadog
|
|
460
377
|
escaped_message = escape_service_check_message(message)
|
461
378
|
sc_string << "|m:#{escaped_message}"
|
462
379
|
else
|
463
|
-
|
380
|
+
if key == :timestamp && opts[key].is_a?(Integer)
|
381
|
+
value = opts[key]
|
382
|
+
else
|
383
|
+
value = remove_pipes(opts[key])
|
384
|
+
end
|
464
385
|
sc_string << "|#{shorthand_key}#{value}"
|
465
386
|
end
|
466
387
|
end
|
467
388
|
sc_string
|
468
389
|
end
|
469
390
|
|
470
|
-
def format_event(title, text, opts=EMPTY_OPTIONS)
|
391
|
+
def format_event(title, text, opts = EMPTY_OPTIONS)
|
471
392
|
escaped_title = escape_event_content(title)
|
472
393
|
escaped_text = escape_event_content(text)
|
473
|
-
event_string_data = "_e{#{escaped_title.
|
394
|
+
event_string_data = "_e{#{escaped_title.bytesize},#{escaped_text.bytesize}}:#{escaped_title}|#{escaped_text}".dup
|
474
395
|
|
475
396
|
# We construct the string to be sent by adding '|key:value' parts to it when needed
|
476
397
|
# All pipes ('|') in the metadata are removed. Title and Text can keep theirs
|
477
398
|
OPTS_KEYS.each do |key, shorthand_key|
|
478
399
|
if key != :tags && opts[key]
|
479
|
-
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
|
480
408
|
event_string_data << "|#{shorthand_key}:#{value}"
|
481
409
|
end
|
482
410
|
end
|
@@ -486,13 +414,18 @@ module Datadog
|
|
486
414
|
event_string_data << "|##{tags_string}"
|
487
415
|
end
|
488
416
|
|
489
|
-
|
417
|
+
if event_string_data.bytesize > MAX_EVENT_SIZE
|
418
|
+
raise "Event #{title} payload is too big (more that 8KB), event discarded"
|
419
|
+
end
|
490
420
|
event_string_data
|
491
421
|
end
|
492
422
|
|
493
423
|
def tags_as_string(opts)
|
494
424
|
if tag_arr = opts[:tags]
|
495
|
-
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
|
496
429
|
tag_arr = tags + tag_arr # @tags are normalized when set, so not need to normalize them again
|
497
430
|
else
|
498
431
|
tag_arr = tags
|
@@ -500,63 +433,65 @@ module Datadog
|
|
500
433
|
tag_arr.join(COMMA) unless tag_arr.empty?
|
501
434
|
end
|
502
435
|
|
503
|
-
def
|
504
|
-
|
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)
|
505
444
|
end
|
506
445
|
|
507
446
|
def escape_tag_content(tag)
|
508
447
|
tag = remove_pipes(tag.to_s)
|
509
|
-
tag.delete!
|
448
|
+
tag.delete!(COMMA)
|
510
449
|
tag
|
511
450
|
end
|
512
451
|
|
513
|
-
def remove_pipes(
|
514
|
-
|
452
|
+
def remove_pipes(message)
|
453
|
+
message.delete(PIPE)
|
515
454
|
end
|
516
455
|
|
517
|
-
def escape_service_check_message(
|
518
|
-
escape_event_content(
|
456
|
+
def escape_service_check_message(message)
|
457
|
+
escape_event_content(message).gsub('m:', 'm\:')
|
519
458
|
end
|
520
459
|
|
521
|
-
def send_stats(stat, delta, type, opts=EMPTY_OPTIONS)
|
522
|
-
|
523
|
-
|
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
|
524
464
|
full_stat = ''.dup
|
525
465
|
full_stat << @prefix if @prefix
|
526
466
|
|
527
467
|
stat = stat.is_a?(String) ? stat.dup : stat.to_s
|
528
468
|
# Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
|
529
469
|
stat.gsub!(DOUBLE_COLON, DOT)
|
530
|
-
stat.tr!(':|@'
|
470
|
+
stat.tr!(':|@', UNDERSCORE)
|
531
471
|
full_stat << stat
|
532
472
|
|
533
|
-
full_stat << ':'
|
473
|
+
full_stat << ':'
|
534
474
|
full_stat << delta.to_s
|
535
475
|
full_stat << PIPE
|
536
476
|
full_stat << type
|
537
477
|
|
538
478
|
unless sample_rate == 1
|
539
479
|
full_stat << PIPE
|
540
|
-
full_stat << '@'
|
480
|
+
full_stat << '@'
|
541
481
|
full_stat << sample_rate.to_s
|
542
482
|
end
|
543
483
|
|
544
484
|
if tags_string = tags_as_string(opts)
|
545
485
|
full_stat << PIPE
|
546
|
-
full_stat << '#'
|
486
|
+
full_stat << '#'
|
547
487
|
full_stat << tags_string
|
548
488
|
end
|
549
|
-
|
550
489
|
send_stat(full_stat)
|
551
490
|
end
|
552
491
|
end
|
553
492
|
|
554
493
|
def send_stat(message)
|
555
|
-
|
556
|
-
@batch.add message
|
557
|
-
else
|
558
|
-
@connection.write(message)
|
559
|
-
end
|
494
|
+
@batch.open? ? @batch.add(message) : @connection.write(message)
|
560
495
|
end
|
561
496
|
end
|
562
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:
|
@@ -40,7 +49,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
49
|
- !ruby/object:Gem::Version
|
41
50
|
version: '0'
|
42
51
|
requirements: []
|
43
|
-
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 2.7.10
|
44
54
|
signing_key:
|
45
55
|
specification_version: 4
|
46
56
|
summary: A Ruby DogStatsd client
|