dogstatsd-ruby 4.0.0 → 5.3.3

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.
@@ -1,6 +1,21 @@
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/connection_cfg'
9
+ require_relative 'statsd/message_buffer'
10
+ require_relative 'statsd/serialization'
11
+ require_relative 'statsd/sender'
12
+ require_relative 'statsd/single_thread_sender'
13
+ require_relative 'statsd/forwarder'
14
+ require_relative 'statsd/timer'
15
+
16
+ $deprecation_message_mutex = Mutex.new
17
+ $deprecation_message_done = false
18
+
4
19
  # = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
5
20
  #
6
21
  # @example Set up a global Statsd client for a server on localhost:8125
@@ -19,216 +34,129 @@ require 'socket'
19
34
  # statsd = Datadog::Statsd.new 'localhost', 8125, tags: 'tag1:true'
20
35
  module Datadog
21
36
  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
37
+ class Error < StandardError
99
38
  end
100
39
 
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
40
+ OK = 0
41
+ WARNING = 1
42
+ CRITICAL = 2
43
+ UNKNOWN = 3
108
44
 
109
- def open
110
- @depth += 1
111
- yield
112
- ensure
113
- @depth -= 1
114
- flush if !open?
115
- end
45
+ UDP_DEFAULT_BUFFER_SIZE = 1_432
46
+ UDS_DEFAULT_BUFFER_SIZE = 8_192
47
+ DEFAULT_BUFFER_POOL_SIZE = Float::INFINITY
116
48
 
117
- def open?
118
- @depth > 0
119
- end
49
+ UDP_DEFAULT_SENDER_QUEUE_SIZE = 2048
50
+ UDS_DEFAULT_SENDER_QUEUE_SIZE = 512
120
51
 
121
- def add(message)
122
- message_bytes = message.bytesize
52
+ MAX_EVENT_SIZE = 8 * 1_024
123
53
 
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
54
+ # minimum flush interval for the telemetry in seconds
55
+ DEFAULT_TELEMETRY_FLUSH_INTERVAL = 10
132
56
 
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
57
+ COUNTER_TYPE = 'c'
58
+ GAUGE_TYPE = 'g'
59
+ HISTOGRAM_TYPE = 'h'
60
+ DISTRIBUTION_TYPE = 'd'
61
+ TIMING_TYPE = 'ms'
62
+ SET_TYPE = 's'
184
63
 
185
64
  # A namespace to prepend to all statsd calls. Defaults to no namespace.
186
65
  attr_reader :namespace
187
66
 
188
67
  # Global tags to be added to every statsd call. Defaults to no tags.
189
- attr_reader :tags
190
-
191
- # Buffer containing the statsd message before they are sent in batch
192
- attr_reader :buffer
193
-
194
- # Maximum buffer size in bytes before it is flushed
195
- attr_reader :max_buffer_bytes
68
+ def tags
69
+ serializer.global_tags
70
+ end
196
71
 
197
- # Connection
198
- attr_reader :connection
72
+ # Default sample rate
73
+ attr_reader :sample_rate
199
74
 
200
75
  # @param [String] host your statsd host
201
76
  # @param [Integer] port your statsd port
202
77
  # @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
205
- # @option [Integer] max_buffer_bytes max bytes to buffer when using #batch
78
+ # @option [Array<String>|Hash] tags tags to be added to every metric
79
+ # @option [Logger] logger for debugging
80
+ # @option [Integer] buffer_max_payload_size max bytes to buffer
81
+ # @option [Integer] buffer_max_pool_size max messages to buffer
82
+ # @option [Integer] sender_queue_size size of the sender queue in number of buffers (multi-thread only)
83
+ # @option [Numeric] buffer_flush_interval interval in second to flush buffer
206
84
  # @option [String] socket_path unix socket path
85
+ # @option [Float] default sample rate if not overridden
86
+ # @option [Boolean] single_thread flushes the metrics on the main thread instead of in a companion thread
207
87
  def initialize(
208
88
  host = nil,
209
89
  port = nil,
90
+ socket_path: nil,
91
+
210
92
  namespace: nil,
211
93
  tags: nil,
212
- max_buffer_bytes: 8192,
213
- socket_path: nil,
214
- logger: nil
94
+ sample_rate: nil,
95
+
96
+ buffer_max_payload_size: nil,
97
+ buffer_max_pool_size: nil,
98
+ buffer_overflowing_stategy: :drop,
99
+ buffer_flush_interval: nil,
100
+
101
+ sender_queue_size: nil,
102
+
103
+ logger: nil,
104
+
105
+ single_thread: false,
106
+
107
+ telemetry_enable: true,
108
+ telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL
215
109
  )
