dogstatsd-ruby 4.0.0 → 4.8.3

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