dogstatsd-ruby 3.3.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +34 -5
- data/lib/datadog/statsd.rb +290 -182
- metadata +4 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: fde2be6b614574c2555c8cbbd2ca16ef33bcd09837502f482f5f6baf582fd67b
         | 
| 4 | 
            +
              data.tar.gz: cc1523c5aa78643d93fc891116c46bb5322dda297f1676cd73d201c9beee2a26
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 9c96177f1ea0ab2288712cf9829cb8408687e12ce0f22f06ddbb1433447a3a4eaf05fe13f0fda8746b9f0e8fad71ea3b0b50ba7d8a028e464db334349951c64b
         | 
| 7 | 
            +
              data.tar.gz: 11e23d434a7e88925e049484dfbb252d5c78ce12a4a07fd3c09edca57835db30224cba202f86f57352357acdf2073b8c8e93f20bd403975242b201321bef8ecc
         | 
    
        data/README.md
    CHANGED
    
    | @@ -4,7 +4,7 @@ dogstatsd-ruby | |
| 4 4 |  | 
| 5 5 | 
             
            A client for DogStatsD, an extension of the StatsD metric server for Datadog.
         | 
| 6 6 |  | 
| 7 | 
            -
            [](http://travis-ci.org/DataDog/dogstatsd-ruby)
         | 
| 8 8 |  | 
| 9 9 | 
             
            Quick Start Guide
         | 
| 10 10 | 
             
            -----------------
         | 
| @@ -28,9 +28,10 @@ statsd = Datadog::Statsd.new('localhost', 8125) | |
| 28 28 |  | 
| 29 29 | 
             
            # Increment a counter.
         | 
| 30 30 | 
             
            statsd.increment('page.views')
         | 
| 31 | 
            +
            statsd.increment('messages.count', by: 2, tags: ['kind:incoming'])
         | 
| 31 32 |  | 
| 32 33 | 
             
            # Record a gauge 50% of the time.
         | 
| 33 | 
            -
            statsd.gauge('users.online', 123, : | 
| 34 | 
            +
            statsd.gauge('users.online', 123, sample_rate: 0.5)
         | 
| 34 35 |  | 
| 35 36 | 
             
            # Sample a histogram
         | 
| 36 37 | 
             
            statsd.histogram('file.upload.size', 1234)
         | 
| @@ -48,7 +49,15 @@ statsd.batch do |s| | |
| 48 49 | 
             
            end
         | 
| 49 50 |  | 
| 50 51 | 
             
            # Tag a metric.
         | 
| 51 | 
            -
            statsd.histogram('query.time', 10, : | 
| 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
         | 
| 52 61 | 
             
            ```
         | 
| 53 62 |  | 
| 54 63 | 
             
            You can also post events to your stream. You can tag them, set priority and even aggregate them with other events.
         | 
| @@ -57,14 +66,34 @@ Aggregation in the stream is made on hostname/event_type/source_type/aggregation | |
| 57 66 |  | 
| 58 67 | 
             
            ``` ruby
         | 
| 59 68 | 
             
            # Post a simple message
         | 
| 60 | 
            -
            statsd.event( | 
| 69 | 
            +
            statsd.event('There might be a storm tomorrow', 'A friend warned me earlier.')
         | 
| 61 70 |  | 
| 62 71 | 
             
            # Cry for help
         | 
| 63 | 
            -
            statsd.event( | 
| 72 | 
            +
            statsd.event(
         | 
| 73 | 
            +
              'SO MUCH SNOW',
         | 
| 74 | 
            +
              "Started yesterday and it won't stop !!",
         | 
| 75 | 
            +
              alert_type: 'error',
         | 
| 76 | 
            +
              tags: ['urgent', 'endoftheworld']
         | 
| 77 | 
            +
            )
         | 
| 64 78 | 
             
            ```
         | 
| 65 79 |  | 
| 66 80 |  | 
| 67 81 |  | 
| 82 | 
            +
            Origin detection over UDP
         | 
| 83 | 
            +
            -------------
         | 
| 84 | 
            +
            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 | 
            +
             | 
| 86 | 
            +
            To enable origin detection over UDP, add the following lines to your application manifest
         | 
| 87 | 
            +
            ```yaml
         | 
| 88 | 
            +
            env:
         | 
| 89 | 
            +
              - name: DD_ENTITY_ID
         | 
| 90 | 
            +
                valueFrom:
         | 
| 91 | 
            +
                  fieldRef:
         | 
| 92 | 
            +
                    fieldPath: metadata.uid
         | 
| 93 | 
            +
            ```
         | 
| 94 | 
            +
            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 | 
            +
             | 
| 96 | 
            +
             | 
| 68 97 | 
             
            Documentation
         | 
| 69 98 | 
             
            -------------
         | 
| 70 99 |  | 
    
        data/lib/datadog/statsd.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require 'socket'
         | 
| 2 3 |  | 
| 3 4 | 
             
            # = Datadog::Statsd: A DogStatsd client (https://www.datadoghq.com)
         | 
| @@ -15,12 +16,151 @@ require 'socket' | |
| 15 16 | 
             
            #   statsd = Datadog::Statsd.new 'localhost', 8125, :namespace => 'account'
         | 
| 16 17 | 
             
            #   statsd.increment 'activate'
         | 
| 17 18 | 
             
            # @example Create a statsd client with global tags
         | 
| 18 | 
            -
            #   statsd = Datadog::Statsd.new 'localhost', 8125, : | 
| 19 | 
            +
            #   statsd = Datadog::Statsd.new 'localhost', 8125, tags: 'tag1:true'
         | 
| 19 20 | 
             
            module Datadog
         | 
| 20 21 | 
             
              class Statsd
         | 
| 21 22 |  | 
| 22 | 
            -
                 | 
| 23 | 
            -
             | 
| 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 &&
         | 
| 48 | 
            +
                      (boom.is_a?(Errno::ENOTCONN) or
         | 
| 49 | 
            +
                       boom.is_a?(Errno::ECONNREFUSED) or
         | 
| 50 | 
            +
                       boom.is_a?(IOError) && boom.message =~ /closed stream/i)
         | 
| 51 | 
            +
                      retries += 1
         | 
| 52 | 
            +
                      begin
         | 
| 53 | 
            +
                        @socket = connect
         | 
| 54 | 
            +
                        retry
         | 
| 55 | 
            +
                      rescue StandardError => e
         | 
| 56 | 
            +
                        boom = e
         | 
| 57 | 
            +
                      end
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    @logger.error { "Statsd: #{boom.class} #{boom}" } if @logger
         | 
| 61 | 
            +
                    nil
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  private
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  def socket
         | 
| 67 | 
            +
                    @socket ||= connect
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                class UDPConnection < Connection
         | 
| 72 | 
            +
                  def initialize(host, port, logger)
         | 
| 73 | 
            +
                    @host = host || ENV.fetch('DD_AGENT_HOST', nil) || DEFAULT_HOST
         | 
| 74 | 
            +
                    @port = port || ENV.fetch('DD_DOGSTATSD_PORT', nil) || DEFAULT_PORT
         | 
| 75 | 
            +
                    @logger = logger
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  private
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  def connect
         | 
| 81 | 
            +
                    socket = UDPSocket.new
         | 
| 82 | 
            +
                    socket.connect(@host, @port)
         | 
| 83 | 
            +
                    socket
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  def send_message(message)
         | 
| 87 | 
            +
                    socket.send(message, 0)
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                class UDSConnection < Connection
         | 
| 92 | 
            +
                  class BadSocketError < StandardError; end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                  def initialize(socket_path, logger)
         | 
| 95 | 
            +
                    @socket_path = socket_path
         | 
| 96 | 
            +
                    @logger = logger
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                  private
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                  def connect
         | 
| 102 | 
            +
                    socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
         | 
| 103 | 
            +
                    socket.connect(Socket.pack_sockaddr_un(@socket_path))
         | 
| 104 | 
            +
                    socket
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  def send_message(message)
         | 
| 108 | 
            +
                    socket.sendmsg_nonblock(message)
         | 
| 109 | 
            +
                  rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENOENT => e
         | 
| 110 | 
            +
                    @socket = nil
         | 
| 111 | 
            +
                    raise BadSocketError, "#{e.class}: #{e}"
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                class Batch
         | 
| 116 | 
            +
                  def initialize(connection, max_buffer_bytes)
         | 
| 117 | 
            +
                    @connection = connection
         | 
| 118 | 
            +
                    @max_buffer_bytes = max_buffer_bytes
         | 
| 119 | 
            +
                    @depth = 0
         | 
| 120 | 
            +
                    reset
         | 
| 121 | 
            +
                  end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                  def open
         | 
| 124 | 
            +
                    @depth += 1
         | 
| 125 | 
            +
                    yield
         | 
| 126 | 
            +
                  ensure
         | 
| 127 | 
            +
                    @depth -= 1
         | 
| 128 | 
            +
                    flush if !open?
         | 
| 129 | 
            +
                  end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                  def open?
         | 
| 132 | 
            +
                    @depth > 0
         | 
| 133 | 
            +
                  end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                  def add(message)
         | 
| 136 | 
            +
                    message_bytes = message.bytesize
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                    unless @buffer_bytes == 0
         | 
| 139 | 
            +
                      if @buffer_bytes + 1 + message_bytes >= @max_buffer_bytes
         | 
| 140 | 
            +
                        flush
         | 
| 141 | 
            +
                      else
         | 
| 142 | 
            +
                        @buffer << NEW_LINE
         | 
| 143 | 
            +
                        @buffer_bytes += 1
         | 
| 144 | 
            +
                      end
         | 
| 145 | 
            +
                    end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    @buffer << message
         | 
| 148 | 
            +
                    @buffer_bytes += message_bytes
         | 
| 149 | 
            +
                  end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                  def flush
         | 
| 152 | 
            +
                    return if @buffer_bytes == 0
         | 
| 153 | 
            +
                    @connection.write @buffer
         | 
| 154 | 
            +
                    reset
         | 
| 155 | 
            +
                  end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                  private
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                  def reset
         | 
| 160 | 
            +
                    @buffer = String.new
         | 
| 161 | 
            +
                    @buffer_bytes = 0
         | 
| 162 | 
            +
                  end
         | 
| 163 | 
            +
                end
         | 
| 24 164 |  | 
| 25 165 | 
             
                # Create a dictionary to assign a key to every parameter's name, except for tags (treated differently)
         | 
| 26 166 | 
             
                # Goal: Simple and fast to add some other parameters
         | 
| @@ -46,78 +186,84 @@ module Datadog | |
| 46 186 | 
             
                CRITICAL  = 2
         | 
| 47 187 | 
             
                UNKNOWN   = 3
         | 
| 48 188 |  | 
| 189 | 
            +
                MAX_EVENT_SIZE = 8 * 1024
         | 
| 190 | 
            +
             | 
| 49 191 | 
             
                COUNTER_TYPE = 'c'.freeze
         | 
| 50 192 | 
             
                GAUGE_TYPE = 'g'.freeze
         | 
| 51 193 | 
             
                HISTOGRAM_TYPE = 'h'.freeze
         | 
| 52 194 | 
             
                DISTRIBUTION_TYPE = 'd'.freeze
         | 
| 53 195 | 
             
                TIMING_TYPE = 'ms'.freeze
         | 
| 54 196 | 
             
                SET_TYPE = 's'.freeze
         | 
| 55 | 
            -
                VERSION = " | 
| 197 | 
            +
                VERSION = "4.5.0".freeze
         | 
| 56 198 |  | 
| 57 199 | 
             
                # A namespace to prepend to all statsd calls. Defaults to no namespace.
         | 
| 58 200 | 
             
                attr_reader :namespace
         | 
| 59 201 |  | 
| 60 | 
            -
                # StatsD host. Defaults to 127.0.0.1.
         | 
| 61 | 
            -
                attr_reader :host
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                # StatsD port. Defaults to 8125.
         | 
| 64 | 
            -
                attr_reader :port
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                # DogStatsd unix socket path. Not used by default.
         | 
| 67 | 
            -
                attr_reader :socket_path
         | 
| 68 | 
            -
             | 
| 69 202 | 
             
                # Global tags to be added to every statsd call. Defaults to no tags.
         | 
| 70 203 | 
             
                attr_reader :tags
         | 
| 71 204 |  | 
| 72 205 | 
             
                # Buffer containing the statsd message before they are sent in batch
         | 
| 73 206 | 
             
                attr_reader :buffer
         | 
| 74 207 |  | 
| 75 | 
            -
                # Maximum  | 
| 76 | 
            -
                 | 
| 208 | 
            +
                # Maximum buffer size in bytes before it is flushed
         | 
| 209 | 
            +
                attr_reader :max_buffer_bytes
         | 
| 77 210 |  | 
| 78 | 
            -
                 | 
| 79 | 
            -
             | 
| 80 | 
            -
                  attr_accessor :logger
         | 
| 81 | 
            -
                end
         | 
| 211 | 
            +
                # Default sample rate
         | 
| 212 | 
            +
                attr_reader :sample_rate
         | 
| 82 213 |  | 
| 83 | 
            -
                #  | 
| 84 | 
            -
                 | 
| 85 | 
            -
                def self.VERSION
         | 
| 86 | 
            -
                  VERSION
         | 
| 87 | 
            -
                end
         | 
| 214 | 
            +
                # Connection
         | 
| 215 | 
            +
                attr_reader :connection
         | 
| 88 216 |  | 
| 89 217 | 
             
                # @param [String] host your statsd host
         | 
| 90 218 | 
             
                # @param [Integer] port your statsd port
         | 
| 91 | 
            -
                # @option  | 
| 92 | 
            -
                # @option  | 
| 93 | 
            -
                 | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
                   | 
| 99 | 
            -
                   | 
| 100 | 
            -
                   | 
| 101 | 
            -
                   | 
| 102 | 
            -
                   | 
| 103 | 
            -
             | 
| 219 | 
            +
                # @option [String] namespace set a namespace to be prepended to every metric name
         | 
| 220 | 
            +
                # @option [Array<String>|Hash] tags tags to be added to every metric
         | 
| 221 | 
            +
                # @option [Logger] logger for debugging
         | 
| 222 | 
            +
                # @option [Integer] max_buffer_bytes max bytes to buffer when using #batch
         | 
| 223 | 
            +
                # @option [String] socket_path unix socket path
         | 
| 224 | 
            +
                # @option [Float] default sample rate if not overridden
         | 
| 225 | 
            +
                def initialize(
         | 
| 226 | 
            +
                  host = nil,
         | 
| 227 | 
            +
                  port = nil,
         | 
| 228 | 
            +
                  namespace: nil,
         | 
| 229 | 
            +
                  tags: nil,
         | 
| 230 | 
            +
                  max_buffer_bytes: 8192,
         | 
| 231 | 
            +
                  socket_path: nil,
         | 
| 232 | 
            +
                  logger: nil,
         | 
| 233 | 
            +
                  sample_rate: nil
         | 
| 234 | 
            +
                )
         | 
| 235 | 
            +
                  if socket_path.nil?
         | 
| 236 | 
            +
                    @connection = UDPConnection.new(host, port, logger)
         | 
| 237 | 
            +
                  else
         | 
| 238 | 
            +
                    @connection = UDSConnection.new(socket_path, logger)
         | 
| 239 | 
            +
                  end
         | 
| 240 | 
            +
                  @logger = logger
         | 
| 104 241 |  | 
| 105 | 
            -
                def namespace=(namespace) #:nodoc:
         | 
| 106 242 | 
             
                  @namespace = namespace
         | 
| 107 | 
            -
                  @prefix = namespace | 
| 108 | 
            -
                end
         | 
| 243 | 
            +
                  @prefix = @namespace ? "#{@namespace}.".freeze : nil
         | 
| 109 244 |  | 
| 110 | 
            -
             | 
| 111 | 
            -
                  @host = host || DEFAULT_HOST
         | 
| 112 | 
            -
                end
         | 
| 245 | 
            +
                  @sample_rate = sample_rate
         | 
| 113 246 |  | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 247 | 
            +
                  unless tags.nil? or tags.is_a? Array or tags.is_a? Hash
         | 
| 248 | 
            +
                    raise ArgumentError, 'tags must be a Array<String> or a Hash'
         | 
| 249 | 
            +
                  end
         | 
| 117 250 |  | 
| 118 | 
            -
             | 
| 119 | 
            -
                  raise ArgumentError, 'tags must be a Array<String>' unless tags.nil? or tags.is_a? Array
         | 
| 251 | 
            +
                  tags = tag_hash_to_array(tags) if tags.is_a? Hash
         | 
| 120 252 | 
             
                  @tags = (tags || []).compact.map! {|tag| escape_tag_content(tag)}
         | 
| 253 | 
            +
             | 
| 254 | 
            +
                  # append the entity id to tags if DD_ENTITY_ID env var is not nil
         | 
| 255 | 
            +
                  @tags << 'dd.internal.entity_id:' + escape_tag_content(ENV.fetch('DD_ENTITY_ID', nil)) unless ENV.fetch('DD_ENTITY_ID', nil).nil?
         | 
| 256 | 
            +
             | 
| 257 | 
            +
                  @batch = Batch.new @connection, max_buffer_bytes
         | 
| 258 | 
            +
                end
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                # yield a new instance to a block and close it when done
         | 
| 261 | 
            +
                # for short-term use-cases that don't want to close the socket manually
         | 
| 262 | 
            +
                def self.open(*args)
         | 
| 263 | 
            +
                  instance = new(*args)
         | 
| 264 | 
            +
                  yield instance
         | 
| 265 | 
            +
                ensure
         | 
| 266 | 
            +
                  instance.close
         | 
| 121 267 | 
             
                end
         | 
| 122 268 |  | 
| 123 269 | 
             
                # Sends an increment (count = 1) for the given stat to the statsd server.
         | 
| @@ -128,7 +274,7 @@ module Datadog | |
| 128 274 | 
             
                # @option opts [Array<String>] :tags An array of tags
         | 
| 129 275 | 
             
                # @option opts [Numeric] :by increment value, default 1
         | 
| 130 276 | 
             
                # @see #count
         | 
| 131 | 
            -
                def increment(stat, opts= | 
| 277 | 
            +
                def increment(stat, opts=EMPTY_OPTIONS)
         | 
| 132 278 | 
             
                  opts = {:sample_rate => opts} if opts.is_a? Numeric
         | 
| 133 279 | 
             
                  incr_value = opts.fetch(:by, 1)
         | 
| 134 280 | 
             
                  count stat, incr_value, opts
         | 
| @@ -142,7 +288,7 @@ module Datadog | |
| 142 288 | 
             
                # @option opts [Array<String>] :tags An array of tags
         | 
| 143 289 | 
             
                # @option opts [Numeric] :by decrement value, default 1
         | 
| 144 290 | 
             
                # @see #count
         | 
| 145 | 
            -
                def decrement(stat, opts= | 
| 291 | 
            +
                def decrement(stat, opts=EMPTY_OPTIONS)
         | 
| 146 292 | 
             
                  opts = {:sample_rate => opts} if opts.is_a? Numeric
         | 
| 147 293 | 
             
                  decr_value = - opts.fetch(:by, 1)
         | 
| 148 294 | 
             
                  count stat, decr_value, opts
         | 
| @@ -155,7 +301,7 @@ module Datadog | |
| 155 301 | 
             
                # @param [Hash] opts the options to create the metric with
         | 
| 156 302 | 
             
                # @option opts [Numeric] :sample_rate sample rate, 1 for always
         | 
| 157 303 | 
             
                # @option opts [Array<String>] :tags An array of tags
         | 
| 158 | 
            -
                def count(stat, count, opts= | 
| 304 | 
            +
                def count(stat, count, opts=EMPTY_OPTIONS)
         | 
| 159 305 | 
             
                  opts = {:sample_rate => opts} if opts.is_a? Numeric
         | 
| 160 306 | 
             
                  send_stats stat, count, COUNTER_TYPE, opts
         | 
| 161 307 | 
             
                end
         | 
| @@ -173,7 +319,7 @@ module Datadog | |
| 173 319 | 
             
                # @option opts [Array<String>] :tags An array of tags
         | 
| 174 320 | 
             
                # @example Report the current user count:
         | 
| 175 321 | 
             
                #   $statsd.gauge('user.count', User.count)
         | 
| 176 | 
            -
                def gauge(stat, value, opts= | 
| 322 | 
            +
                def gauge(stat, value, opts=EMPTY_OPTIONS)
         | 
| 177 323 | 
             
                  opts = {:sample_rate => opts} if opts.is_a? Numeric
         | 
| 178 324 | 
             
                  send_stats stat, value, GAUGE_TYPE, opts
         | 
| 179 325 | 
             
                end
         | 
| @@ -187,7 +333,7 @@ module Datadog | |
| 187 333 | 
             
                # @option opts [Array<String>] :tags An array of tags
         | 
| 188 334 | 
             
                # @example Report the current user count:
         | 
| 189 335 | 
             
                #   $statsd.histogram('user.count', User.count)
         | 
| 190 | 
            -
                def histogram(stat, value, opts= | 
| 336 | 
            +
                def histogram(stat, value, opts=EMPTY_OPTIONS)
         | 
| 191 337 | 
             
                  send_stats stat, value, HISTOGRAM_TYPE, opts
         | 
| 192 338 | 
             
                end
         | 
| 193 339 |  | 
| @@ -203,7 +349,7 @@ module Datadog | |
| 203 349 | 
             
                # @option opts [Array<String>] :tags An array of tags
         | 
| 204 350 | 
             
                # @example Report the current user count:
         | 
| 205 351 | 
             
                #   $statsd.distribution('user.count', User.count)
         | 
| 206 | 
            -
                def distribution(stat, value, opts= | 
| 352 | 
            +
                def distribution(stat, value, opts=EMPTY_OPTIONS)
         | 
| 207 353 | 
             
                  send_stats stat, value, DISTRIBUTION_TYPE, opts
         | 
| 208 354 | 
             
                end
         | 
| 209 355 |  | 
| @@ -217,7 +363,7 @@ module Datadog | |
| 217 363 | 
             
                # @param [Hash] opts the options to create the metric with
         | 
| 218 364 | 
             
                # @option opts [Numeric] :sample_rate sample rate, 1 for always
         | 
| 219 365 | 
             
                # @option opts [Array<String>] :tags An array of tags
         | 
| 220 | 
            -
                def timing(stat, ms, opts= | 
| 366 | 
            +
                def timing(stat, ms, opts=EMPTY_OPTIONS)
         | 
| 221 367 | 
             
                  opts = {:sample_rate => opts} if opts.is_a? Numeric
         | 
| 222 368 | 
             
                  send_stats stat, ms, TIMING_TYPE, opts
         | 
| 223 369 | 
             
                end
         | 
| @@ -235,7 +381,7 @@ module Datadog | |
| 235 381 | 
             
                # @see #timing
         | 
| 236 382 | 
             
                # @example Report the time (in ms) taken to activate an account
         | 
| 237 383 | 
             
                #   $statsd.time('account.activate') { @account.activate! }
         | 
| 238 | 
            -
                def time(stat, opts= | 
| 384 | 
            +
                def time(stat, opts=EMPTY_OPTIONS)
         | 
| 239 385 | 
             
                  opts = {:sample_rate => opts} if opts.is_a? Numeric
         | 
| 240 386 | 
             
                  start = (PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f)
         | 
| 241 387 | 
             
                  return yield
         | 
| @@ -243,6 +389,7 @@ module Datadog | |
| 243 389 | 
             
                  finished = (PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f)
         | 
| 244 390 | 
             
                  timing(stat, ((finished - start) * 1000).round, opts)
         | 
| 245 391 | 
             
                end
         | 
| 392 | 
            +
             | 
| 246 393 | 
             
                # Sends a value to be tracked as a set to the statsd server.
         | 
| 247 394 | 
             
                #
         | 
| 248 395 | 
             
                # @param [String] stat stat name.
         | 
| @@ -252,7 +399,7 @@ module Datadog | |
| 252 399 | 
             
                # @option opts [Array<String>] :tags An array of tags
         | 
| 253 400 | 
             
                # @example Record a unique visitory by id:
         | 
| 254 401 | 
             
                #   $statsd.set('visitors.uniques', User.id)
         | 
| 255 | 
            -
                def set(stat, value, opts= | 
| 402 | 
            +
                def set(stat, value, opts=EMPTY_OPTIONS)
         | 
| 256 403 | 
             
                  opts = {:sample_rate => opts} if opts.is_a? Numeric
         | 
| 257 404 | 
             
                  send_stats stat, value, SET_TYPE, opts
         | 
| 258 405 | 
             
                end
         | 
| @@ -262,37 +409,14 @@ module Datadog | |
| 262 409 | 
             
                # @param [String] name Service check name
         | 
| 263 410 | 
             
                # @param [String] status Service check status.
         | 
| 264 411 | 
             
                # @param [Hash] opts the additional data about the service check
         | 
| 265 | 
            -
                  # @option opts [Integer, nil] :timestamp (nil) Assign a timestamp to the  | 
| 266 | 
            -
                  # @option opts [String, nil] :hostname (nil) Assign a hostname to the  | 
| 412 | 
            +
                  # @option opts [Integer, String, nil] :timestamp (nil) Assign a timestamp to the service check. Default is now when none
         | 
| 413 | 
            +
                  # @option opts [String, nil] :hostname (nil) Assign a hostname to the service check.
         | 
| 267 414 | 
             
                  # @option opts [Array<String>, nil] :tags (nil) An array of tags
         | 
| 268 415 | 
             
                  # @option opts [String, nil] :message (nil) A message to associate with this service check status
         | 
| 269 416 | 
             
                # @example Report a critical service check status
         | 
| 270 417 | 
             
                #   $statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
         | 
| 271 | 
            -
                def service_check(name, status, opts= | 
| 272 | 
            -
                   | 
| 273 | 
            -
                  send_to_socket service_check_string
         | 
| 274 | 
            -
                end
         | 
| 275 | 
            -
             | 
| 276 | 
            -
                def format_service_check(name, status, opts={})
         | 
| 277 | 
            -
                  sc_string = "_sc|#{name}|#{status}"
         | 
| 278 | 
            -
             | 
| 279 | 
            -
                  SC_OPT_KEYS.each do |key, shorthand_key|
         | 
| 280 | 
            -
                    next unless opts[key]
         | 
| 281 | 
            -
             | 
| 282 | 
            -
                    if key == :tags
         | 
| 283 | 
            -
                      if tags_string = tags_as_string(opts)
         | 
| 284 | 
            -
                        sc_string << "|##{tags_string}"
         | 
| 285 | 
            -
                      end
         | 
| 286 | 
            -
                    elsif key == :message
         | 
| 287 | 
            -
                      message = remove_pipes(opts[:message])
         | 
| 288 | 
            -
                      escaped_message = escape_service_check_message(message)
         | 
| 289 | 
            -
                      sc_string << "|m:#{escaped_message}"
         | 
| 290 | 
            -
                    else
         | 
| 291 | 
            -
                      value = remove_pipes(opts[key])
         | 
| 292 | 
            -
                      sc_string << "|#{shorthand_key}#{value}"
         | 
| 293 | 
            -
                    end
         | 
| 294 | 
            -
                  end
         | 
| 295 | 
            -
                  return sc_string
         | 
| 418 | 
            +
                def service_check(name, status, opts=EMPTY_OPTIONS)
         | 
| 419 | 
            +
                  send_stat format_service_check(name, status, opts)
         | 
| 296 420 | 
             
                end
         | 
| 297 421 |  | 
| 298 422 | 
             
                # This end point allows you to post events to the stream. You can tag them, set priority and even aggregate them with other events.
         | 
| @@ -302,9 +426,9 @@ module Datadog | |
| 302 426 | 
             
                # it will be grouped with other events that don't have an event type.
         | 
| 303 427 | 
             
                #
         | 
| 304 428 | 
             
                # @param [String] title Event title
         | 
| 305 | 
            -
                # @param [String] text Event text. Supports  | 
| 429 | 
            +
                # @param [String] text Event text. Supports newlines (+\n+)
         | 
| 306 430 | 
             
                # @param [Hash] opts the additional data about the event
         | 
| 307 | 
            -
                # @option opts [Integer, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
         | 
| 431 | 
            +
                # @option opts [Integer, String, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none
         | 
| 308 432 | 
             
                # @option opts [String, nil] :hostname (nil) Assign a hostname to the event.
         | 
| 309 433 | 
             
                # @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others
         | 
| 310 434 | 
             
                # @option opts [String, nil] :priority ('normal') Can be "normal" or "low"
         | 
| @@ -313,11 +437,8 @@ module Datadog | |
| 313 437 | 
             
                # @option opts [Array<String>] :tags tags to be added to every metric
         | 
| 314 438 | 
             
                # @example Report an awful event:
         | 
| 315 439 | 
             
                #   $statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent'])
         | 
| 316 | 
            -
                def event(title, text, opts= | 
| 317 | 
            -
                   | 
| 318 | 
            -
                  raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string.length > 8 * 1024
         | 
| 319 | 
            -
             | 
| 320 | 
            -
                  send_to_socket event_string
         | 
| 440 | 
            +
                def event(title, text, opts=EMPTY_OPTIONS)
         | 
| 441 | 
            +
                  send_stat format_event(title, text, opts)
         | 
| 321 442 | 
             
                end
         | 
| 322 443 |  | 
| 323 444 | 
             
                # Send several metrics in the same UDP Packet
         | 
| @@ -328,24 +449,73 @@ module Datadog | |
| 328 449 | 
             
                #      s.gauge('users.online',156)
         | 
| 329 450 | 
             
                #      s.increment('page.views')
         | 
| 330 451 | 
             
                #    end
         | 
| 331 | 
            -
                def batch | 
| 332 | 
            -
                  @ | 
| 333 | 
            -
             | 
| 334 | 
            -
             | 
| 335 | 
            -
             | 
| 336 | 
            -
             | 
| 452 | 
            +
                def batch
         | 
| 453 | 
            +
                  @batch.open { yield self }
         | 
| 454 | 
            +
                end
         | 
| 455 | 
            +
             | 
| 456 | 
            +
                # Close the underlying socket
         | 
| 457 | 
            +
                def close
         | 
| 458 | 
            +
                  @connection.close
         | 
| 459 | 
            +
                end
         | 
| 460 | 
            +
             | 
| 461 | 
            +
                private
         | 
| 462 | 
            +
             | 
| 463 | 
            +
                NEW_LINE = "\n".freeze
         | 
| 464 | 
            +
                ESC_NEW_LINE = "\\n".freeze
         | 
| 465 | 
            +
                COMMA = ",".freeze
         | 
| 466 | 
            +
                PIPE = "|".freeze
         | 
| 467 | 
            +
                DOT = ".".freeze
         | 
| 468 | 
            +
                DOUBLE_COLON = "::".freeze
         | 
| 469 | 
            +
                UNDERSCORE = "_".freeze
         | 
| 470 | 
            +
                PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= "2.1.0")
         | 
| 471 | 
            +
                EMPTY_OPTIONS = {}.freeze
         | 
| 472 | 
            +
             | 
| 473 | 
            +
                private_constant :NEW_LINE, :ESC_NEW_LINE, :COMMA, :PIPE, :DOT,
         | 
| 474 | 
            +
                  :DOUBLE_COLON, :UNDERSCORE, :EMPTY_OPTIONS
         | 
| 475 | 
            +
             | 
| 476 | 
            +
                def format_service_check(name, status, opts=EMPTY_OPTIONS)
         | 
| 477 | 
            +
                  sc_string = "_sc|#{name}|#{status}".dup
         | 
| 478 | 
            +
             | 
| 479 | 
            +
                  SC_OPT_KEYS.each do |key, shorthand_key|
         | 
| 480 | 
            +
                    next unless opts[key]
         | 
| 481 | 
            +
             | 
| 482 | 
            +
                    if key == :tags
         | 
| 483 | 
            +
                      if tags_string = tags_as_string(opts)
         | 
| 484 | 
            +
                        sc_string << "|##{tags_string}"
         | 
| 485 | 
            +
                      end
         | 
| 486 | 
            +
                    elsif key == :message
         | 
| 487 | 
            +
                      message = remove_pipes(opts[:message])
         | 
| 488 | 
            +
                      escaped_message = escape_service_check_message(message)
         | 
| 489 | 
            +
                      sc_string << "|m:#{escaped_message}"
         | 
| 490 | 
            +
                    else
         | 
| 491 | 
            +
                      if key == :timestamp && opts[key].is_a?(Integer)
         | 
| 492 | 
            +
                        value = opts[key]
         | 
| 493 | 
            +
                      else
         | 
| 494 | 
            +
                        value = remove_pipes(opts[key])
         | 
| 495 | 
            +
                      end
         | 
| 496 | 
            +
                      sc_string << "|#{shorthand_key}#{value}"
         | 
| 497 | 
            +
                    end
         | 
| 498 | 
            +
                  end
         | 
| 499 | 
            +
                  sc_string
         | 
| 337 500 | 
             
                end
         | 
| 338 501 |  | 
| 339 | 
            -
                def format_event(title, text, opts= | 
| 502 | 
            +
                def format_event(title, text, opts=EMPTY_OPTIONS)
         | 
| 340 503 | 
             
                  escaped_title = escape_event_content(title)
         | 
| 341 504 | 
             
                  escaped_text = escape_event_content(text)
         | 
| 342 | 
            -
                  event_string_data = "_e{#{escaped_title. | 
| 505 | 
            +
                  event_string_data = "_e{#{escaped_title.bytesize},#{escaped_text.bytesize}}:#{escaped_title}|#{escaped_text}".dup
         | 
| 343 506 |  | 
| 344 507 | 
             
                  # We construct the string to be sent by adding '|key:value' parts to it when needed
         | 
| 345 508 | 
             
                  # All pipes ('|') in the metadata are removed. Title and Text can keep theirs
         | 
| 346 509 | 
             
                  OPTS_KEYS.each do |key, shorthand_key|
         | 
| 347 510 | 
             
                    if key != :tags && opts[key]
         | 
| 348 | 
            -
                      value  | 
| 511 | 
            +
                      # :date_happened is the only key where the value is an Integer
         | 
| 512 | 
            +
                      # To not break backwards compatibility, we still accept a String
         | 
| 513 | 
            +
                      if key == :date_happened && opts[key].is_a?(Integer)
         | 
| 514 | 
            +
                          value = opts[key]
         | 
| 515 | 
            +
                      # All other keys only have String values
         | 
| 516 | 
            +
                      else
         | 
| 517 | 
            +
                          value = remove_pipes(opts[key])
         | 
| 518 | 
            +
                      end
         | 
| 349 519 | 
             
                      event_string_data << "|#{shorthand_key}:#{value}"
         | 
| 350 520 | 
             
                    end
         | 
| 351 521 | 
             
                  end
         | 
| @@ -355,36 +525,25 @@ module Datadog | |
| 355 525 | 
             
                    event_string_data << "|##{tags_string}"
         | 
| 356 526 | 
             
                  end
         | 
| 357 527 |  | 
| 358 | 
            -
                  raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data. | 
| 359 | 
            -
                   | 
| 360 | 
            -
                end
         | 
| 361 | 
            -
             | 
| 362 | 
            -
                # Close the underlying socket
         | 
| 363 | 
            -
                def close()
         | 
| 364 | 
            -
                  @socket.close
         | 
| 528 | 
            +
                  raise "Event #{title} payload is too big (more that 8KB), event discarded" if event_string_data.bytesize > MAX_EVENT_SIZE
         | 
| 529 | 
            +
                  event_string_data
         | 
| 365 530 | 
             
                end
         | 
| 366 531 |  | 
| 367 | 
            -
                private
         | 
| 368 | 
            -
             | 
| 369 | 
            -
                NEW_LINE = "\n".freeze
         | 
| 370 | 
            -
                ESC_NEW_LINE = "\\n".freeze
         | 
| 371 | 
            -
                COMMA = ",".freeze
         | 
| 372 | 
            -
                PIPE = "|".freeze
         | 
| 373 | 
            -
                DOT = ".".freeze
         | 
| 374 | 
            -
                DOUBLE_COLON = "::".freeze
         | 
| 375 | 
            -
                UNDERSCORE = "_".freeze
         | 
| 376 | 
            -
                PROCESS_TIME_SUPPORTED = (RUBY_VERSION >= "2.1.0")
         | 
| 377 | 
            -
             | 
| 378 | 
            -
                private_constant :NEW_LINE, :ESC_NEW_LINE, :COMMA, :PIPE, :DOT,
         | 
| 379 | 
            -
                  :DOUBLE_COLON, :UNDERSCORE
         | 
| 380 | 
            -
             | 
| 381 532 | 
             
                def tags_as_string(opts)
         | 
| 382 | 
            -
                  tag_arr = opts[:tags] | 
| 383 | 
            -
             | 
| 384 | 
            -
             | 
| 533 | 
            +
                  if tag_arr = opts[:tags]
         | 
| 534 | 
            +
                    tag_arr = tag_hash_to_array(tag_arr) if tag_arr.is_a? Hash
         | 
| 535 | 
            +
                    tag_arr = tag_arr.map { |tag| escape_tag_content(tag) }
         | 
| 536 | 
            +
                    tag_arr = tags + tag_arr # @tags are normalized when set, so not need to normalize them again
         | 
| 537 | 
            +
                  else
         | 
| 538 | 
            +
                    tag_arr = tags
         | 
| 539 | 
            +
                  end
         | 
| 385 540 | 
             
                  tag_arr.join(COMMA) unless tag_arr.empty?
         | 
| 386 541 | 
             
                end
         | 
| 387 542 |  | 
| 543 | 
            +
                def tag_hash_to_array(tag_hash)
         | 
| 544 | 
            +
                  tag_hash.to_a.map {|pair| pair.compact.join(":")}
         | 
| 545 | 
            +
                end
         | 
| 546 | 
            +
             | 
| 388 547 | 
             
                def escape_event_content(msg)
         | 
| 389 548 | 
             
                  msg.gsub NEW_LINE, ESC_NEW_LINE
         | 
| 390 549 | 
             
                end
         | 
| @@ -403,10 +562,10 @@ module Datadog | |
| 403 562 | 
             
                  escape_event_content(msg).gsub('m:'.freeze, 'm\:'.freeze)
         | 
| 404 563 | 
             
                end
         | 
| 405 564 |  | 
| 406 | 
            -
                def send_stats(stat, delta, type, opts= | 
| 407 | 
            -
                  sample_rate = opts[:sample_rate] || 1
         | 
| 408 | 
            -
                  if sample_rate == 1 or rand  | 
| 409 | 
            -
                    full_stat = ''
         | 
| 565 | 
            +
                def send_stats(stat, delta, type, opts=EMPTY_OPTIONS)
         | 
| 566 | 
            +
                  sample_rate = opts[:sample_rate] || @sample_rate || 1
         | 
| 567 | 
            +
                  if sample_rate == 1 or rand <= sample_rate
         | 
| 568 | 
            +
                    full_stat = ''.dup
         | 
| 410 569 | 
             
                    full_stat << @prefix if @prefix
         | 
| 411 570 |  | 
| 412 571 | 
             
                    stat = stat.is_a?(String) ? stat.dup : stat.to_s
         | 
| @@ -437,62 +596,11 @@ module Datadog | |
| 437 596 | 
             
                end
         | 
| 438 597 |  | 
| 439 598 | 
             
                def send_stat(message)
         | 
| 440 | 
            -
                  if @ | 
| 441 | 
            -
                    @ | 
| 442 | 
            -
                    flush_buffer if @buffer.length >= @max_buffer_size
         | 
| 599 | 
            +
                  if @batch.open?
         | 
| 600 | 
            +
                    @batch.add message
         | 
| 443 601 | 
             
                  else
         | 
| 444 | 
            -
                     | 
| 445 | 
            -
                  end
         | 
| 446 | 
            -
                end
         | 
| 447 | 
            -
             | 
| 448 | 
            -
                def flush_buffer
         | 
| 449 | 
            -
                  return @buffer if @buffer.empty?
         | 
| 450 | 
            -
                  send_to_socket(@buffer.join(NEW_LINE))
         | 
| 451 | 
            -
                  @buffer = Array.new
         | 
| 452 | 
            -
                end
         | 
| 453 | 
            -
             | 
| 454 | 
            -
                def connect_to_socket
         | 
| 455 | 
            -
                  if !@socket_path.nil?
         | 
| 456 | 
            -
                    socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM)
         | 
| 457 | 
            -
                    socket.connect(Socket.pack_sockaddr_un(@socket_path))
         | 
| 458 | 
            -
                  else
         | 
| 459 | 
            -
                    socket = UDPSocket.new
         | 
| 460 | 
            -
                    socket.connect(@host, @port)
         | 
| 602 | 
            +
                    @connection.write(message)
         | 
| 461 603 | 
             
                  end
         | 
| 462 | 
            -
                  socket
         | 
| 463 | 
            -
                end
         | 
| 464 | 
            -
             | 
| 465 | 
            -
                def sock
         | 
| 466 | 
            -
                  @socket ||= connect_to_socket
         | 
| 467 | 
            -
                end
         | 
| 468 | 
            -
             | 
| 469 | 
            -
                def send_to_socket(message)
         | 
| 470 | 
            -
                  self.class.logger.debug { "Statsd: #{message}" } if self.class.logger
         | 
| 471 | 
            -
                  if @socket_path.nil?
         | 
| 472 | 
            -
                    sock.send(message, 0)
         | 
| 473 | 
            -
                  else
         | 
| 474 | 
            -
                    sock.sendmsg_nonblock(message)
         | 
| 475 | 
            -
                  end
         | 
| 476 | 
            -
                rescue => boom
         | 
| 477 | 
            -
                  if @socket_path && (boom.is_a?(Errno::ECONNREFUSED) ||
         | 
| 478 | 
            -
                                      boom.is_a?(Errno::ECONNRESET) ||
         | 
| 479 | 
            -
                                      boom.is_a?(Errno::ENOENT))
         | 
| 480 | 
            -
                    return @socket = nil
         | 
| 481 | 
            -
                  end
         | 
| 482 | 
            -
                  # Try once to reconnect if the socket has been closed
         | 
| 483 | 
            -
                  retries ||= 1
         | 
| 484 | 
            -
                  if retries <= 1 && boom.is_a?(IOError) && boom.message =~ /closed stream/i
         | 
| 485 | 
            -
                    retries += 1
         | 
| 486 | 
            -
                    begin
         | 
| 487 | 
            -
                      @socket = connect_to_socket
         | 
| 488 | 
            -
                      retry
         | 
| 489 | 
            -
                    rescue => e
         | 
| 490 | 
            -
                      boom = e
         | 
| 491 | 
            -
                    end
         | 
| 492 | 
            -
                  end
         | 
| 493 | 
            -
             | 
| 494 | 
            -
                  self.class.logger.error { "Statsd: #{boom.class} #{boom}" } if self.class.logger
         | 
| 495 | 
            -
                  nil
         | 
| 496 604 | 
             
                end
         | 
| 497 605 | 
             
              end
         | 
| 498 606 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: dogstatsd-ruby
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version:  | 
| 4 | 
            +
              version: 4.5.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Rein Henrichs
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2019-08-22 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies: []
         | 
| 13 13 | 
             
            description: A Ruby DogStastd client
         | 
| 14 14 | 
             
            email: code@datadoghq.com
         | 
| @@ -33,7 +33,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 33 33 | 
             
              requirements:
         | 
| 34 34 | 
             
              - - ">="
         | 
| 35 35 | 
             
                - !ruby/object:Gem::Version
         | 
| 36 | 
            -
                  version:  | 
| 36 | 
            +
                  version: 2.0.0
         | 
| 37 37 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 38 38 | 
             
              requirements:
         | 
| 39 39 | 
             
              - - ">="
         | 
| @@ -41,7 +41,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 41 41 | 
             
                  version: '0'
         | 
| 42 42 | 
             
            requirements: []
         | 
| 43 43 | 
             
            rubyforge_project: 
         | 
| 44 | 
            -
            rubygems_version: 2.7. | 
| 44 | 
            +
            rubygems_version: 2.7.6
         | 
| 45 45 | 
             
            signing_key: 
         | 
| 46 46 | 
             
            specification_version: 4
         | 
| 47 47 | 
             
            summary: A Ruby DogStatsd client
         |