216
- @connection = Connection.new(host, port, socket_path, logger)
217
- @logger = logger
110
+ unless tags.nil? || tags.is_a?(Array) || tags.is_a?(Hash)
111
+ raise ArgumentError, 'tags must be an array of string tags or a Hash'
112
+ end
218
113
 
219
114
  @namespace = namespace
220
115
  @prefix = @namespace ? "#{@namespace}.".freeze : nil
116
+ @serializer = Serialization::Serializer.new(prefix: @prefix, global_tags: tags)
117
+ @sample_rate = sample_rate
118
+
119
+ # deprecation message for ruby < 2.1.0 users as we will drop support for ruby 2.0
120
+ # in dogstatsd-ruby 5.4.0
121
+ # TODO(remy): remove this message and the two global vars used in dogstatd-ruby 5.4.0
122
+ if RUBY_VERSION < '2.1.0' && $deprecation_message_mutex.try_lock && !$deprecation_message_done
123
+ if logger != nil
124
+ logger.warn { "deprecation: dogstatsd-ruby will drop support of Ruby < 2.1.0 in a next minor release" }
125
+ else
126
+ puts("warning: deprecation: dogstatsd-ruby will drop support of Ruby < 2.1.0 in a next minor release")
127
+ end
128
+ $deprecation_message_done = true
129
+ $deprecation_message_mutex.unlock
130
+ end
131
+
132
+ @forwarder = Forwarder.new(
133
+ connection_cfg: ConnectionCfg.new(
134
+ host: host,
135
+ port: port,
136
+ socket_path: socket_path,
137
+ ),
138
+
139
+ global_tags: tags,
140
+ logger: logger,
141
+
142
+ single_thread: single_thread,
221
143
 
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)}
144
+ buffer_max_payload_size: buffer_max_payload_size,
145
+ buffer_max_pool_size: buffer_max_pool_size,
146
+ buffer_overflowing_stategy: buffer_overflowing_stategy,
147
+ buffer_flush_interval: buffer_flush_interval,
224
148
 
225
- @batch = Batch.new @connection, max_buffer_bytes
149
+ sender_queue_size: sender_queue_size,
150
+
151
+ telemetry_flush_interval: telemetry_enable ? telemetry_flush_interval : nil,
152
+ )
226
153
  end
227
154
 
228
155
  # yield a new instance to a block and close it when done
229
156
  # for short-term use-cases that don't want to close the socket manually
230
157
  def self.open(*args)
231
158
  instance = new(*args)
159
+
232
160
  yield instance
233
161
  ensure
234
162
  instance.close
@@ -242,10 +170,10 @@ module Datadog
242
170
  # @option opts [Array<String>] :tags An array of tags
243
171
  # @option opts [Numeric] :by increment value, default 1
244
172
  # @see #count
245
- def increment(stat, opts=EMPTY_OPTIONS)
246
- opts = {:sample_rate => opts} if opts.is_a? Numeric
173
+ def increment(stat, opts = EMPTY_OPTIONS)
174
+ opts = { sample_rate: opts } if opts.is_a?(Numeric)
247
175
  incr_value = opts.fetch(:by, 1)
248
- count stat, incr_value, opts
176
+ count(stat, incr_value, opts)
249
177
  end
250
178
 
251
179
  # Sends a decrement (count = -1) for the given stat to the statsd server.
@@ -256,10 +184,10 @@ module Datadog
256
184
  # @option opts [Array<String>] :tags An array of tags
257
185
  # @option opts [Numeric] :by decrement value, default 1
258
186
  # @see #count
259
- def decrement(stat, opts=EMPTY_OPTIONS)
260
- opts = {:sample_rate => opts} if opts.is_a? Numeric
187
+ def decrement(stat, opts = EMPTY_OPTIONS)
188
+ opts = { sample_rate: opts } if opts.is_a?(Numeric)
261
189
  decr_value = - opts.fetch(:by, 1)
262
- count stat, decr_value, opts
190
+ count(stat, decr_value, opts)
263
191
  end
264
192
 
265
193
  # Sends an arbitrary count for the given stat to the statsd server.
@@ -269,9 +197,9 @@ module Datadog
269
197
  # @param [Hash] opts the options to create the metric with
270
198
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
271
199
  # @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
200
+ def count(stat, count, opts = EMPTY_OPTIONS)
201
+ opts = { sample_rate: opts } if opts.is_a?(Numeric)
202
+ send_stats(stat, count, COUNTER_TYPE, opts)
275
203
  end
276
204
 
277
205
  # Sends an arbitary gauge value for the given stat to the statsd server.
@@ -287,9 +215,9 @@ module Datadog
287
215
  # @option opts [Array<String>] :tags An array of tags
