dogstatsd-ruby 4.3.0 → 4.8.0

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