This repository was archived by the owner on Jun 21, 2023. It is now read-only.
forked from reinh/statsd
-
Notifications
You must be signed in to change notification settings - Fork 52
/
Copy pathstatsd.rb
132 lines (112 loc) · 4.12 KB
/
statsd.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
require 'socket'
# = Statsd: A Statsd client (https://github.com/etsy/statsd)
#
# @example Set up a global Statsd client for a server on localhost:9125
# $statsd = Statsd.new 'localhost', 8125
# @example Send some stats
# $statsd.increment 'garets'
# $statsd.timing 'glork', 320
# @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'
class Statsd
class ConfigError < StandardError; end;
# A namespace to prepend to all statsd calls.
attr_accessor :namespace
#characters that will be replaced with _ in stat names
RESERVED_CHARS_REGEX = /[\:\|\@]/
class << self
# Set to any standard logger instance (including stdlib's Logger) to enable
# stat logging using logger.debug
attr_accessor :logger
end
# @param [String] host your statsd host
# @param [Integer] port your statsd port
def initialize(host, port=8125)
@host, @port = host, port
end
# Sends an increment (count = 1) for the given stat to the statsd server.
#
# @param stat (see #count)
# @param sample_rate (see #count)
# @see #count
def increment(stat, sample_rate=1); count stat, 1, sample_rate end
# Sends a decrement (count = -1) for the given stat to the statsd server.
#
# @param stat (see #count)
# @param sample_rate (see #count)
# @see #count
def decrement(stat, sample_rate=1); count stat, -1, sample_rate end
# Sends an arbitrary count for the given stat to the statsd server.
#
# @param [String] stat stat name
# @param [Integer] count count
# @param [Integer] sample_rate sample rate, 1 for always
def count(stat, count, sample_rate=1); send stat, count, 'c', sample_rate end
# Sends an arbitary gauge value for the given stat to the statsd server.
#
# @param [String] stat stat name.
# @param [Numeric] gauge value.
# @example Report the current user count:
# $statsd.gauge('user.count', User.count)
def gauge(stat, value)
send stat, value, 'g'
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 stat stat name
# @param [Integer] ms timing in milliseconds
# @param [Integer] sample_rate sample rate, 1 for always
def timing(stat, ms, sample_rate=1); send stat, ms, 'ms', sample_rate end
# Reports execution time of the provided block using {#timing}.
#
# @param stat (see #timing)
# @param sample_rate (see #timing)
# @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! }
def time(stat, sample_rate=1)
start = Time.now
result = yield
timing(stat, ((Time.now - start) * 1000).round, sample_rate)
result
end
private
def sampled(sample_rate)
yield unless sample_rate < 1 and rand > sample_rate
end
def send(stat, delta, type, sample_rate=1)
sampled(sample_rate) do
prefix = "#{@namespace}." unless @namespace.nil?
stat = stat.to_s.gsub('::', '.').gsub(RESERVED_CHARS_REGEX, '_')
send_to_socket("#{prefix}#{stat}:#{delta}|#{type}#{'|@' << sample_rate.to_s if sample_rate < 1}")
end
end
def send_to_socket(message)
self.class.logger.debug {"Statsd: #{message}"} if self.class.logger
socket.send(message, 0, @host, @port)
rescue => boom
self.class.logger.error {"Statsd: #{boom.class} #{boom}"} if self.class.logger
end
def socket; @socket ||= UDPSocket.new end
def self.setup(options={})
params = [
options[:host] || "localhost",
options[:port]
].compact
@instance = self.new(*params)
end
def self.clear_setup
@instance = nil
end
def self.method_missing(m, *args, &block)
raise ConfigError.new("I'm not setup()") unless @instance
@instance.__send__(m, *args, &block)
end
end