288
216
  # @example Report the current user count:
289
217
  # $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
218
+ def gauge(stat, value, opts = EMPTY_OPTIONS)
219
+ opts = { sample_rate: opts } if opts.is_a?(Numeric)
220
+ send_stats(stat, value, GAUGE_TYPE, opts)
293
221
  end
294
222
 
295
223
  # Sends a value to be tracked as a histogram to the statsd server.
@@ -301,14 +229,11 @@ module Datadog
301
229
  # @option opts [Array<String>] :tags An array of tags
302
230
  # @example Report the current user count:
303
231
  # $statsd.histogram('user.count', User.count)
304
- def histogram(stat, value, opts=EMPTY_OPTIONS)
305
- send_stats stat, value, HISTOGRAM_TYPE, opts
232
+ def histogram(stat, value, opts = EMPTY_OPTIONS)
233
+ send_stats(stat, value, HISTOGRAM_TYPE, opts)
306
234
  end
307
235
 
308
236
  # 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
237
  #
313
238
  # @param [String] stat stat name.
314
239
  # @param [Numeric] value distribution value.
@@ -317,8 +242,8 @@ module Datadog
317
242
  # @option opts [Array<String>] :tags An array of tags
318
243
  # @example Report the current user count:
319
244
  # $statsd.distribution('user.count', User.count)
320
- def distribution(stat, value, opts=EMPTY_OPTIONS)
321
- send_stats stat, value, DISTRIBUTION_TYPE, opts
245
+ def distribution(stat, value, opts = EMPTY_OPTIONS)
246
+ send_stats(stat, value, DISTRIBUTION_TYPE, opts)
322
247
  end
323
248
 
324
249
  # Sends a timing (in ms) for the given stat to the statsd server. The
@@ -331,9 +256,9 @@ module Datadog
331
256
  # @param [Hash] opts the options to create the metric with
332
257
  # @option opts [Numeric] :sample_rate sample rate, 1 for always
333
258
  # @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
259
+ def timing(stat, ms, opts = EMPTY_OPTIONS)
260
+ opts = { sample_rate: opts } if opts.is_a?(Numeric)
261
+ send_stats(stat, ms, TIMING_TYPE, opts)
337
262
  end
338
263
 
339
264
  # Reports execution time of the provided block using {#timing}.
@@ -349,13 +274,12 @@ module Datadog
349
274
  # @see #timing
350
275
  # @example Report the time (in ms) taken to activate an account
351
276
  # $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
277
+ def time(stat, opts = EMPTY_OPTIONS)
278
+ opts = { sample_rate: opts } if opts.is_a?(Numeric)
279
+ start = now
280
+ yield
356
281
  ensure
357
- finished = (PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f)
358
- timing(stat, ((finished - start) * 1000).round, opts)
282
+ timing(stat, ((now - start) * 1000).round, opts)
359
283
  end
360
284
 
361
285
  # Sends a value to be tracked as a set to the statsd server.
@@ -367,9 +291,9 @@ module Datadog
367
291
  # @option opts [Array<String>] :tags An array of tags
368
292
  # @example Record a unique visitory by id:
369
293
  # $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
294
+ def set(stat, value, opts = EMPTY_OPTIONS)
295
+ opts = { sample_rate: opts } if opts.is_a?(Numeric)
296
+ send_stats(stat, value, SET_TYPE, opts)
373
297
  end
374
298
 
375
299
  # This method allows you to send custom service check statuses.
@@ -377,14 +301,16 @@ module Datadog
377
301
  # @param [String] name Service check name
378
302
  # @param [String] status Service check status.
379
303
  # @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.
304
+ # @option opts [Integer, String, nil] :timestamp (nil) Assign a timestamp to the service check. Default is now when none
305
+ # @option opts [String, nil] :hostname (nil) Assign a hostname to the service check.
382
306
  # @option opts [Array<String>, nil] :tags (nil) An array of tags
383
307
  # @option opts [String, nil] :message (nil) A message to associate with this service check status
384
308
  # @example Report a critical service check status
385
309
  # $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)
310
+ def service_check(name, status, opts = EMPTY_OPTIONS)
311
+ telemetry.sent(service_checks: 1) if telemetry
312
+
313
+ forwarder.send_message(serializer.to_service_check(name, status, opts))
388
314
  end
389
315
 
390
316
  # 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,23 +320,33 @@ module Datadog
394
320
  # it will be grouped with other events that don't have an event type.
395
321
  #
396
322
  # @param [String] title Event title
397
- # @param [String] text Event text. Supports \n
323
+ # @param [String] text Event text. Supports newlines (+\n+)
398
324
  # @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
