class StatsdTcp
StatsdTcp: A StatsdTcp
client (github.com/etsy/statsd)¶ ↑
@example Set up a global StatsdTcp
client for a server on localhost:8125
$statsd = StatsdTcp.new 'localhost', 8125
@example Set up a global StatsdTcp
client for a server on IPv6 port 8125
$statsd = StatsdTcp.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 = StatsdTcp.new('localhost').tap{|sd| sd.namespace = 'account'} statsd.increment 'activate'
StatsdTcp
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 StatsdTcp
object, or create separate objects for each namespace / host+port combination.
Attributes
Set to a standard logger instance to enable debug logging.
The default batch size, in bytes, for new batches (default: default nil; use batch_size
)
The default batch size for new batches. Set to nil to use batch_byte_size
(default: 10)
- The replacement of
-
on ruby module names when transformed to statsd metric names
The flush interval, in seconds, for new batches (default: nil)
StatsD host. Defaults to 127.0.0.1.
A namespace to prepend to all statsd calls.
StatsD port. Defaults to 8125.
a postfix to append to all metrics
StatsD namespace prefix, generated from namespace
Public Class Methods
@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_tcp.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
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_tcp.rb, line 442 def batch(&block) Batch.new(self).easy(&block) end
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_tcp.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
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_tcp.rb, line 369 def count(stat, count, sample_rate=1) send_stats stat, count, :c, sample_rate end
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_tcp.rb, line 360 def decrement(stat, sample_rate=1) count stat, -1, sample_rate end
@attribute [w] stat_delimiter
Allows for custom delimiter replacement for :: when Ruby modules are transformed to statsd metric name
# File lib/statsd_tcp.rb, line 342 def delimiter=(delimiter) @delimiter = delimiter || "." end
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_tcp.rb, line 384 def gauge(stat, value, sample_rate=1) send_stats stat, value, :g, sample_rate end
@attribute [w] host
Writes are not thread safe. Users should call hup after making changes.
# File lib/statsd_tcp.rb, line 329 def host=(host) @host = host || '127.0.0.1' end
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_tcp.rb, line 351 def increment(stat, sample_rate=1) count stat, 1, sample_rate end
@attribute [w] namespace
Writes are not thread safe.
# File lib/statsd_tcp.rb, line 311 def namespace=(namespace) @namespace = namespace @prefix = "#{namespace}." end
@attribute [w] port
Writes are not thread safe. Users should call hup after making changes.
# File lib/statsd_tcp.rb, line 336 def port=(port) @port = port || 8125 end
@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_tcp.rb, line 319 def postfix=(pf) case pf when nil, false, '' then @postfix = nil else @postfix = ".#{pf}" end end
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_tcp.rb, line 400 def set(stat, value, sample_rate=1) send_stats stat, value, :s, sample_rate end
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_tcp.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
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_tcp.rb, line 412 def timing(stat, ms, sample_rate=1) send_stats stat, ms, :ms, sample_rate end
Protected Instance Methods
# File lib/statsd_tcp.rb, line 470 def send_to_socket(message) self.class.logger.debug { "StatsdTcp: #{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. l = @protocol == :tcp ? message.length + 1 : message.length br = @protocol == :tcp ? "\n" : "" n = socket.write(message + br) if n == l break end connect retries += 1 raise "statsd: Failed to send after #{retries} attempts" if retries >= 5 end n rescue => boom self.class.logger.error { "StatsdTcp: #{boom.class} #{boom}" } if self.class.logger nil end
Private Instance Methods
# File lib/statsd_tcp.rb, line 499 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
# File lib/statsd_tcp.rb, line 508 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