statsdserver 0.4 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/statsd +17 -5
- data/lib/statsdserver.rb +44 -37
- data/lib/statsdserver/input/zeromq.rb +10 -1
- data/lib/statsdserver/math.rb +29 -0
- data/lib/statsdserver/output/amqp.rb +60 -0
- data/lib/statsdserver/output/tcp.rb +8 -1
- data/lib/statsdserver/proto/v1.rb +1 -1
- metadata +27 -9
data/bin/statsd
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
$: << File.join(File.dirname(__FILE__), "..", "lib")
|
3
4
|
|
4
5
|
require "rubygems"
|
5
|
-
require "bundler/setup"
|
6
6
|
|
7
|
+
require "daemons"
|
7
8
|
require "parseconfig"
|
8
9
|
require "statsdserver"
|
9
|
-
require "statsdserver/output/
|
10
|
+
require "statsdserver/output/amqp"
|
10
11
|
require "statsdserver/output/tcp"
|
12
|
+
require "statsdserver/output/stdout"
|
11
13
|
require "sysexits"
|
12
14
|
|
13
15
|
include Sysexits
|
@@ -28,7 +30,7 @@ end
|
|
28
30
|
|
29
31
|
config = {}
|
30
32
|
%w(daemonize inputs flush_interval outputs prefix percentile
|
31
|
-
suffix).each do |key|
|
33
|
+
suffix preserve_counters).each do |key|
|
32
34
|
config[key.to_sym] = config_file[key] if config_file[key]
|
33
35
|
end
|
34
36
|
|
@@ -42,16 +44,26 @@ if config[:outputs].nil? || config[:outputs].empty?
|
|
42
44
|
exit EX_DATAERR
|
43
45
|
end
|
44
46
|
|
47
|
+
config[:inputs] = config[:inputs].split(/, */)
|
45
48
|
input_config = {}
|
46
|
-
config[:inputs].
|
49
|
+
config[:inputs].each do |input|
|
47
50
|
input_config[input] = config_file["input:#{input}"] || {}
|
48
51
|
end
|
49
52
|
|
53
|
+
config[:outputs] = config[:outputs].split(/, */)
|
50
54
|
output_config = {}
|
51
|
-
config[:outputs].
|
55
|
+
config[:outputs].each do |output|
|
52
56
|
output_config[output] = config_file["output:#{output}"] || {}
|
53
57
|
end
|
54
58
|
|
59
|
+
if config_file["daemonize"] == "true"
|
60
|
+
if config[:outputs].include?("stdout")
|
61
|
+
$stderr.puts "#{progname}: output stdout is not compatible with daemonize"
|
62
|
+
exit EX_DATAERR
|
63
|
+
end
|
64
|
+
Daemons.daemonize(:app_name => progname)
|
65
|
+
end
|
66
|
+
|
55
67
|
EM.run do
|
56
68
|
s = StatsdServer.new(config, input_config, output_config)
|
57
69
|
s.run
|
data/lib/statsdserver.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "logger"
|
2
2
|
require "statsdserver/input/udp"
|
3
3
|
require "statsdserver/input/zeromq"
|
4
|
+
require "statsdserver/math"
|
4
5
|
require "statsdserver/stats"
|
5
6
|
|
6
7
|
# Hack because the latest amqp gem uses String#bytesize, and not everyone
|
@@ -26,7 +27,8 @@ class StatsdServer
|
|
26
27
|
:port => 8125,
|
27
28
|
:percentile => 90,
|
28
29
|
:flush_interval => 30,
|
29
|
-
:prefix => "stats"
|
30
|
+
:prefix => "stats",
|
31
|
+
:preserve_counters => "true",
|
30
32
|
}.merge(opts)
|
31
33
|
@input_config = input_config
|
32
34
|
@output_config = output_config
|
@@ -140,60 +142,65 @@ class StatsdServer
|
|
140
142
|
# end
|
141
143
|
# end # def setup_amqp
|
142
144
|
|
145
|
+
private
|
146
|
+
def metric_name(name)
|
147
|
+
if @opts[:prefix] && !@opts[:prefix].empty?
|
148
|
+
prefix = @opts[:prefix] + "."
|
149
|
+
else
|
150
|
+
prefix = ""
|
151
|
+
end
|
152
|
+
|
153
|
+
if @opts[:suffix] && !@opts[:suffix].empty?
|
154
|
+
suffix = @opts[:suffix] + "."
|
155
|
+
else
|
156
|
+
suffix = ""
|
157
|
+
end
|
158
|
+
|
159
|
+
return [prefix, name, suffix].join("")
|
160
|
+
end
|
161
|
+
|
143
162
|
public
|
144
163
|
def carbon_update_str
|
145
164
|
updates = []
|
146
165
|
now = Time.now.to_i
|
147
166
|
|
148
167
|
timers = {}
|
149
|
-
@stats.timers.each do |k
|
168
|
+
@stats.timers.keys.each do |k|
|
150
169
|
timers[k] = @stats.timers.delete(k)
|
151
170
|
end
|
152
171
|
|
153
172
|
counters = {}
|
154
|
-
@stats.counters.each do |k
|
173
|
+
@stats.counters.keys.each do |k|
|
155
174
|
counters[k] = @stats.counters.delete(k)
|
156
175
|
end
|
157
176
|
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
177
|
+
if @opts[:preserve_counters] == "true"
|
178
|
+
# Keep sending a 0 for counters (even if we don't get updates)
|
179
|
+
counters.keys.each do |k|
|
180
|
+
@stats.counters[k] ||= 0 # Keep sending a 0 if we don't get updates
|
176
181
|
end
|
182
|
+
end
|
177
183
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
updates << "
|
183
|
-
|
184
|
-
updates << "
|
185
|
-
|
184
|
+
timers.each do |key, values|
|
185
|
+
next if values.length == 0
|
186
|
+
summary = ::StatsdServer::Math.summarize(values, @opts)
|
187
|
+
|
188
|
+
updates << [metric_name("timers.#{key}.mean"),
|
189
|
+
summary[:mean], now].join(" ")
|
190
|
+
updates << [metric_name("timers.#{key}.upper"),
|
191
|
+
summary[:upper], now].join(" ")
|
192
|
+
updates << [metric_name("timers.#{key}.lower"),
|
193
|
+
summary[:lower], now].join(" ")
|
194
|
+
updates << [metric_name("timers.#{key}.count"),
|
195
|
+
values.length, now].join(" ")
|
196
|
+
updates << [metric_name("timers.#{key}.upper_#{@opts[:percentile]}"),
|
197
|
+
summary[:max_at_threshold], now].join(" ")
|
186
198
|
end # timers.each
|
187
199
|
|
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
200
|
counters.each do |key, value|
|
194
|
-
|
195
|
-
|
196
|
-
|
201
|
+
updates << [metric_name(key),
|
202
|
+
value / @opts[:flush_interval],
|
203
|
+
now].join(" ")
|
197
204
|
end # counters.each
|
198
205
|
|
199
206
|
return updates.length == 0 ? nil : updates.join("\n") + "\n"
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require "em-zeromq"
|
2
1
|
require "logger"
|
3
2
|
require "statsdserver/proto/v1"
|
4
3
|
|
@@ -10,6 +9,16 @@ class StatsdServer
|
|
10
9
|
|
11
10
|
public
|
12
11
|
def initialize
|
12
|
+
begin
|
13
|
+
require "em-zeromq"
|
14
|
+
rescue LoadError => e
|
15
|
+
raise unless e.message =~ /em-zeromq/
|
16
|
+
new_e = \
|
17
|
+
e.exception("Please install the em-zeromq gem for ZeroMQ input.")
|
18
|
+
new_e.set_backtrace(e.backtrace)
|
19
|
+
raise new_e
|
20
|
+
end
|
21
|
+
|
13
22
|
@logger = Logger.new(STDOUT)
|
14
23
|
end
|
15
24
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
class StatsdServer
|
4
|
+
module Math
|
5
|
+
def self.summarize(values, opts = {})
|
6
|
+
opts = {
|
7
|
+
:percentile => 90,
|
8
|
+
}.merge(opts)
|
9
|
+
res = {}
|
10
|
+
|
11
|
+
values.sort!
|
12
|
+
res[:min] = values[0]
|
13
|
+
res[:max] = values[-1]
|
14
|
+
res[:mean] = res[:min]
|
15
|
+
res[:max_at_threshold] = res[:min]
|
16
|
+
if values.length > 1
|
17
|
+
threshold_index = ((100 - opts[:percentile]) / 100.0) * values.length
|
18
|
+
threshold_count = values.length - threshold_index.round
|
19
|
+
valid_values = values.slice(0, threshold_count)
|
20
|
+
res[:max_at_threshold] = valid_values[-1]
|
21
|
+
sum = 0
|
22
|
+
valid_values.each { |v| sum += v }
|
23
|
+
res[:mean] = sum / valid_values.length
|
24
|
+
end
|
25
|
+
|
26
|
+
return res
|
27
|
+
end
|
28
|
+
end # class Math
|
29
|
+
end # class StatsdServer
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
class StatsdServer::Output
|
4
|
+
class Amqp
|
5
|
+
attr_accessor :logger
|
6
|
+
|
7
|
+
public
|
8
|
+
def initialize(opts = {})
|
9
|
+
begin
|
10
|
+
require "bunny"
|
11
|
+
rescue LoadError => e
|
12
|
+
raise unless e.message =~ /bunny/
|
13
|
+
new_e = e.exception("Please install the bunny gem for AMQP output.")
|
14
|
+
new_e.set_backtrace(e.backtrace)
|
15
|
+
raise new_e
|
16
|
+
end
|
17
|
+
|
18
|
+
if opts["exchange_type"].nil?
|
19
|
+
raise ArgumentError, "missing host in [output:tcp] config section"
|
20
|
+
end
|
21
|
+
|
22
|
+
if opts["exchange_name"].nil?
|
23
|
+
raise ArgumentError, "missing port in [output:tcp] config section"
|
24
|
+
end
|
25
|
+
|
26
|
+
@opts = opts
|
27
|
+
@logger = Logger.new(STDOUT)
|
28
|
+
end
|
29
|
+
|
30
|
+
public
|
31
|
+
def send(str)
|
32
|
+
if @bunny.nil?
|
33
|
+
@bunny, @exchange = connect
|
34
|
+
end
|
35
|
+
|
36
|
+
begin
|
37
|
+
@exchange.publish(str)
|
38
|
+
rescue => e
|
39
|
+
@bunny.close_connection rescue nil
|
40
|
+
@bunny = nil
|
41
|
+
raise
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def connect
|
47
|
+
bunny = Bunny.new(@opts)
|
48
|
+
bunny.start
|
49
|
+
|
50
|
+
exchange = bunny.exchange(
|
51
|
+
@opts["exchange_name"],
|
52
|
+
:type => @opts["exchange_type"].to_sym,
|
53
|
+
:durable => @opts["exchange_durable"] == "true" ? true : false,
|
54
|
+
:auto_delete => @opts["exchange_auto_delete"] == "true" ? true : false,
|
55
|
+
)
|
56
|
+
|
57
|
+
return bunny, exchange
|
58
|
+
end
|
59
|
+
end # class Amqp
|
60
|
+
end # class StatsdServer::Output
|
@@ -22,7 +22,14 @@ class StatsdServer::Output
|
|
22
22
|
public
|
23
23
|
def send(str)
|
24
24
|
@socket ||= connect
|
25
|
-
|
25
|
+
begin
|
26
|
+
@socket.write("#{str}")
|
27
|
+
rescue => e
|
28
|
+
# set @socket to nil to force a re-connect, then pass up the exception
|
29
|
+
@socket.close rescue nil
|
30
|
+
@socket = nil
|
31
|
+
raise
|
32
|
+
end
|
26
33
|
end
|
27
34
|
|
28
35
|
private
|
@@ -22,7 +22,7 @@ class StatsdServer
|
|
22
22
|
raise ParseError, "invalid update: #{bit}"
|
23
23
|
end
|
24
24
|
|
25
|
-
if fields[1] == "ms" # timer update
|
25
|
+
if fields[1] == "ms" or fields[1] == "t" # timer update
|
26
26
|
if fields[0].index(",")
|
27
27
|
fields[0].split(",").each do |value_str|
|
28
28
|
value = Integer(value_str) rescue nil
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: statsdserver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.5'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -44,7 +44,7 @@ dependencies:
|
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
47
|
+
name: daemons
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
49
49
|
none: false
|
50
50
|
requirements:
|
@@ -107,6 +107,22 @@ dependencies:
|
|
107
107
|
- - ! '>='
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: bunny
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
110
126
|
- !ruby/object:Gem::Dependency
|
111
127
|
name: em-zeromq
|
112
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,7 +131,7 @@ dependencies:
|
|
115
131
|
- - ! '>='
|
116
132
|
- !ruby/object:Gem::Version
|
117
133
|
version: '0'
|
118
|
-
type: :
|
134
|
+
type: :development
|
119
135
|
prerelease: false
|
120
136
|
version_requirements: !ruby/object:Gem::Requirement
|
121
137
|
none: false
|
@@ -130,13 +146,15 @@ executables:
|
|
130
146
|
extensions: []
|
131
147
|
extra_rdoc_files: []
|
132
148
|
files:
|
133
|
-
- lib/statsdserver/
|
134
|
-
- lib/statsdserver/output/stdout.rb
|
135
|
-
- lib/statsdserver/input/zeromq.rb
|
149
|
+
- lib/statsdserver/math.rb
|
136
150
|
- lib/statsdserver/input/udp.rb
|
137
|
-
- lib/statsdserver/
|
138
|
-
- lib/statsdserver/
|
151
|
+
- lib/statsdserver/input/zeromq.rb
|
152
|
+
- lib/statsdserver/output/stdout.rb
|
153
|
+
- lib/statsdserver/output/tcp.rb
|
154
|
+
- lib/statsdserver/output/amqp.rb
|
139
155
|
- lib/statsdserver/stats.rb
|
156
|
+
- lib/statsdserver/proto/parseerror.rb
|
157
|
+
- lib/statsdserver/proto/v1.rb
|
140
158
|
- lib/statsdserver.rb
|
141
159
|
- bin/statsd
|
142
160
|
homepage: https://github.com/fetep/ruby-statsd
|