ganymed 0.2.3 → 0.3.0
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/Gemfile +1 -0
- data/ganymed.gemspec +1 -1
- data/lib/ganymed/collector.rb +18 -13
- data/lib/ganymed/collectors/cpu.rb +15 -13
- data/lib/ganymed/collectors/disk.rb +1 -1
- data/lib/ganymed/collectors/iostat.rb +9 -7
- data/lib/ganymed/collectors/load.rb +3 -3
- data/lib/ganymed/collectors/memory.rb +32 -30
- data/lib/ganymed/collectors/network.rb +3 -3
- data/lib/ganymed/collectors/process.rb +14 -12
- data/lib/ganymed/collectors/uptime.rb +6 -4
- data/lib/ganymed/config.yml +7 -3
- data/lib/ganymed/ext/array.rb +38 -0
- data/lib/ganymed/master.rb +57 -54
- data/lib/ganymed/processor.rb +34 -19
- data/lib/ganymed/sampler/counter.rb +4 -17
- data/lib/ganymed/sampler/datasource.rb +44 -51
- data/lib/ganymed/sampler/derive.rb +6 -15
- data/lib/ganymed/sampler/gauge.rb +3 -7
- data/lib/ganymed/sampler.rb +18 -50
- data/lib/ganymed/version.rb +1 -1
- data/lib/ganymed/websocket/query.rb +4 -0
- metadata +34 -33
data/Gemfile
CHANGED
data/ganymed.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
# master
|
20
20
|
s.add_dependency "activesupport", ">= 3.2"
|
21
21
|
s.add_dependency "eventmachine", ">= 0.12.10"
|
22
|
-
s.add_dependency "ganymed-client"
|
22
|
+
s.add_dependency "ganymed-client", ">= 0.2.0"
|
23
23
|
s.add_dependency "madvertise-ext", ">= 0.1.2"
|
24
24
|
s.add_dependency "madvertise-logging", ">= 0.3.2"
|
25
25
|
s.add_dependency "mixlib-cli"
|
data/lib/ganymed/collector.rb
CHANGED
@@ -49,7 +49,17 @@ module Ganymed
|
|
49
49
|
# @param [Section] config The configuration object.
|
50
50
|
def initialize(config)
|
51
51
|
@config = config
|
52
|
-
|
52
|
+
|
53
|
+
@config.client.sampler.tap do |sampler|
|
54
|
+
log.info("emitting collected samples to tcp##{sampler.host}:#{sampler.port}")
|
55
|
+
@sampler = Ganymed::Client::Sampler.connect(sampler.host, sampler.port)
|
56
|
+
end
|
57
|
+
|
58
|
+
@config.client.processor.tap do |processor|
|
59
|
+
log.info("emitting collected events to tcp##{processor.host}:#{processor.port}")
|
60
|
+
@processor = Ganymed::Client::Processor.connect(processor.host, processor.port)
|
61
|
+
end
|
62
|
+
|
53
63
|
load_collectors
|
54
64
|
end
|
55
65
|
|
@@ -58,10 +68,9 @@ module Ganymed
|
|
58
68
|
collectors.each do |file|
|
59
69
|
name = File.basename(file, '.rb')
|
60
70
|
config = @config.collectors[name.to_sym] || Section.new
|
61
|
-
config[:resolution] = @config.resolution
|
62
71
|
|
63
72
|
log.debug("loading collector #{name} from #{file}")
|
64
|
-
Plugin.new(config, @
|
73
|
+
Plugin.new(config, @sampler, @processor).from_file(file).tap do |collector|
|
65
74
|
log.info("collecting #{name} metrics every #{collector.interval} seconds")
|
66
75
|
collector.run
|
67
76
|
end
|
@@ -97,21 +106,19 @@ module Ganymed
|
|
97
106
|
attr_accessor :interval
|
98
107
|
|
99
108
|
# Processor socket.
|
100
|
-
# @return [Ganymed::Client::
|
109
|
+
# @return [Ganymed::Client::Processor]
|
101
110
|
attr_accessor :processor
|
102
111
|
|
103
112
|
# Sampler socket.
|
104
|
-
# @return [Ganymed::Client::
|
113
|
+
# @return [Ganymed::Client::Sampler]
|
105
114
|
attr_accessor :sampler
|
106
115
|
|
107
116
|
# Create a new plugin instance.
|
108
117
|
#
|
109
118
|
# @param [Section] config The configuration object.
|
110
119
|
# @param [Ganymed::Client] client A client instance.
|
111
|
-
def initialize(config,
|
112
|
-
@config, @
|
113
|
-
@processor = @client.processor
|
114
|
-
@sampler = @client.sampler
|
120
|
+
def initialize(config, sampler, processor)
|
121
|
+
@config, @sampler, @processor = config, sampler, processor
|
115
122
|
end
|
116
123
|
|
117
124
|
# Set the block used to collect metrics with this plugin.
|
@@ -119,9 +126,7 @@ module Ganymed
|
|
119
126
|
# @param [Fixnum,Float] interval Custom plugin interval.
|
120
127
|
# @return [void]
|
121
128
|
def collect(interval=nil, &block)
|
122
|
-
@interval
|
123
|
-
@interval ||= @config.interval.tap{}
|
124
|
-
@interval ||= @config.resolution
|
129
|
+
@interval = interval || config.interval.tap{} || 1
|
125
130
|
@collector = Proc.new(&block)
|
126
131
|
end
|
127
132
|
|
@@ -129,7 +134,7 @@ module Ganymed
|
|
129
134
|
# interval.
|
130
135
|
# @return [void]
|
131
136
|
def run
|
132
|
-
EM.add_periodic_timer(
|
137
|
+
EM.add_periodic_timer(interval) do
|
133
138
|
EM.defer { collect! }
|
134
139
|
end
|
135
140
|
end
|
@@ -1,19 +1,21 @@
|
|
1
|
-
collect
|
1
|
+
collect do
|
2
2
|
break if not File.readable?('/proc/stat')
|
3
3
|
|
4
|
-
File.open('/proc/stat')
|
5
|
-
|
4
|
+
File.open('/proc/stat') do | file|
|
5
|
+
file.each do |line|
|
6
|
+
next if not line =~ /^cpu /
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
cpu = line.chomp.split[1,7].map do |x|
|
9
|
+
x.to_i / 100
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
sampler.emit(:derive, "os.cpu.user", cpu[0])
|
13
|
+
sampler.emit(:derive, "os.cpu.nice", cpu[1])
|
14
|
+
sampler.emit(:derive, "os.cpu.system", cpu[2])
|
15
|
+
sampler.emit(:derive, "os.cpu.idle", cpu[3])
|
16
|
+
sampler.emit(:derive, "os.cpu.iowait", cpu[4])
|
17
|
+
sampler.emit(:derive, "os.cpu.irq", cpu[5])
|
18
|
+
sampler.emit(:derive, "os.cpu.softirq", cpu[6])
|
19
|
+
end
|
18
20
|
end
|
19
21
|
end
|
@@ -4,13 +4,15 @@ Struct.new("IOStat",
|
|
4
4
|
:wio, :wmerge, :wsect, :wuse,
|
5
5
|
:running, :use, :aveq)
|
6
6
|
|
7
|
-
collect
|
7
|
+
collect do
|
8
8
|
next if not File.readable?('/proc/diskstats')
|
9
|
-
File.open('/proc/diskstats')
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
File.open('/proc/diskstats') do |file|
|
10
|
+
file.each do |line|
|
11
|
+
ios = Struct::IOStat.new(*line.strip.split(/\s+/))
|
12
|
+
next if config.skip_numbered.tap{} and ios.dev =~ /\d+$/
|
13
|
+
next if config.exclude.map {|e| Regexp.new(e).match(ios.dev)}.any?
|
14
|
+
sampler.emit(:derive, "os.iostat.#{ios.dev}.rsect", ios.rsect)
|
15
|
+
sampler.emit(:derive, "os.iostat.#{ios.dev}.wsect", ios.wsect)
|
16
|
+
end
|
15
17
|
end
|
16
18
|
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
collect
|
1
|
+
collect do
|
2
2
|
next if not File.readable?('/proc/loadavg')
|
3
|
-
File.open('/proc/loadavg') do |
|
4
|
-
loadavg =
|
3
|
+
File.open('/proc/loadavg') do |file|
|
4
|
+
loadavg = file.read.chomp.split[0,3].map(&:to_f)
|
5
5
|
sampler.emit(:gauge, "os.loadavg", loadavg[0])
|
6
6
|
end
|
7
7
|
end
|
@@ -1,39 +1,41 @@
|
|
1
|
-
collect
|
1
|
+
collect do
|
2
2
|
next if not File.readable?('/proc/meminfo')
|
3
3
|
|
4
4
|
# calculate app memory from total
|
5
5
|
apps = 0
|
6
6
|
|
7
|
-
File.open('/proc/meminfo')
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
File.open('/proc/meminfo') do |file|
|
8
|
+
file.each do |line|
|
9
|
+
key, value, unit = line.chomp.split
|
10
|
+
key.gsub!(/:/, '')
|
11
|
+
value = value.to_i * 1024
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
13
|
+
case key
|
14
|
+
when "MemTotal"
|
15
|
+
apps = value
|
16
|
+
when "MemFree"
|
17
|
+
sampler.emit(:gauge, "os.mem.unused", value)
|
18
|
+
apps -= value
|
19
|
+
when "Buffers"
|
20
|
+
sampler.emit(:gauge, "os.mem.buffers", value)
|
21
|
+
apps -= value
|
22
|
+
when "Cached"
|
23
|
+
sampler.emit(:gauge, "os.mem.cache", value)
|
24
|
+
apps -= value
|
25
|
+
when "SwapCached"
|
26
|
+
sampler.emit(:gauge, "os.mem.swap_cache", value)
|
27
|
+
apps -= value
|
28
|
+
when "Slab"
|
29
|
+
sampler.emit(:gauge, "os.mem.slab_cache", value)
|
30
|
+
apps -= value
|
31
|
+
when "PageTables"
|
32
|
+
sampler.emit(:gauge, "os.mem.page_tables", value)
|
33
|
+
apps -= value
|
34
|
+
when "Mapped"
|
35
|
+
sampler.emit(:gauge, "os.mem.mapped", value)
|
36
|
+
end
|
35
37
|
end
|
36
|
-
end
|
37
38
|
|
38
|
-
|
39
|
+
sampler.emit(:gauge, "os.mem.apps", apps)
|
40
|
+
end
|
39
41
|
end
|
@@ -3,11 +3,11 @@ Struct.new("NetworkStat", :dev,
|
|
3
3
|
:rbytes, :rpackets, :rerrs, :rdrop, :rfifo, :rframe, :rcompressed, :rmulticast,
|
4
4
|
:wbytes, :wpackets, :werrs, :wdrop, :wfifo, :wcolls, :wcarrier, :wcompressed)
|
5
5
|
|
6
|
-
collect
|
6
|
+
collect do
|
7
7
|
break if not File.readable?('/proc/net/dev')
|
8
8
|
|
9
|
-
File.open('/proc/net/dev') do |
|
10
|
-
|
9
|
+
File.open('/proc/net/dev') do |file|
|
10
|
+
file.each do |line|
|
11
11
|
next unless line =~ /:/
|
12
12
|
|
13
13
|
ns = Struct::NetworkStat.new(*line.strip.split(/\s+/))
|
@@ -1,16 +1,18 @@
|
|
1
|
-
collect
|
1
|
+
collect do
|
2
2
|
break if not File.readable?('/proc/stat')
|
3
|
-
File.open('/proc/stat')
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
File.open('/proc/stat') do |file|
|
4
|
+
file.each do |line|
|
5
|
+
key, value = line.chomp.split
|
6
|
+
case key
|
7
|
+
when "ctxt"
|
8
|
+
sampler.emit(:derive, "os.procs.switch", value.to_i)
|
9
|
+
when "processes"
|
10
|
+
sampler.emit(:derive, "os.procs.forks", value.to_i)
|
11
|
+
when "procs_running"
|
12
|
+
sampler.emit(:gauge, "os.procs.running", value.to_i)
|
13
|
+
when "procs_blocked"
|
14
|
+
sampler.emit(:gauge, "os.procs.blocked", value.to_i)
|
15
|
+
end
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
collect do
|
2
2
|
break if not File.readable?('/proc/stat')
|
3
|
-
File.open('/proc/stat')
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
File.open('/proc/stat') do |file|
|
4
|
+
file.each do |line|
|
5
|
+
next if not line =~ /^btime /
|
6
|
+
boottime = Time.at(line.chomp.split[1].to_i)
|
7
|
+
processor.event("os.reboot", 1, :now => boottime)
|
8
|
+
end
|
7
9
|
end
|
8
10
|
end
|
data/lib/ganymed/config.yml
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
generic:
|
2
|
-
resolution: 300
|
3
|
-
|
4
2
|
eventmachine:
|
5
3
|
threadpool_size: 2
|
6
4
|
|
@@ -24,10 +22,16 @@ generic:
|
|
24
22
|
|
25
23
|
sampler:
|
26
24
|
enabled: true
|
27
|
-
|
25
|
+
window: 300
|
28
26
|
listen:
|
29
27
|
host: 127.0.0.1
|
30
28
|
port: 1337
|
29
|
+
consolidations:
|
30
|
+
min: [min]
|
31
|
+
max: [max]
|
32
|
+
avg: [mean]
|
33
|
+
stdev: [stdev]
|
34
|
+
u90: [percentile, 0.9]
|
31
35
|
|
32
36
|
client:
|
33
37
|
processor:
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'inline'
|
2
|
+
|
3
|
+
class Array
|
4
|
+
# this is incredibly faster than doing ary.inject(0, :+)
|
5
|
+
# should only be called on numbers-only arrays, though ;-)
|
6
|
+
inline :C do |builder|
|
7
|
+
builder.c_raw <<-EOC
|
8
|
+
static VALUE sum(int argc, VALUE *argv, VALUE self) {
|
9
|
+
double result = 0;
|
10
|
+
VALUE *arr = RARRAY_PTR(self);
|
11
|
+
long i, len = RARRAY_LEN(self);
|
12
|
+
|
13
|
+
for (i = 0; i < len; i++) {
|
14
|
+
result += NUM2DBL(arr[i]);
|
15
|
+
}
|
16
|
+
|
17
|
+
return rb_float_new(result);
|
18
|
+
}
|
19
|
+
EOC
|
20
|
+
end
|
21
|
+
|
22
|
+
def mean
|
23
|
+
sum.to_f / length
|
24
|
+
end
|
25
|
+
|
26
|
+
def variance
|
27
|
+
m = mean
|
28
|
+
reduce(0) {|accum, item| accum + (item - m) ** 2}.to_f / (length - 1)
|
29
|
+
end
|
30
|
+
|
31
|
+
def stdev
|
32
|
+
Math.sqrt(variance)
|
33
|
+
end
|
34
|
+
|
35
|
+
def percentile(pc)
|
36
|
+
sort[(pc * length).ceil - 1]
|
37
|
+
end
|
38
|
+
end
|
data/lib/ganymed/master.rb
CHANGED
@@ -31,74 +31,77 @@ module Ganymed
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def run
|
34
|
-
|
35
|
-
@
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
log.debug("EventMachine threadpool_size=#{config.eventmachine.threadpool_size}")
|
40
|
-
EventMachine.threadpool_size = config.eventmachine.threadpool_size
|
41
|
-
|
42
|
-
@reactor = Thread.new do
|
43
|
-
begin
|
44
|
-
EventMachine.run { setup_reactor }
|
45
|
-
rescue Exception => exc
|
46
|
-
log.exception(exc)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
if config.profiling.gcprofiler
|
52
|
-
puts GC::Profiler.report
|
34
|
+
if pid = Process.fork
|
35
|
+
@supervisor = true
|
36
|
+
Process.wait(pid)
|
37
|
+
else
|
38
|
+
child
|
53
39
|
end
|
54
40
|
end
|
55
41
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
42
|
+
def child
|
43
|
+
log.info("starting Ganymed reactor in #{Env.mode} mode")
|
44
|
+
|
45
|
+
# configure eventmachine
|
46
|
+
log.debug("EventMachine threadpool_size=#{config.eventmachine.threadpool_size}")
|
47
|
+
EventMachine.threadpool_size = config.eventmachine.threadpool_size
|
48
|
+
|
49
|
+
EventMachine.epoll # use epoll
|
50
|
+
EventMachine.run do
|
51
|
+
Processor.new(config) if config.processor.enabled
|
52
|
+
Sampler.new(config) if config.sampler.enabled
|
53
|
+
Collector.new(config) if config.collectors.any?
|
54
|
+
end
|
55
|
+
rescue Exception => exc
|
56
|
+
log.exception(exc)
|
60
57
|
end
|
61
58
|
|
62
59
|
def before_starting
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
60
|
+
unless @supervisor
|
61
|
+
if config.profiling.perftools
|
62
|
+
log.info("activating PerfTools CPU profiler")
|
63
|
+
require 'perftools'
|
64
|
+
PerfTools::CpuProfiler.start('profile')
|
65
|
+
end
|
68
66
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
67
|
+
if config.profiling.gcprofiler
|
68
|
+
log.info("activating GC::Profiler")
|
69
|
+
GC::Profiler.enable
|
70
|
+
end
|
73
71
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
72
|
+
if config.profiling.rubyprof
|
73
|
+
require 'ruby-prof'
|
74
|
+
log.info("activating RubyProf")
|
75
|
+
RubyProf.start
|
76
|
+
end
|
78
77
|
end
|
79
78
|
end
|
80
79
|
|
80
|
+
def before_stopping
|
81
|
+
if EventMachine.reactor_running?
|
82
|
+
log.info("shutting down Ganymed reactor")
|
83
|
+
EventMachine.stop_event_loop
|
84
|
+
end
|
85
|
+
end
|
81
86
|
|
82
87
|
def after_stopping
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
PerfTools::CpuProfiler.stop
|
89
|
-
`pprof.rb --svg profile > profile.svg`
|
90
|
-
end
|
88
|
+
unless @supervisor
|
89
|
+
if config.profiling.perftools
|
90
|
+
PerfTools::CpuProfiler.stop
|
91
|
+
`pprof.rb --svg profile > profile.svg`
|
92
|
+
end
|
91
93
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
94
|
+
if config.profiling.rubyprof
|
95
|
+
result = RubyProf.stop
|
96
|
+
result.eliminate_methods!([
|
97
|
+
/Queue#pop/,
|
98
|
+
/Queue#push/,
|
99
|
+
/Mutex#synchronize/,
|
100
|
+
/Mutex#sleep/,
|
101
|
+
])
|
102
|
+
printer = RubyProf::MultiPrinter.new(result)
|
103
|
+
printer.print(:path => "prof", :profile => "ganymed")
|
104
|
+
end
|
102
105
|
end
|
103
106
|
|
104
107
|
log.info("done shutting down. exiting ...")
|
data/lib/ganymed/processor.rb
CHANGED
@@ -9,7 +9,7 @@ require 'ganymed/websocket'
|
|
9
9
|
module Ganymed
|
10
10
|
|
11
11
|
##
|
12
|
-
# The Processor
|
12
|
+
# The Processor accepts {Event events} from a UDP socket and stores these
|
13
13
|
# metrics into MongoDB. All metrics are also published to interested parties
|
14
14
|
# via WebSocket.
|
15
15
|
#
|
@@ -18,7 +18,14 @@ module Ganymed
|
|
18
18
|
@config = config
|
19
19
|
@db = MongoDB.new(config.processor.mongodb)
|
20
20
|
@websocket = Websocket.new(config.processor.websocket, @db)
|
21
|
-
|
21
|
+
|
22
|
+
@config.client.sampler.tap do |sampler|
|
23
|
+
# schedule connect on next tick so that our sampler socket is
|
24
|
+
# available. Processor#initialize is called from EM.run and therefore
|
25
|
+
# no sockets have been opened yet.
|
26
|
+
EM.next_tick { @sampler = Ganymed::Client::Sampler.connect(sampler.host, sampler.port) }
|
27
|
+
end
|
28
|
+
|
22
29
|
cache!
|
23
30
|
listen!
|
24
31
|
end
|
@@ -39,21 +46,28 @@ module Ganymed
|
|
39
46
|
EM.open_datagram_socket(listen.host, listen.port, Connection) do |connection|
|
40
47
|
connection.processor = self
|
41
48
|
end
|
49
|
+
|
50
|
+
log.info("processing metrics on tcp##{listen.host}:#{listen.port}")
|
51
|
+
EM.start_server(listen.host, listen.port, Connection) do |connection|
|
52
|
+
connection.processor = self
|
53
|
+
end
|
42
54
|
end
|
43
55
|
end
|
44
56
|
|
45
57
|
def process(data)
|
46
|
-
data = MessagePack.unpack(data)
|
47
58
|
case data['_type'].to_sym
|
48
59
|
when :event
|
49
60
|
process_event(Event.parse(data))
|
50
|
-
@client.sampler.emit(:counter, 'ganymed.processor.events', 1)
|
51
61
|
when :metadata
|
52
62
|
process_metadata(data)
|
53
63
|
end
|
64
|
+
rescue Exception => exc
|
65
|
+
log.exception(exc)
|
54
66
|
end
|
55
67
|
|
56
68
|
def process_event(event)
|
69
|
+
@sampler.emit(:counter, 'ganymed.processor.events', 1) rescue nil
|
70
|
+
|
57
71
|
if not @metadata[event.origin].include?(event.ns)
|
58
72
|
# insert origin + namespace into metadata
|
59
73
|
metadata = @db['metadata'].find_and_modify({
|
@@ -70,19 +84,11 @@ module Ganymed
|
|
70
84
|
@websocket.send(:metadata, [metadata])
|
71
85
|
end
|
72
86
|
|
73
|
-
# send the event to websocket clients
|
74
|
-
@websocket.each do |connection|
|
75
|
-
connection.publish(event)
|
76
|
-
end
|
77
|
-
|
78
87
|
# normalize timestamp for events with a resolution
|
79
88
|
if event.resolution > 1
|
80
89
|
event.timestamp -= event.timestamp % event.resolution
|
81
90
|
end
|
82
91
|
|
83
|
-
# skip events that are produced below the resolution threshold
|
84
|
-
return if (1...@config.resolution).include?(event.resolution)
|
85
|
-
|
86
92
|
# insert event into ns collection
|
87
93
|
@db.collection(event.ns).update({
|
88
94
|
:c => event.cf,
|
@@ -91,6 +97,11 @@ module Ganymed
|
|
91
97
|
}, {
|
92
98
|
:$set => {:v => event.value}
|
93
99
|
}, :upsert => true)
|
100
|
+
|
101
|
+
# send the event to websocket clients
|
102
|
+
@websocket.each do |connection|
|
103
|
+
connection.publish(event)
|
104
|
+
end
|
94
105
|
end
|
95
106
|
|
96
107
|
def process_metadata(data)
|
@@ -105,17 +116,21 @@ module Ganymed
|
|
105
116
|
end
|
106
117
|
|
107
118
|
# @private
|
108
|
-
|
119
|
+
class Connection < EM::Connection
|
109
120
|
attr_accessor :processor
|
110
121
|
|
122
|
+
def initialize
|
123
|
+
@pac = MessagePack::Unpacker.new
|
124
|
+
end
|
125
|
+
|
111
126
|
def receive_data(data)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
rescue Exception => exc
|
116
|
-
log.exception(exc)
|
117
|
-
end
|
127
|
+
@pac.feed(data)
|
128
|
+
@pac.each do |obj|
|
129
|
+
EM.defer { processor.process(obj) }
|
118
130
|
end
|
131
|
+
rescue Exception => exc
|
132
|
+
log.exception(exc)
|
133
|
+
close_connection
|
119
134
|
end
|
120
135
|
end
|
121
136
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'ganymed/ext/array'
|
1
2
|
require 'ganymed/sampler/datasource'
|
2
3
|
|
3
4
|
module Ganymed
|
@@ -8,25 +9,11 @@ module Ganymed
|
|
8
9
|
# as a webserver request, user login, etc) and produces a rate/s gauge.
|
9
10
|
#
|
10
11
|
class Counter < DataSource
|
11
|
-
def flush
|
12
|
-
each
|
13
|
-
|
14
|
-
event[0]
|
15
|
-
end.group_by do |event| # group by timestamp
|
16
|
-
event[0].to_i
|
17
|
-
end.map do |_, events| # calculate counts for each second.
|
18
|
-
events.reduce(0) do |memo, event| # sum(event.count)
|
19
|
-
memo + event[1]
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
yield ns, origin, values
|
12
|
+
def flush
|
13
|
+
each do |ns, origin, ts|
|
14
|
+
yield ns, origin, ts.values.map(&:sum)
|
24
15
|
end
|
25
16
|
end
|
26
|
-
|
27
|
-
def feed(ns, origin, ts, value)
|
28
|
-
add(ns, origin, [ts, value])
|
29
|
-
end
|
30
17
|
end
|
31
18
|
end
|
32
19
|
end
|
@@ -1,10 +1,13 @@
|
|
1
|
+
require 'inline'
|
2
|
+
|
1
3
|
module Ganymed
|
2
4
|
class Sampler
|
3
5
|
|
4
6
|
##
|
5
7
|
# A DataSource processes samples from various sources. Samples are stored
|
6
|
-
# to an in-memory buffer
|
7
|
-
#
|
8
|
+
# to an in-memory buffer using a sliding-window algorithm. Values are kept
|
9
|
+
# for a configurable time and consolidated values can be flushed to the
|
10
|
+
# processor at any time.
|
8
11
|
#
|
9
12
|
# All DataSources interpolate their samples to gauge style values before
|
10
13
|
# flushing to the processor, e.g. a counter (like requests, or bytes sent
|
@@ -13,79 +16,69 @@ module Ganymed
|
|
13
16
|
#
|
14
17
|
class DataSource
|
15
18
|
|
16
|
-
attr_reader :
|
19
|
+
attr_reader :buffer, :window
|
17
20
|
|
18
|
-
# Create a new DataSource
|
19
|
-
#
|
20
|
-
# @param [Array] ticks All ticks that should be stored in the buffer.
|
21
|
+
# Create a new DataSource.
|
21
22
|
#
|
22
|
-
|
23
|
-
|
24
|
-
@
|
25
|
-
@buffer = Hash.new do |buffer,
|
26
|
-
buffer[
|
27
|
-
|
28
|
-
ns[
|
23
|
+
# @param [Fixnum] window Sliding window size.
|
24
|
+
def initialize(window)
|
25
|
+
@window = window
|
26
|
+
@buffer = Hash.new do |buffer, origin|
|
27
|
+
buffer[origin] = Hash.new do |origin, ns|
|
28
|
+
origin[ns] = Hash.new do |ns, timestamp|
|
29
|
+
ns[timestamp] = []
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
|
-
# @group Methods for subclasses
|
35
|
-
|
36
35
|
# Feed data from the source.
|
37
36
|
#
|
38
37
|
# @param [String] ns The namespace for this value.
|
39
38
|
# @param [String] origin The origin of this value.
|
40
|
-
# @param [Time] ts The timestamp of this value.
|
41
39
|
# @param [Fixnum,Float] value The actual value.
|
42
40
|
# @return [void]
|
43
|
-
def feed(ns, origin,
|
44
|
-
|
41
|
+
def feed(ns, origin, value)
|
42
|
+
@buffer[origin][ns][now] << value
|
45
43
|
end
|
46
44
|
|
47
|
-
# Flush and consolidate the buffer
|
45
|
+
# Flush and consolidate the buffer.
|
48
46
|
#
|
49
|
-
# @
|
50
|
-
#
|
51
|
-
#
|
47
|
+
# @yield [ns, origin, values] Run the block once for every
|
48
|
+
# namespace/origin, passing in all the
|
49
|
+
# consolidated values.
|
52
50
|
# @return [void]
|
53
|
-
def flush
|
51
|
+
def flush
|
54
52
|
raise NotImplementedError
|
55
53
|
end
|
56
54
|
|
57
|
-
#
|
58
|
-
|
59
|
-
#
|
55
|
+
# Yield values for every origin/ns combination and remove old elements
|
56
|
+
# from the buffer (sliding window). This is used internally to iterate
|
57
|
+
# through the values in the current window.
|
60
58
|
#
|
61
|
-
# @
|
62
|
-
#
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
@
|
67
|
-
|
59
|
+
# @yield [ns, origin, ts] Run the block once for every namespace
|
60
|
+
# passing in all the collected samples grouped
|
61
|
+
# by timestamp.
|
62
|
+
def each(&block)
|
63
|
+
oldest = now - window
|
64
|
+
@buffer.each do |origin, ns|
|
65
|
+
ns.each do |ns, ts|
|
66
|
+
ts.delete_if { |ts, _| ts < oldest }
|
67
|
+
yield ns, origin, ts
|
68
|
+
end
|
68
69
|
end
|
69
70
|
end
|
70
71
|
|
71
|
-
#
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
result.replace(@buffer[tick].dup)
|
82
|
-
@buffer[tick].clear
|
83
|
-
end
|
84
|
-
end.each do |ns, origins|
|
85
|
-
origins.each do |origin, values|
|
86
|
-
yield ns, origin, values
|
87
|
-
end
|
88
|
-
end
|
72
|
+
# Time.now.utc.to_i is way too expensive
|
73
|
+
inline :C do |builder|
|
74
|
+
builder.include "<sys/time.h>"
|
75
|
+
builder.c <<-EOC
|
76
|
+
long now(void) {
|
77
|
+
struct timeval tv;
|
78
|
+
gettimeofday(&tv, NULL);
|
79
|
+
return tv.tv_sec;
|
80
|
+
}
|
81
|
+
EOC
|
89
82
|
end
|
90
83
|
end
|
91
84
|
end
|
@@ -10,27 +10,18 @@ module Ganymed
|
|
10
10
|
# rate/s gauge, but only accepts absolute values and produces the
|
11
11
|
# derivative instead of summation of all samples.
|
12
12
|
#
|
13
|
-
# In order to get (meaningful) realtime events from a Derive {DataSource}
|
14
|
-
# samples must be sent multiple times per second. Derive does not store the
|
15
|
-
# previously flushed value for performance reasons and therefore cannot
|
16
|
-
# infer a rate/s gauge with only one event per flush.
|
17
|
-
#
|
18
13
|
class Derive < DataSource
|
19
|
-
def flush
|
20
|
-
each
|
21
|
-
values =
|
22
|
-
|
23
|
-
end.
|
24
|
-
|
14
|
+
def flush
|
15
|
+
each do |ns, origin, ts|
|
16
|
+
values = ts.each_cons(2).map do |a, b|
|
17
|
+
(b[1].last - a[1].last)/(b[0] - a[0]) # ∆value / ∆ts
|
18
|
+
end.reject do |value|
|
19
|
+
value < 0 # ignore overflow/counter wrap
|
25
20
|
end
|
26
21
|
|
27
22
|
yield ns, origin, values
|
28
23
|
end
|
29
24
|
end
|
30
|
-
|
31
|
-
def feed(ns, origin, ts, value)
|
32
|
-
add(ns, origin, [ts, value])
|
33
|
-
end
|
34
25
|
end
|
35
26
|
end
|
36
27
|
end
|
@@ -10,15 +10,11 @@ module Ganymed
|
|
10
10
|
# currently logged in, etc).
|
11
11
|
#
|
12
12
|
class Gauge < DataSource
|
13
|
-
def flush
|
14
|
-
each
|
15
|
-
yield ns, origin, values
|
13
|
+
def flush
|
14
|
+
each do |ns, origin, ts|
|
15
|
+
yield ns, origin, ts.values.flatten
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
19
|
-
def feed(ns, origin, ts, value)
|
20
|
-
add(ns, origin, value)
|
21
|
-
end
|
22
18
|
end
|
23
19
|
end
|
24
20
|
end
|
data/lib/ganymed/sampler.rb
CHANGED
@@ -1,33 +1,29 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
2
|
require 'eventmachine'
|
3
|
-
require 'inline'
|
4
|
-
require 'madvertise/ext/enumerable'
|
5
3
|
|
6
4
|
require 'ganymed'
|
7
5
|
require 'ganymed/client'
|
6
|
+
require 'ganymed/ext/array'
|
8
7
|
|
9
8
|
module Ganymed
|
10
9
|
|
11
10
|
##
|
12
11
|
# The Sampler processes samples from a UDP socket, stores these samples into
|
13
12
|
# memory and flushes a possibly interpolated and consolidated value to the
|
14
|
-
# {Processor}
|
13
|
+
# {Processor}.
|
15
14
|
#
|
16
15
|
class Sampler
|
17
16
|
def initialize(config)
|
18
17
|
@config = config
|
19
|
-
@config.sampler.ticks |= [@config.resolution]
|
20
|
-
@fqdn = @config.fqdn
|
21
18
|
listen!
|
22
19
|
emit!
|
23
|
-
tick!
|
24
20
|
end
|
25
21
|
|
26
22
|
def listen!
|
27
23
|
@config.sampler.listen.tap do |listen|
|
28
24
|
log.info("processing samples on udp##{listen.host}:#{listen.port}")
|
29
25
|
EM.open_datagram_socket(listen.host, listen.port, Connection) do |connection|
|
30
|
-
connection.fqdn = @fqdn
|
26
|
+
connection.fqdn = @config.fqdn
|
31
27
|
connection.datasources = datasources
|
32
28
|
end
|
33
29
|
end
|
@@ -35,53 +31,37 @@ module Ganymed
|
|
35
31
|
|
36
32
|
def emit!
|
37
33
|
@config.client.processor.tap do |processor|
|
38
|
-
log.info("emitting consolidated samples to
|
39
|
-
@
|
40
|
-
@processor = @client.processor
|
34
|
+
log.info("emitting consolidated samples to tcp##{processor.host}:#{processor.port}")
|
35
|
+
@processor = Ganymed::Client::Processor.connect(processor.host, processor.port)
|
41
36
|
end
|
42
|
-
end
|
43
37
|
|
44
|
-
|
45
|
-
@config.sampler.
|
46
|
-
|
47
|
-
end
|
38
|
+
# emit consolidated samples 5 times per window
|
39
|
+
interval = [1, @config.sampler.window / 5].max
|
40
|
+
EM.add_periodic_timer(interval) { flush }
|
48
41
|
end
|
49
42
|
|
50
43
|
def datasources
|
51
44
|
@datasources ||= Hash.new do |hsh, key|
|
52
45
|
klass = "Ganymed::Sampler::#{key.classify}".constantize
|
53
|
-
hsh[key] = klass.new(@config.sampler.
|
46
|
+
hsh[key] = klass.new(@config.sampler.window)
|
54
47
|
end
|
55
48
|
end
|
56
49
|
|
57
|
-
def flush
|
50
|
+
def flush
|
58
51
|
datasources.each do |_, datasource|
|
59
|
-
|
60
|
-
|
61
|
-
emit(ns, origin, tick, values)
|
62
|
-
end
|
52
|
+
datasource.flush do |ns, origin, values|
|
53
|
+
emit(ns, origin, values)
|
63
54
|
end
|
64
55
|
end
|
65
56
|
end
|
66
57
|
|
67
|
-
|
68
|
-
|
69
|
-
:min => [:min],
|
70
|
-
:max => [:max],
|
71
|
-
:avg => [:mean],
|
72
|
-
:stdev => [:stdev],
|
73
|
-
:u90 => [:percentile, 0.9],
|
74
|
-
}
|
75
|
-
|
76
|
-
def emit(ns, origin, tick, values)
|
77
|
-
CFS.each do |cf, args|
|
78
|
-
# do not calculate the consolidations below threshold
|
79
|
-
next if tick < @config.resolution and cf != :avg
|
58
|
+
def emit(ns, origin, values)
|
59
|
+
@config.sampler.consolidations.each do |cf, args|
|
80
60
|
begin
|
81
61
|
@processor.event(ns, values.send(*args), {
|
82
62
|
:cf => cf,
|
83
63
|
:origin => origin,
|
84
|
-
:resolution =>
|
64
|
+
:resolution => @config.sampler.window,
|
85
65
|
})
|
86
66
|
rescue Exception => exc
|
87
67
|
log.warn("failed to emit #{ns}@#{origin}")
|
@@ -94,24 +74,12 @@ module Ganymed
|
|
94
74
|
module Connection
|
95
75
|
attr_accessor :datasources, :fqdn
|
96
76
|
|
97
|
-
# Time.now.utc.to_f is way too expensive
|
98
|
-
inline :C do |builder|
|
99
|
-
builder.include "<sys/time.h>"
|
100
|
-
builder.c <<-EOC
|
101
|
-
double timestamp(void) {
|
102
|
-
struct timeval tv;
|
103
|
-
gettimeofday(&tv, NULL);
|
104
|
-
return ((double)tv.tv_sec + ((double)tv.tv_usec / 10000000.0));
|
105
|
-
}
|
106
|
-
EOC
|
107
|
-
end
|
108
|
-
|
109
77
|
def receive_data(data)
|
110
78
|
begin
|
111
|
-
# pack format: <datasource><namespace><origin><
|
112
|
-
data = data.unpack("Z*Z*Z*
|
79
|
+
# pack format: <datasource><namespace><origin><value>
|
80
|
+
data = data.unpack("Z*Z*Z*G")
|
113
81
|
datasources[data.slice!(0)].feed(*data)
|
114
|
-
datasources['counter'].feed('ganymed.sampler.samples',
|
82
|
+
datasources['counter'].feed('ganymed.sampler.samples', fqdn, 1)
|
115
83
|
rescue Exception => exc
|
116
84
|
log.exception(exc)
|
117
85
|
end
|
data/lib/ganymed/version.rb
CHANGED
@@ -12,12 +12,16 @@ module Ganymed
|
|
12
12
|
return if not authenticated?
|
13
13
|
data.each do |ns, query|
|
14
14
|
query_id = query.delete('_id')
|
15
|
+
|
15
16
|
log.debug("query #{query_id} from #{peer}: #{ns}(#{query.inspect})")
|
17
|
+
t = Time.now
|
16
18
|
|
17
19
|
events = db.collection(ns).find(query).map do |event|
|
18
20
|
Event.parse(event.merge({'n' => ns}))
|
19
21
|
end
|
20
22
|
|
23
|
+
log.debug("query #{query_id} returned #{events.length} results in #{Time.now - t}s")
|
24
|
+
|
21
25
|
send(:result, {query_id => convert(events)})
|
22
26
|
end
|
23
27
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ganymed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-06-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement: &
|
16
|
+
requirement: &16400780 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '3.2'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *16400780
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: eventmachine
|
27
|
-
requirement: &
|
27
|
+
requirement: &16399280 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,21 +32,21 @@ dependencies:
|
|
32
32
|
version: 0.12.10
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *16399280
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: ganymed-client
|
38
|
-
requirement: &
|
38
|
+
requirement: &16397660 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version:
|
43
|
+
version: 0.2.0
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *16397660
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: madvertise-ext
|
49
|
-
requirement: &
|
49
|
+
requirement: &16396540 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 0.1.2
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *16396540
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: madvertise-logging
|
60
|
-
requirement: &
|
60
|
+
requirement: &16395360 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 0.3.2
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *16395360
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: mixlib-cli
|
71
|
-
requirement: &
|
71
|
+
requirement: &16394880 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *16394880
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: RubyInline
|
82
|
-
requirement: &
|
82
|
+
requirement: &16410420 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: '0'
|
88
88
|
type: :runtime
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *16410420
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: servolux
|
93
|
-
requirement: &
|
93
|
+
requirement: &16408420 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ! '>='
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: '0'
|
99
99
|
type: :runtime
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *16408420
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: msgpack
|
104
|
-
requirement: &
|
104
|
+
requirement: &16406820 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: '0'
|
110
110
|
type: :runtime
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *16406820
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: mongo
|
115
|
-
requirement: &
|
115
|
+
requirement: &16405220 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ! '>='
|
@@ -120,10 +120,10 @@ dependencies:
|
|
120
120
|
version: '1.6'
|
121
121
|
type: :runtime
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *16405220
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
125
|
name: em-websocket
|
126
|
-
requirement: &
|
126
|
+
requirement: &16403660 !ruby/object:Gem::Requirement
|
127
127
|
none: false
|
128
128
|
requirements:
|
129
129
|
- - ! '>='
|
@@ -131,10 +131,10 @@ dependencies:
|
|
131
131
|
version: '0'
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
|
-
version_requirements: *
|
134
|
+
version_requirements: *16403660
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
136
|
name: yajl-ruby
|
137
|
-
requirement: &
|
137
|
+
requirement: &16402580 !ruby/object:Gem::Requirement
|
138
138
|
none: false
|
139
139
|
requirements:
|
140
140
|
- - ! '>='
|
@@ -142,10 +142,10 @@ dependencies:
|
|
142
142
|
version: '0'
|
143
143
|
type: :runtime
|
144
144
|
prerelease: false
|
145
|
-
version_requirements: *
|
145
|
+
version_requirements: *16402580
|
146
146
|
- !ruby/object:Gem::Dependency
|
147
147
|
name: sys-filesystem
|
148
|
-
requirement: &
|
148
|
+
requirement: &16433420 !ruby/object:Gem::Requirement
|
149
149
|
none: false
|
150
150
|
requirements:
|
151
151
|
- - ! '>='
|
@@ -153,10 +153,10 @@ dependencies:
|
|
153
153
|
version: '0'
|
154
154
|
type: :runtime
|
155
155
|
prerelease: false
|
156
|
-
version_requirements: *
|
156
|
+
version_requirements: *16433420
|
157
157
|
- !ruby/object:Gem::Dependency
|
158
158
|
name: ohai
|
159
|
-
requirement: &
|
159
|
+
requirement: &16432680 !ruby/object:Gem::Requirement
|
160
160
|
none: false
|
161
161
|
requirements:
|
162
162
|
- - ! '>='
|
@@ -164,7 +164,7 @@ dependencies:
|
|
164
164
|
version: 0.6.12
|
165
165
|
type: :runtime
|
166
166
|
prerelease: false
|
167
|
-
version_requirements: *
|
167
|
+
version_requirements: *16432680
|
168
168
|
description: Ganymed is an event collection daemon
|
169
169
|
email:
|
170
170
|
- bb@xnull.de
|
@@ -197,6 +197,7 @@ files:
|
|
197
197
|
- lib/ganymed/collectors/uptime.rb
|
198
198
|
- lib/ganymed/config.yml
|
199
199
|
- lib/ganymed/event.rb
|
200
|
+
- lib/ganymed/ext/array.rb
|
200
201
|
- lib/ganymed/master.rb
|
201
202
|
- lib/ganymed/mongodb.rb
|
202
203
|
- lib/ganymed/processor.rb
|
@@ -238,7 +239,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
238
239
|
version: '0'
|
239
240
|
segments:
|
240
241
|
- 0
|
241
|
-
hash:
|
242
|
+
hash: 4174202345634086766
|
242
243
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
243
244
|
none: false
|
244
245
|
requirements:
|
@@ -247,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
247
248
|
version: '0'
|
248
249
|
segments:
|
249
250
|
- 0
|
250
|
-
hash:
|
251
|
+
hash: 4174202345634086766
|
251
252
|
requirements: []
|
252
253
|
rubyforge_project:
|
253
254
|
rubygems_version: 1.8.17
|