class Statsd

Statsd: A Statsd client (github.com/etsy/statsd)

@example Set up a global Statsd client for a server on localhost:8125

$statsd = Statsd.new 'localhost', 8125

@example Set up a global Statsd client for a server on IPv6 port 8125

$statsd = Statsd.new '::1', 8125

@example Send some stats

$statsd.increment 'garets'
$statsd.timing 'glork', 320
$statsd.gauge 'bork', 100

@example Use {#time} to time the execution of a block

$statsd.time('account.activate') { @account.activate! }

@example Create a namespaced statsd client and increment 'account.activate'

statsd = Statsd.new('localhost').tap{|sd| sd.namespace = 'account'}
statsd.increment 'activate'

Statsd instances are thread safe for general usage, by utilizing the thread safe nature of UDP sends. The attributes are stateful, and are not mutexed, it is expected that users will not change these at runtime in threaded environments. If users require such use cases, it is recommend that users either mutex around their Statsd object, or create separate objects for each namespace / host+port combination.

Attributes

logger[RW]

Set to a standard logger instance to enable debug logging.

batch_byte_size[RW]

The default batch size, in bytes, for new batches (default: default nil; use batch_size)

batch_size[RW]

The default batch size for new batches. Set to nil to use batch_byte_size (default: 10)

delimiter[R]
The replacement of

on ruby module names when transformed to statsd metric names

flush_interval[RW]

The flush interval, in seconds, for new batches (default: nil)

host[R]

StatsD host. Defaults to 127.0.0.1.

namespace[R]

A namespace to prepend to all statsd calls.

port[R]

StatsD port. Defaults to 8125.

postfix[R]

a postfix to append to all metrics

prefix[R]

StatsD namespace prefix, generated from namespace

Public Class Methods

new(host = '127.0.0.1', port = 8125, protocol = :udp) click to toggle source

@param [String] host your statsd host @param [Integer] port your statsd port @param [Symbol] protocol :tcp for TCP, :udp or any other value for UDP

# File lib/statsd.rb, line 294
def initialize(host = '127.0.0.1', port = 8125, protocol = :udp)
  @host = host || '127.0.0.1'
  @port = port || 8125
  self.delimiter = "."
  @prefix = nil
  @batch_size = 10
  @batch_byte_size = nil
  @flush_interval = nil
  @postfix = nil
  @socket = nil
  @protocol = protocol || :udp
  @s_mu = Mutex.new
  connect
end

Public Instance Methods

batch(&block) click to toggle source

Creates and yields a Batch that can be used to batch instrument reports into larger packets. Batches are sent either when the packet is “full” (defined by batch_size), or when the block completes, whichever is the sooner.

@yield [Batch] a statsd subclass that collects and batches instruments @example Batch two instument operations:

$statsd.batch do |batch|
  batch.increment 'sys.requests'
  batch.gauge('user.count', User.count)
end
# File lib/statsd.rb, line 442
def batch(&block)
  Batch.new(self).easy(&block)
end
connect() click to toggle source

Reconnects the socket, useful if the address of the statsd has changed. This method is not thread safe from a perspective of stat submission. It is safe from resource leaks. Users do not normally need to call this, but calling it may be appropriate when reconfiguring a process (e.g. from HUP).

# File lib/statsd.rb, line 450
def connect
  @s_mu.synchronize do
    begin
      @socket.close if @socket
    rescue
      # Errors are ignored on reconnects.
    end

    case @protocol
    when :tcp
      @socket = TCPSocket.new @host, @port
    else
      @socket = UDPSocket.new Addrinfo.ip(@host).afamily
      @socket.connect host, port
    end
  end
end
count(stat, count, sample_rate=1) click to toggle source

Sends an arbitrary count for the given stat to the statsd server.

@param [String] stat stat name @param [Integer] count count @param [Numeric] sample_rate sample rate, 1 for always

# File lib/statsd.rb, line 369
def count(stat, count, sample_rate=1)
  send_stats stat, count, :c, sample_rate
end
decrement(stat, sample_rate=1) click to toggle source

Sends a decrement (count = -1) for the given stat to the statsd server.

@param [String] stat stat name @param [Numeric] sample_rate sample rate, 1 for always @see count

# File lib/statsd.rb, line 360
def decrement(stat, sample_rate=1)
  count stat, -1, sample_rate
end
delimiter=(delimiter) click to toggle source

@attribute [w] stat_delimiter

Allows for custom delimiter replacement for :: when Ruby modules are transformed to statsd metric name
# File lib/statsd.rb, line 342
def delimiter=(delimiter)
  @delimiter = delimiter || "."
end
gauge(stat, value, sample_rate=1) click to toggle source

Sends an arbitary gauge value for the given stat to the statsd server.

This is useful for recording things like available disk space, memory usage, and the like, which have different semantics than counters.

@param [String] stat stat name. @param [Numeric] value gauge value. @param [Numeric] sample_rate sample rate, 1 for always @example Report the current user count:

$statsd.gauge('user.count', User.count)
# File lib/statsd.rb, line 384
def gauge(stat, value, sample_rate=1)
  send_stats stat, value, :g, sample_rate
end
host=(host) click to toggle source

@attribute [w] host

Writes are not thread safe.
Users should call hup after making changes.
# File lib/statsd.rb, line 329
def host=(host)
  @host = host || '127.0.0.1'
end
increment(stat, sample_rate=1) click to toggle source

Sends an increment (count = 1) for the given stat to the statsd server.

@param [String] stat stat name @param [Numeric] sample_rate sample rate, 1 for always @see count

# File lib/statsd.rb, line 351
def increment(stat, sample_rate=1)
  count stat, 1, sample_rate
end
namespace=(namespace) click to toggle source

@attribute [w] namespace

Writes are not thread safe.
# File lib/statsd.rb, line 311
def namespace=(namespace)
  @namespace = namespace
  @prefix = "#{namespace}."
end
port=(port) click to toggle source

@attribute [w] port

Writes are not thread safe.
Users should call hup after making changes.
# File lib/statsd.rb, line 336
def port=(port)
  @port = port || 8125
end
postfix=(pf) click to toggle source

@attribute [w] postfix

A value to be appended to the stat name after a '.'. If the value is
blank then the postfix will be reset to nil (rather than to '.').
# File lib/statsd.rb, line 319
def postfix=(pf)
  case pf
  when nil, false, '' then @postfix = nil
  else @postfix = ".#{pf}"
  end
end
set(stat, value, sample_rate=1) click to toggle source

Sends an arbitary set value for the given stat to the statsd server.

This is for recording counts of unique events, which are useful to see on graphs to correlate to other values. For example, a deployment might get recorded as a set, and be drawn as annotations on a CPU history graph.

@param [String] stat stat name. @param [Numeric] value event value. @param [Numeric] sample_rate sample rate, 1 for always @example Report a deployment happening:

$statsd.set('deployment', DEPLOYMENT_EVENT_CODE)
# File lib/statsd.rb, line 400
def set(stat, value, sample_rate=1)
  send_stats stat, value, :s, sample_rate
end
time(stat, sample_rate=1) { || ... } click to toggle source

Reports execution time of the provided block using {#timing}.

@param [String] stat stat name @param [Numeric] sample_rate sample rate, 1 for always @yield The operation to be timed @see timing @example Report the time (in ms) taken to activate an account

$statsd.time('account.activate') { @account.activate! }
# File lib/statsd.rb, line 424
def time(stat, sample_rate=1)
  start = MonotonicTime.time_in_ms
  result = yield
ensure
  timing(stat, (MonotonicTime.time_in_ms - start).round, sample_rate)
  result
end
timing(stat, ms, sample_rate=1) click to toggle source

Sends a timing (in ms) for the given stat to the statsd server. The sample_rate determines what percentage of the time this report is sent. The statsd server then uses the sample_rate to correctly track the average timing for the stat.

@param [String] stat stat name @param [Integer] ms timing in milliseconds @param [Numeric] sample_rate sample rate, 1 for always

# File lib/statsd.rb, line 412
def timing(stat, ms, sample_rate=1)
  send_stats stat, ms, :ms, sample_rate
end

Protected Instance Methods

send_to_socket(message) click to toggle source
# File lib/statsd.rb, line 470
def send_to_socket(message)
  self.class.logger.debug { "Statsd: #{message}" } if self.class.logger

  retries = 0
  n = 0
  while true
    # send(2) is atomic, however, in stream cases (TCP) the socket is left
    # in an inconsistent state if a partial message is written. If that case
    # occurs, the socket is closed down and we retry on a new socket.
    message = @protocol == :tcp ? message + "\n" : message
    n = socket.write(message) rescue (err = $!; 0)
    if n == message.length
      break
    end

    connect
    retries += 1
    raise (err || "statsd: Failed to send after #{retries} attempts") if retries >= 5
  end
  n
rescue => boom
  self.class.logger.error { "Statsd: #{boom.class} #{boom}" } if self.class.logger
  nil
end

Private Instance Methods

send_stats(stat, delta, type, sample_rate=1) click to toggle source
# File lib/statsd.rb, line 497
def send_stats(stat, delta, type, sample_rate=1)
  if sample_rate == 1 or rand < sample_rate
    # Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
    stat = stat.to_s.gsub('::', delimiter).tr(':|@', '_')
    rate = "|@#{sample_rate}" unless sample_rate == 1
    send_to_socket "#{prefix}#{stat}#{postfix}:#{delta}|#{type}#{rate}"
  end
end
socket() click to toggle source
# File lib/statsd.rb, line 506
def socket
  # Subtle: If the socket is half-way through initialization in connect, it
  # cannot be used yet.
  @s_mu.synchronize { @socket } || raise(ThreadError, "socket missing")
end