dogstatsd-ruby 4.4.0 → 4.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 25baf4b0dc57e8ed9a79bd2115224f22de9097200ccf4931b54fbb1e40905363
4
- data.tar.gz: a51c91dc366316b6edbadc64eb5e7383b6b100c9a2a859eb9bc31b9bef9ec7e2
3
+ metadata.gz: 9657b1d27b4367d96457d57faaed215c8ed07343d5d0803549b9078bd9713fbe
4
+ data.tar.gz: ad7c7fb2638790a3fe0300c23d81e87c33dc0fa9f7d496bc164e148e67e18992
5
5
  SHA512:
6
- metadata.gz: 225ef009dcb475534ec746d9eaa6e8b156ad7b50291c2894ba9171806bc91231150510a65a47ec754dbf519f80fe7381c589d34f35e983519fb8e991347e1a12
7
- data.tar.gz: e8609033506332625db4a6655adceacfb7fd1b85346020c1582dd032815ea56158d46442cecafd40fdd7bf098745e41ab1e70323b323ff63dd8bb43be3be8b88
6
+ metadata.gz: a6b5a0217129bdb2bb2e7da14711395775b2d8897f74ffd8bd9925dc6a5b8dd3503fe7ee8427e1840617197de251d5b46abe99284cf8df41928ad9b69399c814
7
+ data.tar.gz: c129849337907f82ddbeba16484371e18aa1f155d5fac46b1a62be31b178ceddcb3c47d04bf6ce59b1ea485770050918377dec4422d78f8b2c0d0e894794182f
data/README.md CHANGED
@@ -1,86 +1,40 @@
1
+ # dogstatsd-ruby
1
2
 
