statsdserver 0.4
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.
- data/bin/statsd +58 -0
- data/lib/statsdserver.rb +216 -0
- data/lib/statsdserver/input/udp.rb +28 -0
- data/lib/statsdserver/input/zeromq.rb +51 -0
- data/lib/statsdserver/output/stdout.rb +16 -0
- data/lib/statsdserver/output/tcp.rb +33 -0
- data/lib/statsdserver/proto/parseerror.rb +6 -0
- data/lib/statsdserver/proto/v1.rb +64 -0
- data/lib/statsdserver/stats.rb +16 -0
- metadata +168 -0
data/bin/statsd
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "bundler/setup"
|
6
|
+
|
7
|
+
require "parseconfig"
|
8
|
+
require "statsdserver"
|
9
|
+
require "statsdserver/output/stdout"
|
10
|
+
require "statsdserver/output/tcp"
|
11
|
+
require "sysexits"
|
12
|
+
|
13
|
+
include Sysexits
|
14
|
+
progname = File.basename($0)
|
15
|
+
|
16
|
+
if ARGV.length != 1
|
17
|
+
$stderr.puts "usage: #{progname} path_to_config"
|
18
|
+
exit EX_USAGE
|
19
|
+
end
|
20
|
+
|
21
|
+
config_path = ARGV.shift
|
22
|
+
begin
|
23
|
+
config_file = ParseConfig.new(config_path)
|
24
|
+
rescue
|
25
|
+
$stderr.puts "#{progname}: error reading #{config_path}: #{$!}"
|
26
|
+
exit EX_DATAERR
|
27
|
+
end
|
28
|
+
|
29
|
+
config = {}
|
30
|
+
%w(daemonize inputs flush_interval outputs prefix percentile
|
31
|
+
suffix).each do |key|
|
32
|
+
config[key.to_sym] = config_file[key] if config_file[key]
|
33
|
+
end
|
34
|
+
|
35
|
+
if config[:inputs].nil? || config[:inputs].empty?
|
36
|
+
$stderr.puts "#{progname}: no inputs specified"
|
37
|
+
exit EX_DATAERR
|
38
|
+
end
|
39
|
+
|
40
|
+
if config[:outputs].nil? || config[:outputs].empty?
|
41
|
+
$stderr.puts "#{progname}: no outputs specified"
|
42
|
+
exit EX_DATAERR
|
43
|
+
end
|
44
|
+
|
45
|
+
input_config = {}
|
46
|
+
config[:inputs].split(/, */).each do |input|
|
47
|
+
input_config[input] = config_file["input:#{input}"] || {}
|
48
|
+
end
|
49
|
+
|
50
|
+
output_config = {}
|
51
|
+
config[:outputs].split(/, */).each do |output|
|
52
|
+
output_config[output] = config_file["output:#{output}"] || {}
|
53
|
+
end
|
54
|
+
|
55
|
+
EM.run do
|
56
|
+
s = StatsdServer.new(config, input_config, output_config)
|
57
|
+
s.run
|
58
|
+
end
|
data/lib/statsdserver.rb
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
require "logger"
|
2
|
+
require "statsdserver/input/udp"
|
3
|
+
require "statsdserver/input/zeromq"
|
4
|
+
require "statsdserver/stats"
|
5
|
+
|
6
|
+
# Hack because the latest amqp gem uses String#bytesize, and not everyone
|
7
|
+
# is running ruby 1.8.7.
|
8
|
+
if !String.instance_methods.include?(:bytesize)
|
9
|
+
class String
|
10
|
+
alias :bytesize :length
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class StatsdServer
|
15
|
+
attr_accessor :logger
|
16
|
+
attr_accessor :stats
|
17
|
+
|
18
|
+
public
|
19
|
+
def initialize(opts, input_config, output_config)
|
20
|
+
@stats = StatsdServer::Stats.new
|
21
|
+
@logger = Logger.new(STDERR)
|
22
|
+
@logger.progname = File.basename($0)
|
23
|
+
|
24
|
+
@opts = {
|
25
|
+
:bind => "127.0.0.1",
|
26
|
+
:port => 8125,
|
27
|
+
:percentile => 90,
|
28
|
+
:flush_interval => 30,
|
29
|
+
:prefix => "stats"
|
30
|
+
}.merge(opts)
|
31
|
+
@input_config = input_config
|
32
|
+
@output_config = output_config
|
33
|
+
|
34
|
+
# argument checking
|
35
|
+
[:port, :percentile, :flush_interval].each do |key|
|
36
|
+
begin
|
37
|
+
@opts[key] = Float(@opts[key])
|
38
|
+
rescue
|
39
|
+
raise "#{key}: #{@opts[key].inspect}: must be a valid number"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end # def initialize
|
43
|
+
|
44
|
+
public
|
45
|
+
def run
|
46
|
+
# initialize outputs
|
47
|
+
@outputs = []
|
48
|
+
@output_config.each do |output, config|
|
49
|
+
klass = StatsdServer::Output.const_get(output.capitalize)
|
50
|
+
if klass.nil?
|
51
|
+
@logger.fatal("unknown output #{output.inspect}")
|
52
|
+
exit EX_DATAERR
|
53
|
+
end
|
54
|
+
@outputs << klass.new(config)
|
55
|
+
end # @output_config.each
|
56
|
+
|
57
|
+
# start inputs
|
58
|
+
@input_config.each do |input, config|
|
59
|
+
case input
|
60
|
+
when "udp"
|
61
|
+
EM.open_datagram_socket(config["bind"], config["port"].to_i,
|
62
|
+
Input::Udp) do |s|
|
63
|
+
s.logger = @logger
|
64
|
+
s.stats = @stats
|
65
|
+
end # EM.open_datagram_socket
|
66
|
+
when "zeromq"
|
67
|
+
s = Input::ZeroMQ.new
|
68
|
+
s.logger = @logger
|
69
|
+
s.stats = @stats
|
70
|
+
$ctx = EM::ZeroMQ::Context.new(1)
|
71
|
+
sock = $ctx.socket(ZMQ::PULL, s)
|
72
|
+
sock.setsockopt(ZMQ::HWM, 100)
|
73
|
+
sock.bind(config["bind"])
|
74
|
+
else
|
75
|
+
@logger.fatal("unknown input #{input.inspect}")
|
76
|
+
exit EX_DATAERR
|
77
|
+
end # case input
|
78
|
+
end # @inputs.each
|
79
|
+
|
80
|
+
# start flusher
|
81
|
+
Thread.abort_on_exception = true
|
82
|
+
@flusher = Thread.new do
|
83
|
+
while sleep(@opts[:flush_interval])
|
84
|
+
begin
|
85
|
+
flush
|
86
|
+
rescue => e
|
87
|
+
@logger.warn("trouble flushing: #{$!}")
|
88
|
+
@logger.debug(e.backtrace.join("\n"))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end # def run
|
93
|
+
|
94
|
+
#private
|
95
|
+
#def setup_amqp
|
96
|
+
# begin
|
97
|
+
# require "amqp"
|
98
|
+
# rescue LoadError
|
99
|
+
# @logger.fatal("missing amqp ruby module. try gem install amqp")
|
100
|
+
# exit(1)
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# user = @output_url.user || ""
|
104
|
+
# user, vhost = user.split("@", 2)
|
105
|
+
# _, mqtype, mqname = @output_url.path.split("/", 3)
|
106
|
+
# amqp_settings = {
|
107
|
+
# :host => @output_url.host,
|
108
|
+
# :port => @output_url.port || 5672,
|
109
|
+
# :user => user,
|
110
|
+
# :pass => @output_url.password,
|
111
|
+
# :vhost => vhost || "/",
|
112
|
+
# }
|
113
|
+
#
|
114
|
+
#
|
115
|
+
# @amqp = AMQP.connect(amqp_settings)
|
116
|
+
# @channel = AMQP::Channel.new(@amqp)
|
117
|
+
#
|
118
|
+
# opts = {
|
119
|
+
# :durable => true,
|
120
|
+
# :auto_delete => false,
|
121
|
+
# }
|
122
|
+
#
|
123
|
+
# if @output_url.query
|
124
|
+
# @output_url.query.split("&").each do |param|
|
125
|
+
# k, v = param.split("=", 2)
|
126
|
+
# opts[:durable] = false if k == "durable" and v == "false"
|
127
|
+
# opts[:auto_delete] = true if k == "autodelete" and v == "true"
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# @exchange = case mqtype
|
132
|
+
# when "fanout"
|
133
|
+
# @channel.fanout(mqname, opts)
|
134
|
+
# when "direct"
|
135
|
+
# @channel.exchange(mqname, opts)
|
136
|
+
# when "topic"
|
137
|
+
# @channel.topic(mqname, opts)
|
138
|
+
# else
|
139
|
+
# raise TypeError, "unknown amqp output type #{mqtype}"
|
140
|
+
# end
|
141
|
+
# end # def setup_amqp
|
142
|
+
|
143
|
+
public
|
144
|
+
def carbon_update_str
|
145
|
+
updates = []
|
146
|
+
now = Time.now.to_i
|
147
|
+
|
148
|
+
timers = {}
|
149
|
+
@stats.timers.each do |k, v|
|
150
|
+
timers[k] = @stats.timers.delete(k)
|
151
|
+
end
|
152
|
+
|
153
|
+
counters = {}
|
154
|
+
@stats.counters.each do |k, v|
|
155
|
+
counters[k] = @stats.counters.delete(k)
|
156
|
+
end
|
157
|
+
|
158
|
+
timers.each do |key, values|
|
159
|
+
next if values.length == 0
|
160
|
+
values.sort!
|
161
|
+
|
162
|
+
# do some basic summarizing of our timer data
|
163
|
+
min = values[0]
|
164
|
+
max = values[-1]
|
165
|
+
mean = min
|
166
|
+
maxAtThreshold = min
|
167
|
+
if values.length > 1
|
168
|
+
threshold_index = ((100 - @opts[:percentile]) / 100.0) \
|
169
|
+
* values.length
|
170
|
+
threshold_count = values.length - threshold_index.round
|
171
|
+
valid_values = values.slice(0, threshold_count)
|
172
|
+
maxAtThreshold = valid_values[-1]
|
173
|
+
sum = 0
|
174
|
+
valid_values.each { |v| sum += v }
|
175
|
+
mean = sum / valid_values.length
|
176
|
+
end
|
177
|
+
|
178
|
+
prefix = @opts[:prefix] ? "#{@opts[:prefix]}." : ""
|
179
|
+
suffix = @opts[:suffix] ? ".#{@opts[:suffix]}" : ""
|
180
|
+
updates << "#{prefix}timers.#{key}.mean#{suffix} #{mean} #{now}"
|
181
|
+
updates << "#{prefix}timers.#{key}.upper#{suffix} #{max} #{now}"
|
182
|
+
updates << "#{prefix}timers.#{key}.upper_#{@opts[:percentile]}#{suffix} " \
|
183
|
+
"#{maxAtThreshold} #{now}"
|
184
|
+
updates << "#{prefix}timers.#{key}.lower#{suffix} #{min} #{now}"
|
185
|
+
updates << "#{prefix}timers.#{key}.count#{suffix} #{values.length} #{now}"
|
186
|
+
end # timers.each
|
187
|
+
|
188
|
+
# Keep sending a 0 for counters (even if we don't get updates)
|
189
|
+
counters.keys.each do |k|
|
190
|
+
@stats.counters[k] ||= 0 # Keep sending a 0 if we don't get updates
|
191
|
+
end
|
192
|
+
|
193
|
+
counters.each do |key, value|
|
194
|
+
prefix = @opts[:prefix] ? "#{@opts[:prefix]}." : ""
|
195
|
+
suffix = @opts[:suffix] ? ".#{@opts[:suffix]}" : ""
|
196
|
+
updates << "#{prefix}#{key}#{suffix} #{value / @opts[:flush_interval]} #{now}"
|
197
|
+
end # counters.each
|
198
|
+
|
199
|
+
return updates.length == 0 ? nil : updates.join("\n") + "\n"
|
200
|
+
end # def carbon_update_str
|
201
|
+
|
202
|
+
public
|
203
|
+
def flush
|
204
|
+
s = carbon_update_str
|
205
|
+
return unless s
|
206
|
+
|
207
|
+
if @outputs.nil? or @outputs.length == 0
|
208
|
+
@logger.warn("no outputs configured, can't flush data")
|
209
|
+
return
|
210
|
+
end
|
211
|
+
|
212
|
+
@outputs.each do |output|
|
213
|
+
output.send(s)
|
214
|
+
end
|
215
|
+
end # def flush
|
216
|
+
end # class StatsdServer
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
require "logger"
|
3
|
+
require "statsdserver/proto/v1"
|
4
|
+
|
5
|
+
class StatsdServer
|
6
|
+
class Input
|
7
|
+
class Udp < EventMachine::Connection
|
8
|
+
attr_accessor :logger,
|
9
|
+
:stats
|
10
|
+
|
11
|
+
public
|
12
|
+
def initialize
|
13
|
+
@logger = Logger.new(STDOUT)
|
14
|
+
end
|
15
|
+
|
16
|
+
public
|
17
|
+
def receive_data(packet)
|
18
|
+
raise "@stats must be set" unless @stats
|
19
|
+
|
20
|
+
begin
|
21
|
+
StatsdServer::Proto::V1.parse(packet, @stats)
|
22
|
+
rescue StatsdServer::Proto::ParseError => e
|
23
|
+
@logger.warn(e.message)
|
24
|
+
end
|
25
|
+
end # def receive_data
|
26
|
+
end # class Ucp
|
27
|
+
end # class Input
|
28
|
+
end # class StatsdServer
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "em-zeromq"
|
2
|
+
require "logger"
|
3
|
+
require "statsdserver/proto/v1"
|
4
|
+
|
5
|
+
class StatsdServer
|
6
|
+
class Input
|
7
|
+
class ZeroMQ
|
8
|
+
attr_accessor :logger,
|
9
|
+
:stats
|
10
|
+
|
11
|
+
public
|
12
|
+
def initialize
|
13
|
+
@logger = Logger.new(STDOUT)
|
14
|
+
end
|
15
|
+
|
16
|
+
public
|
17
|
+
def on_readable(socket, parts)
|
18
|
+
parts.each do |part|
|
19
|
+
str = part.copy_out_string
|
20
|
+
receive_data(str)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
public
|
25
|
+
def receive_data(packet)
|
26
|
+
raise "@stats must be set" unless @stats
|
27
|
+
|
28
|
+
sep = packet.index(";")
|
29
|
+
if sep.nil?
|
30
|
+
@logger.warn("received unversioned update: #{packet}")
|
31
|
+
return
|
32
|
+
end
|
33
|
+
|
34
|
+
proto_ver = packet[0 .. sep - 1]
|
35
|
+
payload = packet[sep + 1 .. -1]
|
36
|
+
case proto_ver
|
37
|
+
when "1"
|
38
|
+
begin
|
39
|
+
StatsdServer::Proto::V1.parse(payload, @stats)
|
40
|
+
rescue StatsdServer::Proto::ParseError => e
|
41
|
+
@logger.warn(e.message)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
@logger.warn("unknown protocol version #{proto_ver} in update #{packet}")
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
end # def receive_data
|
49
|
+
end # class Ucp
|
50
|
+
end # class Input
|
51
|
+
end # class StatsdServer
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class StatsdServer::Output
|
2
|
+
class Stdout
|
3
|
+
attr_accessor :logger
|
4
|
+
|
5
|
+
public
|
6
|
+
def initialize(opts = {})
|
7
|
+
@logger = Logger.new(STDERR)
|
8
|
+
$stdout.sync = true # autoflush
|
9
|
+
end
|
10
|
+
|
11
|
+
public
|
12
|
+
def send(str)
|
13
|
+
puts str
|
14
|
+
end
|
15
|
+
end # class Stdout
|
16
|
+
end # class StatsdServer::Output
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "logger"
|
2
|
+
require "socket"
|
3
|
+
|
4
|
+
class StatsdServer::Output
|
5
|
+
class Tcp
|
6
|
+
attr_accessor :logger
|
7
|
+
|
8
|
+
public
|
9
|
+
def initialize(opts = {})
|
10
|
+
if opts["host"].nil?
|
11
|
+
raise ArgumentError, "missing host in [output:tcp] config section"
|
12
|
+
end
|
13
|
+
|
14
|
+
if opts["port"].nil?
|
15
|
+
raise ArgumentError, "missing port in [output:tcp] config section"
|
16
|
+
end
|
17
|
+
|
18
|
+
@opts = opts
|
19
|
+
@logger = Logger.new(STDOUT)
|
20
|
+
end
|
21
|
+
|
22
|
+
public
|
23
|
+
def send(str)
|
24
|
+
@socket ||= connect
|
25
|
+
@socket.write("#{str}\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def connect
|
30
|
+
TCPSocket.new(@opts["host"], @opts["port"].to_i)
|
31
|
+
end
|
32
|
+
end # class Tcp
|
33
|
+
end # class StatsdServer::Output
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "statsdserver/proto/parseerror"
|
2
|
+
|
3
|
+
class StatsdServer
|
4
|
+
class Proto
|
5
|
+
module V1
|
6
|
+
def self.parse(data, stats)
|
7
|
+
data.split("\n").each do |update|
|
8
|
+
self.parse_update(update, stats)
|
9
|
+
end
|
10
|
+
end # def parse
|
11
|
+
|
12
|
+
def self.parse_update(update, stats)
|
13
|
+
bits = update.split(":")
|
14
|
+
# TODO: optimize into single regexp & compile?
|
15
|
+
key = bits.shift.gsub(/\s+/, "_") \
|
16
|
+
.gsub(/\//, "-") \
|
17
|
+
.gsub(/[^a-zA-Z_\-0-9\.]/, "")
|
18
|
+
bits << "1" if bits.length == 0
|
19
|
+
bits.each do |bit|
|
20
|
+
fields = bit.split("|")
|
21
|
+
if fields.length != 2
|
22
|
+
raise ParseError, "invalid update: #{bit}"
|
23
|
+
end
|
24
|
+
|
25
|
+
if fields[1] == "ms" # timer update
|
26
|
+
if fields[0].index(",")
|
27
|
+
fields[0].split(",").each do |value_str|
|
28
|
+
value = Integer(value_str) rescue nil
|
29
|
+
stats.timers[key] << value if value
|
30
|
+
end
|
31
|
+
else
|
32
|
+
value = Integer(fields[0]) rescue nil
|
33
|
+
if value.nil?
|
34
|
+
raise ParseError, "invalid timer value: #{fields[0]}"
|
35
|
+
end
|
36
|
+
stats.timers[key] << fields[0].to_i
|
37
|
+
end
|
38
|
+
elsif fields[1] == "c" # counter update
|
39
|
+
count_str, sample_rate_str = fields[0].split("@", 2)
|
40
|
+
|
41
|
+
if sample_rate_str
|
42
|
+
sample_rate = Float(sample_rate_str) rescue nil
|
43
|
+
if sample_rate.nil?
|
44
|
+
raise ParseError, "invalid sample_rate: #{sample_rate_str}"
|
45
|
+
end
|
46
|
+
else
|
47
|
+
sample_rate = 1
|
48
|
+
end
|
49
|
+
|
50
|
+
count = Integer(count_str) rescue nil
|
51
|
+
if count.nil?
|
52
|
+
raise ParseError, "invalid count: #{count_str}"
|
53
|
+
end
|
54
|
+
|
55
|
+
stats.counters[key] += count.to_i * (1 / sample_rate.to_f)
|
56
|
+
else
|
57
|
+
raise ParseError,
|
58
|
+
"invalid update: #{update}: unknown type #{fields[1]}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end # def parse_update
|
62
|
+
end # module V1
|
63
|
+
end # class Proto
|
64
|
+
end # class StatsdServer
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
class StatsdServer
|
4
|
+
class Stats
|
5
|
+
attr_accessor :counters,
|
6
|
+
:timers,
|
7
|
+
:logger
|
8
|
+
|
9
|
+
public
|
10
|
+
def initialize
|
11
|
+
@timers = Hash.new { |h, k| h[k] = Array.new }
|
12
|
+
@counters = Hash.new { |h, k| h[k] = 0 }
|
13
|
+
@logger = Logger.new(STDERR)
|
14
|
+
end
|
15
|
+
end # class Stats
|
16
|
+
end # class StatsdServer
|
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: statsdserver
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.4'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Pete Fritchman
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: amqp
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: eventmachine
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: parseconfig
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: sysexits
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: em-zeromq
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: collect and aggregate stats, flush to graphite
|
127
|
+
email: petef@databits.net
|
128
|
+
executables:
|
129
|
+
- statsd
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- lib/statsdserver/output/tcp.rb
|
134
|
+
- lib/statsdserver/output/stdout.rb
|
135
|
+
- lib/statsdserver/input/zeromq.rb
|
136
|
+
- lib/statsdserver/input/udp.rb
|
137
|
+
- lib/statsdserver/proto/v1.rb
|
138
|
+
- lib/statsdserver/proto/parseerror.rb
|
139
|
+
- lib/statsdserver/stats.rb
|
140
|
+
- lib/statsdserver.rb
|
141
|
+
- bin/statsd
|
142
|
+
homepage: https://github.com/fetep/ruby-statsd
|
143
|
+
licenses:
|
144
|
+
- Apache License 2.0
|
145
|
+
post_install_message:
|
146
|
+
rdoc_options: []
|
147
|
+
require_paths:
|
148
|
+
- lib
|
149
|
+
- lib
|
150
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
151
|
+
none: false
|
152
|
+
requirements:
|
153
|
+
- - ! '>='
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
|
+
none: false
|
158
|
+
requirements:
|
159
|
+
- - ! '>='
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '0'
|
162
|
+
requirements: []
|
163
|
+
rubyforge_project:
|
164
|
+
rubygems_version: 1.8.24
|
165
|
+
signing_key:
|
166
|
+
specification_version: 3
|
167
|
+
summary: statsd (server) -- stat collector/aggregator
|
168
|
+
test_files: []
|