statsdserver 0.4 → 0.5
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 +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
|