325
+ # @option opts [Integer, String, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
400
326
  # @option opts [String, nil] :hostname (nil) Assign a hostname to the event.
401
327
  # @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others
402
328
  # @option opts [String, nil] :priority ('normal') Can be "normal" or "low"
403
329
  # @option opts [String, nil] :source_type_name (nil) Assign a source type to the event
404
330
  # @option opts [String, nil] :alert_type ('info') Can be "error", "warning", "info" or "success".
331
+ # @option opts [Boolean, false] :truncate_if_too_long (false) Truncate the event if it is too long
405
332
  # @option opts [Array<String>] :tags tags to be added to every metric
406
333
  # @example Report an awful event:
407
334
  # $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)
335
+ def event(title, text, opts = EMPTY_OPTIONS)
336
+ telemetry.sent(events: 1) if telemetry
337
+
338
+ forwarder.send_message(serializer.to_event(title, text, opts))
410
339
  end
411
340
 
412
- # Send several metrics in the same UDP Packet
413
- # They will be buffered and flushed when the block finishes
341
+ # Send several metrics in the same packet.
342
+ # They will be buffered and flushed when the block finishes.
343
+ #
344
+ # This method exists for compatibility with v4.x versions, it is not needed
345
+ # anymore since the batching is now automatically done internally.
346
+ # It also means that an automatic flush could occur if the buffer is filled
347
+ # during the execution of the batch block.
348
+ #
349
+ # This method is DEPRECATED and will be removed in future v6.x API.
414
350
  #
415
351
  # @example Send several metrics in one packet:
416
352
  # $statsd.batch do |s|
@@ -418,140 +354,73 @@ module Datadog
418
354
  # s.increment('page.views')
419
355
  # end
420
356
  def batch
421
- @batch.open { yield self }
357
+ yield self
358
+ flush(sync: true)
422
359
  end
423
360
 
424
361
  # Close the underlying socket
425
- def close
426
- @connection.close
362
+ #
363
+ # @param [Boolean, true] flush Should we flush the metrics before closing
364
+ def close(flush: true)
365
+ flush(sync: true) if flush
366
+ forwarder.close
427
367
  end
428
368
 
429
- private
430
-
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")
439
- EMPTY_OPTIONS = {}.freeze
440
-
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
462
- end
463
- sc_string
369
+ def sync_with_outbound_io
370
+ forwarder.sync_with_outbound_io
464
371
  end
465
372
 
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}"
483
- 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
373
+ # Flush the buffer into the connection
374
+ def flush(flush_telemetry: false, sync: false)
375
+ forwarder.flush(flush_telemetry: flush_telemetry, sync: sync)
487
376
  end
488
377
 
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?
378
+ def telemetry
379
+ forwarder.telemetry
497
380
  end
498
381
 
499
- def escape_event_content(msg)
500
- msg.gsub NEW_LINE, ESC_NEW_LINE
382
+ def host
383
+ forwarder.host
501
384
  end
502
385
 
503
- def escape_tag_content(tag)
504
- tag = remove_pipes(tag.to_s)
505
- tag.delete! COMMA
506
- tag
386
+ def port
387
+ forwarder.port
507
388
  end
508
389
 
509
- def remove_pipes(msg)
510
- msg.delete PIPE
390
+ def socket_path
391
+ forwarder.socket_path
511
392
  end
512
393
 
513
- def escape_service_check_message(msg)
514
- escape_event_content(msg).gsub('m:'.freeze, 'm\:'.freeze)
394
+ def transport_type
395
+ forwarder.transport_type
515
396
  end
516
397
 
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
398
+ private
399
+ attr_reader :serializer
400
+ attr_reader :forwarder
539
401
 
540
- if tags_string = tags_as_string(opts)
541
- full_stat << PIPE
542
- full_stat << '#'.freeze
543
- full_stat << tags_string
544
- end
402
+ PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= '2.1.0')
403
+ EMPTY_OPTIONS = {}.freeze
545
404
 
546
- send_stat(full_stat)
405
+ if PROCESS_TIME_SUPPORTED
406
+ def now
407
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
408
+ end
409
+ else
410
+ def now
411
+ Time.now.to_f
547
412
  end
548
413
  end
549
414
 
550
- def send_stat(message)
551
- if @batch.open?
552
- @batch.add message
553
- else
554
- @connection.write(message)
415
+ def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
416
+ telemetry.sent(metrics: 1) if telemetry
417
+
418
+ sample_rate = opts[:sample_rate] || @sample_rate || 1
419
+
420
+ if sample_rate == 1 || rand <= sample_rate
421
+ full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
422
+
423
+ forwarder.send_message(full_stat)
555
424
  end
556
425
  end
557
426
  end