1414# statsd = Statsd.new('localhost').tap{|sd| sd.namespace = 'account'}
1515# statsd.increment 'activate'
1616class Statsd
17- class Host
18- attr_reader :ip , :port , :key
19- def initialize ( host , port , key = nil )
20- @ip = Addrinfo . ip ( host ) . ip_address
21- @port = port
17+ class RubyUdpClient
18+ attr_reader :key , :sock
19+
20+ def initialize ( address , port , key = nil )
21+ @sock = UDPSocket . new
22+ @sock . connect ( address , port )
2223 @key = key
2324 end
25+
26+ def send ( msg )
27+ sock . write ( msg )
28+ rescue => boom
29+ nil
30+ end
2431 end
2532
2633 # A namespace to prepend to all statsd calls.
@@ -29,22 +36,19 @@ def initialize(host, port, key = nil)
2936 #characters that will be replaced with _ in stat names
3037 RESERVED_CHARS_REGEX = /[\: \| \@ ]/
3138
32- class << self
33- # Set to any standard logger instance (including stdlib's Logger) to enable
34- # stat logging using logger.debug
35- attr_accessor :logger
39+ def initialize ( client_class = nil )
40+ @shards = [ ]
41+ @client_class = client_class || RubyUdpClient
3642 end
3743
38- # @param [String] host your statsd host
39- # @param [Integer] port your statsd port
40- def initialize ( host , port = 8125 , key = nil )
41- @hosts = [ ]
42- add_host ( host , port , key )
44+ def self . simple ( addr , port = nil )
45+ self . new . add_shard ( addr , port )
4346 end
4447
45- def add_host ( host , port = nil , key = nil )
46- host , port = host . split ( ':' ) if host . include? ( ':' )
47- @hosts << Host . new ( host , port . to_i , key )
48+ def add_shard ( addr , port = nil , key = nil )
49+ addr , port = addr . split ( ':' ) if addr . include? ( ':' )
50+ @shards << @client_class . new ( addr , port . to_i , key )
51+ self
4852 end
4953
5054 # Sends an increment (count = 1) for the given stat to the statsd server.
@@ -120,26 +124,16 @@ def send(stat, delta, type, sample_rate=1)
120124 prefix = "#{ @namespace } ." unless @namespace . nil?
121125 stat = stat . to_s . gsub ( '::' , '.' ) . gsub ( RESERVED_CHARS_REGEX , '_' )
122126 msg = "#{ prefix } #{ stat } :#{ delta } |#{ type } #{ '|@' << sample_rate . to_s if sample_rate < 1 } "
123- send_to_socket ( select_host ( stat ) , msg )
124- end
125- end
126-
127- def send_to_socket ( host , message )
128- self . class . logger . debug { "Statsd: #{ message } " } if self . class . logger
129- if host . key . nil?
130- socket . send ( message , 0 , host . ip , host . port )
131- else
132- socket . send ( signed_payload ( host . key , message ) , 0 , host . ip , host . port )
127+ shard = select_shard ( stat )
128+ shard . send ( shard . key ? signed_payload ( shard . key , msg ) : msg )
133129 end
134- rescue => boom
135- self . class . logger . error { "Statsd: #{ boom . class } #{ boom } " } if self . class . logger
136130 end
137131
138- def select_host ( stat )
139- if @hosts . size == 1
140- @hosts . first
132+ def select_shard ( stat )
133+ if @shards . size == 1
134+ @shards . first
141135 else
142- @hosts [ Zlib . crc32 ( stat ) % @hosts . size ]
136+ @shards [ Zlib . crc32 ( stat ) % @shards . size ]
143137 end
144138 end
145139
@@ -167,6 +161,4 @@ def timestamp
167161 def nonce
168162 SecureRandom . random_bytes ( 4 )
169163 end
170-
171- def socket ; @socket ||= UDPSocket . new end
172164end
0 commit comments