2
- dogstatsd-ruby
3
- ==============
3
+ A client for DogStatsD, an extension of the StatsD metric server for Datadog. Full API documentation is available in [DogStatsD-ruby rubydoc](https://www.rubydoc.info/github/DataDog/dogstatsd-ruby/master/Datadog/Statsd).
4
4
 
5
- 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.4.0".freeze
29
+ OK = 0
30
+ WARNING = 1
31
+ CRITICAL = 2
32
+ UNKNOWN = 3
33
+
34
+ DEFAULT_BUFFER_SIZE = 8 * 1_024
35
+ MAX_EVENT_SIZE = 8 * 1_024
36
+ # minimum flush interval for the telemetry in seconds
37
+ DEFAULT_TELEMETRY_FLUSH_INTERVAL = 10
38
+
39
+ COUNTER_TYPE = 'c'
40
+ GAUGE_TYPE = 'g'
41
+ HISTOGRAM_TYPE = 'h'
42
+ DISTRIBUTION_TYPE = 'd'
43
+ TIMING_TYPE = 'ms'
44
+ SET_TYPE = 's'
196
45
 
197
46
  # A namespace to prepend to all statsd calls. Defaults to no namespace.
198
47
  attr_reader :namespace
199
48
 
200
49
  # Global tags to be added to every statsd call. Defaults to no tags.
201
- 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.
@@ -413,8 +269,10 @@ module Datadog
413
269
  # @option opts [String, nil] :message (nil) A message to associate with this service check status
414
270
  # @example Report a critical service check status
415
271
  # $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
416
- def service_check(name, status, opts=EMPTY_OPTIONS)
417
- 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.
@@ -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,146 +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
- if key == :timestamp && opts[key].is_a?(Integer)
490
- value = opts[key]
491
- else
492
- value = remove_pipes(opts[key])
493
- end
494
- sc_string << "|#{shorthand_key}#{value}"
495
- end
496
- end
497
- sc_string
498
- end
499
-
500
- def format_event(title, text, opts=EMPTY_OPTIONS)
501
- escaped_title = escape_event_content(title)
502
- escaped_text = escape_event_content(text)
503
- event_string_data = "_e{#{escaped_title.bytesize},#{escaped_text.bytesize}}:#{escaped_title}|#{escaped_text}".dup
504
-
505
- # We construct the string to be sent by adding '|key:value' parts to it when needed
506
- # All pipes ('|') in the metadata are removed. Title and Text can keep theirs
507
- OPTS_KEYS.each do |key, shorthand_key|
508
- if key != :tags && opts[key]
509
- # :date_happened is the only key where the value is an Integer
510
- # To not break backwards compatibility, we still accept a String
511
- if key == :date_happened && opts[key].is_a?(Integer)
512
- value = opts[key]
513
- # All other keys only have String values
514
- else
515
- value = remove_pipes(opts[key])
516
- end
517
- event_string_data << "|#{shorthand_key}:#{value}"
518
- end
519
- end
520
-
521
- # Tags are joined and added as last part to the string to be sent
522
- if tags_string = tags_as_string(opts)
523
- event_string_data << "|##{tags_string}"
328
+ if PROCESS_TIME_SUPPORTED
329
+ def now
330
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
524
331
  end
525
-
526
- raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.bytesize > MAX_EVENT_SIZE
527
- event_string_data
528
- end
529
-
530
- def tags_as_string(opts)
531
- if tag_arr = opts[:tags]
532
- tag_arr = tag_hash_to_array(tag_arr) if tag_arr.is_a? Hash
533
- tag_arr = tag_arr.map { |tag| escape_tag_content(tag) }
534
- tag_arr = tags + tag_arr # @tags are normalized when set, so not need to normalize them again
535
- else
536
- tag_arr = tags
332
+ else
333
+ def now
334
+ Time.now.to_f
537
335
  end
538
- tag_arr.join(COMMA) unless tag_arr.empty?
539
336
  end
540
337
 
541
- def tag_hash_to_array(tag_hash)
542
- tag_hash.to_a.map {|pair| pair.compact.join(":")}
543
- end
544
-
545
- def escape_event_content(msg)
546
- msg.gsub NEW_LINE, ESC_NEW_LINE
547
- end
548
-
549
- def escape_tag_content(tag)
550
- tag = remove_pipes(tag.to_s)
551
- tag.delete! COMMA
552
- tag
553
- end
338
+ def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
339
+ telemetry.sent(metrics: 1)
554
340
 
555
- def remove_pipes(msg)
556
- msg.delete PIPE
557
- end
558
-
559
- def escape_service_check_message(msg)
560
- escape_event_content(msg).gsub('m:'.freeze, 'm\:'.freeze)
561
- end
562
-
563
- def send_stats(stat, delta, type, opts=EMPTY_OPTIONS)
564
341
  sample_rate = opts[:sample_rate] || @sample_rate || 1
565
- if sample_rate == 1 or rand <= sample_rate
566
- full_stat = ''.dup
567
- full_stat << @prefix if @prefix
568
-
569
- stat = stat.is_a?(String) ? stat.dup : stat.to_s
570
- # Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
571
- stat.gsub!(DOUBLE_COLON, DOT)
572
- stat.tr!(':|@'.freeze, UNDERSCORE)
573
- full_stat << stat
574
-
575
- full_stat << ':'.freeze
576
- full_stat << delta.to_s
577
- full_stat << PIPE
578
- full_stat << type
579
-
580
- unless sample_rate == 1
581
- full_stat << PIPE
582
- full_stat << '@'.freeze
583
- full_stat << sample_rate.to_s
584
- end
585
-
586
- if tags_string = tags_as_string(opts)
587
- full_stat << PIPE
588
- full_stat << '#'.freeze
589
- full_stat << tags_string
590
- end
342
+
343
+ if sample_rate == 1 || rand <= sample_rate
344
+ full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
591
345
 
592
346
  send_stat(full_stat)
593
347
  end
@@ -595,7 +349,7 @@ module Datadog
595
349
 
596
350
  def send_stat(message)
597
351
  if @batch.open?
598
- @batch.add message
352
+ @batch.add(message)
599
353
  else
600
354
  @connection.write(message)
601
355
  end