panoptimon 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/Gemfile +4 -0
- data/LICENSE +29 -0
- data/README.md +78 -0
- data/Rakefile +14 -0
- data/bin/panoptimon +118 -0
- data/collectors/cpu/cpu +39 -0
- data/collectors/cpu/cpu.json +1 -0
- data/collectors/disk/disk +37 -0
- data/collectors/disk/disk.json +1 -0
- data/collectors/disk/requires +1 -0
- data/collectors/disk_free/disk_free +15 -0
- data/collectors/disk_free/disk_free.json +1 -0
- data/collectors/dns/README.md +33 -0
- data/collectors/dns/dns +9 -0
- data/collectors/dns/dns.json +7 -0
- data/collectors/dns/lib/panoptimon-collector-dns/dns.rb +43 -0
- data/collectors/dns/lib/panoptimon-collector-dns.rb +2 -0
- data/collectors/dns/panoptimon-collector-dns.gemspec +4 -0
- data/collectors/files/README.md +32 -0
- data/collectors/files/files +129 -0
- data/collectors/files/files.json +14 -0
- data/collectors/files/spec/files_spec.rb +57 -0
- data/collectors/haproxy/README.md +40 -0
- data/collectors/haproxy/haproxy +16 -0
- data/collectors/haproxy/haproxy.json +3 -0
- data/collectors/haproxy/lib/panoptimon-collector-haproxy/haproxy.rb +149 -0
- data/collectors/haproxy/lib/panoptimon-collector-haproxy.rb +1 -0
- data/collectors/haproxy/notes.txt +13 -0
- data/collectors/haproxy/spec/haproxy_spec.rb +98 -0
- data/collectors/haproxy/spec/haproxy_spec.rb-get.html +22 -0
- data/collectors/haproxy/spec/haproxy_spec.rb-show_info.txt +21 -0
- data/collectors/haproxy/spec/haproxy_spec.rb-show_stat.csv +25 -0
- data/collectors/haproxy/spec/haproxy_spec.rb-show_stat2.csv +11 -0
- data/collectors/http/README.md +49 -0
- data/collectors/http/http +27 -0
- data/collectors/http/http.json +3 -0
- data/collectors/http/lib/panoptimon-collector-http/http.rb +74 -0
- data/collectors/http/lib/panoptimon-collector-http/version.rb +7 -0
- data/collectors/http/lib/panoptimon-collector-http.rb +2 -0
- data/collectors/interfaces/interfaces +33 -0
- data/collectors/interfaces/interfaces.json +1 -0
- data/collectors/iostat/iostat +53 -0
- data/collectors/iostat/iostat.json +1 -0
- data/collectors/json/README.md +27 -0
- data/collectors/json/json +37 -0
- data/collectors/json/json.json +1 -0
- data/collectors/load/load +15 -0
- data/collectors/load/load.json +1 -0
- data/collectors/memcached/memcached +55 -0
- data/collectors/memcached/memcached.json +7 -0
- data/collectors/memcached/test-notes.txt +3 -0
- data/collectors/memory/memory +33 -0
- data/collectors/memory/memory.json +1 -0
- data/collectors/mysql_status/mysql_status +52 -0
- data/collectors/mysql_status/mysql_status.json +4 -0
- data/collectors/network/network +67 -0
- data/collectors/network/network.json +18 -0
- data/collectors/nginx/README.md +32 -0
- data/collectors/nginx/lib/panoptimon-collector-nginx/nginx.rb +45 -0
- data/collectors/nginx/lib/panoptimon-collector-nginx.rb +2 -0
- data/collectors/nginx/nginx +11 -0
- data/collectors/nginx/nginx.json +3 -0
- data/collectors/nginx/panoptimon-collector-nginx.gemspec +4 -0
- data/collectors/ping/README.md +54 -0
- data/collectors/ping/ping +57 -0
- data/collectors/ping/ping.json +7 -0
- data/collectors/process/README.md +36 -0
- data/collectors/process/process +61 -0
- data/collectors/process/process.json +7 -0
- data/collectors/service/README.md +51 -0
- data/collectors/service/samples/.gitignore +1 -0
- data/collectors/service/samples/data/disconnect +11 -0
- data/collectors/service/samples/data/flappy +7 -0
- data/collectors/service/samples/data/solid +18 -0
- data/collectors/service/samples/replay +27 -0
- data/collectors/service/service +86 -0
- data/collectors/service/service.json +7 -0
- data/collectors/smtp/lib/panoptimon-collector-smtp/smtp.rb +30 -0
- data/collectors/smtp/lib/panoptimon-collector-smtp.rb +1 -0
- data/collectors/smtp/smtp +27 -0
- data/collectors/smtp/smtp.json +10 -0
- data/collectors/socket/README.md +36 -0
- data/collectors/socket/lib/panoptimon-collector-socket/socket.rb +38 -0
- data/collectors/socket/lib/panoptimon-collector-socket/tcp.rb +34 -0
- data/collectors/socket/lib/panoptimon-collector-socket/unix.rb +28 -0
- data/collectors/socket/lib/panoptimon-collector-socket.rb +3 -0
- data/collectors/socket/socket +13 -0
- data/collectors/socket/socket.json +16 -0
- data/collectors/socket/tests/tcp_spec.rb +21 -0
- data/collectors/socket/tests/unix_spec.rb +35 -0
- data/collectors/ssh/README.md +27 -0
- data/collectors/ssh/ssh +41 -0
- data/collectors/ssh/ssh.json +3 -0
- data/lib/panoptimon/collector.rb +135 -0
- data/lib/panoptimon/eventmonkeypatch/popen3.rb +40 -0
- data/lib/panoptimon/http.rb +63 -0
- data/lib/panoptimon/logger.rb +19 -0
- data/lib/panoptimon/monitor.rb +154 -0
- data/lib/panoptimon/util/string-with-as_number.rb +5 -0
- data/lib/panoptimon/util.rb +23 -0
- data/lib/panoptimon/version.rb +5 -0
- data/lib/panoptimon.rb +144 -0
- data/misc/collector_setup.rb +23 -0
- data/misc/monitor_setup.rb +25 -0
- data/misc/plugins_setup.rb +25 -0
- data/misc/riemann-cli.rb +33 -0
- data/panoptimon.gemspec +33 -0
- data/plugins/daemon_health/README.md +31 -0
- data/plugins/daemon_health/daemon_health.json +4 -0
- data/plugins/daemon_health/daemon_health.rb +34 -0
- data/plugins/daemon_health/lib/panoptimon-plugin-daemon_health/rollup.rb +64 -0
- data/plugins/daemon_health/panoptimon-plugin-daemon_health.gemspec +10 -0
- data/plugins/daemon_health/spec/moving_avg_spec.rb +24 -0
- data/plugins/email/README.md +30 -0
- data/plugins/email/email.json +3 -0
- data/plugins/email/email.rb +52 -0
- data/plugins/log_to_file/log_to_file.json +1 -0
- data/plugins/log_to_file/log_to_file.rb +8 -0
- data/plugins/log_to_logger/log_to_logger.json +3 -0
- data/plugins/log_to_logger/log_to_logger.rb +7 -0
- data/plugins/metrics_http/README.md +23 -0
- data/plugins/metrics_http/metrics_http.json +1 -0
- data/plugins/metrics_http/metrics_http.rb +17 -0
- data/plugins/riemann_stream/requires +1 -0
- data/plugins/riemann_stream/riemann_stream.json +3 -0
- data/plugins/riemann_stream/riemann_stream.rb +23 -0
- data/plugins/status_http/requires +1 -0
- data/plugins/status_http/status_http.json +1 -0
- data/plugins/status_http/status_http.rb +60 -0
- data/sample_configs/1/collectors/alls_well.json +6 -0
- data/sample_configs/1/collectors/clock/clock +12 -0
- data/sample_configs/1/collectors/clock.json +1 -0
- data/sample_configs/1/collectors/df/df.json +6 -0
- data/sample_configs/1/collectors/df/wrap_df +21 -0
- data/sample_configs/1/collectors/load.json +1 -0
- data/sample_configs/1/panoptimon.json +4 -0
- data/sample_configs/1/plugins/isup/isup.rb +3 -0
- data/sample_configs/1/plugins/isup.json +1 -0
- data/sample_configs/1/plugins/log_to_file.json +1 -0
- data/sample_configs/err_handler/collectors/fail.json +4 -0
- data/sample_configs/err_handler/collectors/noisy.json +5 -0
- data/sample_configs/err_handler/collectors/noisy_failure.json +5 -0
- data/sample_configs/err_handler/collectors/notfound.json +4 -0
- data/sample_configs/err_handler/panoptimon.json +3 -0
- data/sample_configs/err_handler/plugins/.exists +0 -0
- data/sample_configs/passthru/collectors/beep.json +5 -0
- data/sample_configs/passthru/collectors/cat/collect_this.json +1 -0
- data/sample_configs/passthru/collectors/cat.json +5 -0
- data/sample_configs/passthru/panoptimon.json +3 -0
- data/sample_configs/passthru/plugins/okcat/okcat.rb +17 -0
- data/sample_configs/passthru/plugins/okcat.json +1 -0
- data/sample_configs/plugin_error/collectors/beep.json +5 -0
- data/sample_configs/plugin_error/panoptimon.json +1 -0
- data/sample_configs/plugin_error/plugins/error_always/error_always.rb +1 -0
- data/sample_configs/plugin_error/plugins/error_always.json +1 -0
- data/sample_configs/slow/collectors/slowbeep.json +6 -0
- data/sample_configs/slow/panoptimon.json +1 -0
- data/sample_configs/slow/plugins/.exists +0 -0
- data/sample_configs/timeout_newline/collectors/slow_lf.json +8 -0
- data/sample_configs/timeout_newline/panoptimon.json +1 -0
- data/sample_configs/timeout_newline/plugins/.exists +0 -0
- data/sample_configs/timeout_not/collectors/slowly.json +7 -0
- data/sample_configs/timeout_not/panoptimon.json +1 -0
- data/sample_configs/timeout_not/plugins/.exists +0 -0
- data/spec/collector/config_spec.rb +30 -0
- data/spec/collector/initialize_spec.rb +24 -0
- data/spec/collector/metric_spec.rb +22 -0
- data/spec/passthru_spec.rb +13 -0
- data/spec/util_spec.rb +37 -0
- data/tools/link_and_enable +37 -0
- data/tools/metricify +8 -0
- metadata +319 -0
data/panoptimon.gemspec
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require File.expand_path('../lib/panoptimon/version', __FILE__)
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |gem|
|
|
5
|
+
gem.authors = ["Eric Wilhelm"]
|
|
6
|
+
gem.description = %q{The All-Seeing System Monitor Daemon}
|
|
7
|
+
gem.summary = %q{Panoptimon collects and routes system metrics.}
|
|
8
|
+
|
|
9
|
+
gem.email = "sysops@sourcefire.com"
|
|
10
|
+
gem.homepage = "https://github.com/synthesist/panoptimon"
|
|
11
|
+
|
|
12
|
+
gem.license = 'bsd' # The (three-clause) BSD License
|
|
13
|
+
|
|
14
|
+
gem.files = `git ls-files`.split($\)
|
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
17
|
+
gem.name = "panoptimon"
|
|
18
|
+
gem.require_paths = ["lib"]
|
|
19
|
+
gem.version = Panoptimon::VERSION
|
|
20
|
+
gem.required_ruby_version = '>= 1.9'
|
|
21
|
+
# Core gem dependencies
|
|
22
|
+
gem.add_dependency 'eventmachine', '~> 1.0.0'
|
|
23
|
+
gem.add_dependency 'daemons'
|
|
24
|
+
gem.add_dependency 'json'
|
|
25
|
+
gem.add_dependency 'thin', '~> 1.5.0'
|
|
26
|
+
|
|
27
|
+
# Plugin gem dependencies
|
|
28
|
+
gem.add_dependency 'riemann-client'
|
|
29
|
+
|
|
30
|
+
# Collector gem dependencies
|
|
31
|
+
gem.add_dependency 'sys-filesystem'
|
|
32
|
+
gem.add_dependency 'mysql'
|
|
33
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Daemon Health Plugin (Self-Happiness Metrics Collector)
|
|
2
|
+
|
|
3
|
+
This plugin will periodically add metrics to the bus, while also
|
|
4
|
+
listening to other metrics to generate statics about the collected data.
|
|
5
|
+
|
|
6
|
+
# Configuration
|
|
7
|
+
|
|
8
|
+
`periods = ["60m", 300, 30]` - an array of reporting periods (in
|
|
9
|
+
seconds, or minutes with 'm' suffix)
|
|
10
|
+
|
|
11
|
+
`interval = 30` - the reporting interval (and rollup granularity) in
|
|
12
|
+
seconds. The periods should all be a multiple of this for consistent
|
|
13
|
+
results.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"daemon_health|300|metrics|nps" : 4.5867, # 5m moving avg
|
|
19
|
+
"daemon_health|900|metrics|nps" : 5.3778, # 15m ...
|
|
20
|
+
"daemon_health|3600|metrics|nps" : 5.4373, # 1hr...
|
|
21
|
+
"daemon_health|collectors" : 8,
|
|
22
|
+
"daemon_health|loaded_plugins" : 5,
|
|
23
|
+
"daemon_health|active_plugins" : 3, # with callbacks
|
|
24
|
+
"daemon_health|uptime" : 960, # seconds
|
|
25
|
+
"daemon_health|rss" : 21784, # kB
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Moving averages are calculated within the configured window (unless the
|
|
30
|
+
uptime is less than the window, in which case it is the average over
|
|
31
|
+
uptime.)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
require 'rusage'
|
|
3
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
|
|
4
|
+
require 'panoptimon-plugin-daemon_health/rollup'
|
|
5
|
+
|
|
6
|
+
config[:interval] ||= 60
|
|
7
|
+
|
|
8
|
+
start = then_ = Time.now
|
|
9
|
+
rolling = Rollup.new(start, config) # brief history
|
|
10
|
+
|
|
11
|
+
setup = ->() {
|
|
12
|
+
EM.add_periodic_timer(config[:interval], ->(){
|
|
13
|
+
now = Time.now
|
|
14
|
+
elapsed = now - then_
|
|
15
|
+
|
|
16
|
+
metrics = rolling.roll(now).merge({
|
|
17
|
+
'uptime' => (now - start).round(0),
|
|
18
|
+
'rss' => Process.rusage.maxrss,
|
|
19
|
+
collectors: monitor.collectors.length,
|
|
20
|
+
active_plugins: monitor.plugins.keys.length,
|
|
21
|
+
loaded_plugins: monitor.loaded_plugins.keys.length,
|
|
22
|
+
})
|
|
23
|
+
m = Metric.new('daemon_health', metrics)
|
|
24
|
+
monitor.bus.notify(m)
|
|
25
|
+
|
|
26
|
+
then_ = now
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return ->(metric) {
|
|
31
|
+
return if metric.has_key?('daemon_health|uptime') # skip our own data
|
|
32
|
+
if setup; setup[] ; setup = nil; end
|
|
33
|
+
rolling.log(metrics: metric.keys.length)
|
|
34
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
class Rollup < Array
|
|
2
|
+
|
|
3
|
+
def initialize(start, config)
|
|
4
|
+
@start = start
|
|
5
|
+
@start ||= Time.now
|
|
6
|
+
@periods = (config[:periods] || %w{60m 15m 5m 1m}).map {|m|
|
|
7
|
+
m.is_a?(String) ? m.sub(/m$/, '') ? m.to_i*60 : m.to_i : m
|
|
8
|
+
}.sort {|a,b| b <=> a}
|
|
9
|
+
_reset_current(@start)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def log (h)
|
|
13
|
+
h.each {|k,v|
|
|
14
|
+
c = @current[1][k] ||= {min: nil, max: nil, sum: 0}
|
|
15
|
+
c[:sum] += v
|
|
16
|
+
c[:min] ||= v; c[:min] = v if v < c[:min]
|
|
17
|
+
c[:max] ||= v; c[:max] = v if v > c[:max]
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def sunset(horizon)
|
|
22
|
+
cut_here = -1
|
|
23
|
+
self.each {|x| break if x[0] > horizon; cut_here += 1 }
|
|
24
|
+
self.slice!(0..cut_here) if cut_here >= 0
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def _reset_current (time); @current = [time, {}]; end
|
|
28
|
+
def _shortroll(now)
|
|
29
|
+
c = @current
|
|
30
|
+
elapsed = now - c[0]
|
|
31
|
+
self.push(c)
|
|
32
|
+
_reset_current(now)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def roll(now)
|
|
36
|
+
self.sunset(now - @periods[0])
|
|
37
|
+
self._shortroll(now)
|
|
38
|
+
|
|
39
|
+
agg = Hash[@periods.map {|p| [p, {}]}]
|
|
40
|
+
self.each {|x|
|
|
41
|
+
age = now - x[0]
|
|
42
|
+
thru = (0...@periods.length).to_a.find {|i| age > @periods[i]} ||
|
|
43
|
+
@periods.length
|
|
44
|
+
@periods[0...thru].each {|p|
|
|
45
|
+
x[1].each {|k,v|
|
|
46
|
+
o = agg[p][k] ||= {
|
|
47
|
+
# min: nil, max: nil, # XXX not sure if we need that here
|
|
48
|
+
sum: 0}
|
|
49
|
+
o[:sum] += v[:sum]
|
|
50
|
+
# o[:min] ||= v[:min]; o[:min] = v[:min] if v[:min] < o[:min]
|
|
51
|
+
# o[:max] ||= v[:max]; o[:max] = v[:max] if v[:max] > o[:max]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
up = now - @start
|
|
56
|
+
return Hash[agg.map {|p,h|
|
|
57
|
+
span = up > p ? p : up
|
|
58
|
+
h.each {|k,v| v[:nps] = (v.delete(:sum).to_f / span).round(4)}
|
|
59
|
+
[p.to_s, h]
|
|
60
|
+
}]
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'rspec'
|
|
4
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
|
5
|
+
require 'panoptimon-plugin-daemon_health/rollup'
|
|
6
|
+
|
|
7
|
+
describe('calculate a moving average') {
|
|
8
|
+
it('works') {
|
|
9
|
+
start = Time.now
|
|
10
|
+
roller = Rollup.new(start, {})
|
|
11
|
+
roller.log(x: 60*7)
|
|
12
|
+
out = roller.roll(start+60)
|
|
13
|
+
out['300'][:x][:nps].should == 7
|
|
14
|
+
out['60'][:x][:nps].should == 7
|
|
15
|
+
roller.log(x: 60*7)
|
|
16
|
+
out = roller.roll(start+60*2)
|
|
17
|
+
out['300'][:x][:nps].should == 7
|
|
18
|
+
out['60'][:x][:nps].should == 7
|
|
19
|
+
roller.log(x: 60*1)
|
|
20
|
+
out = roller.roll(start+60*3)
|
|
21
|
+
out['300'][:x][:nps].should == ((60*7+60*7+60).to_f/(60*3)).round(4)
|
|
22
|
+
out['60'][:x][:nps].should == 1
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# About
|
|
2
|
+
|
|
3
|
+
The email plugin allows you to collect one or more metrics (matched via
|
|
4
|
+
regexps) and relay them via mail on a configured interval.
|
|
5
|
+
|
|
6
|
+
# Config
|
|
7
|
+
|
|
8
|
+
```json
|
|
9
|
+
{
|
|
10
|
+
"match" : ["^daemon_health", "^cpu"],
|
|
11
|
+
"interval" : 3600, # 5MB / day inbox!
|
|
12
|
+
"unflatten" : true, # like compression, but ...
|
|
13
|
+
|
|
14
|
+
"to" : "required",
|
|
15
|
+
"from" : "you@host", # default ENV['USER'] + /etc/hostname
|
|
16
|
+
"smtp_settings" : {
|
|
17
|
+
# see Net::SMTP / ala ActionMailer
|
|
18
|
+
# address, port, domain, # aka 'helo'
|
|
19
|
+
# user_name, password, authentication # = plain|login|cram_md5
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
# Regexp Matching
|
|
25
|
+
|
|
26
|
+
The elements of the 'match' list are going to be interpolated
|
|
27
|
+
into regular expressions, but quoted in JSON. Thus, you will need to
|
|
28
|
+
write "^cpu\\|user" to specifically match the 'cpu|user' metric. (Hint:
|
|
29
|
+
you may find the "dot" any-character match e.g. "^cpu.user" to be more
|
|
30
|
+
readable and unambiguous enough for most cases.)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
require 'net/smtp'
|
|
3
|
+
require 'mail'
|
|
4
|
+
|
|
5
|
+
interval = config[:interval] || 60
|
|
6
|
+
smtp_args = config[:smtp_settings]
|
|
7
|
+
mail_args = {
|
|
8
|
+
to: config[:to] || raise("must have 'to' address for e-mail"),
|
|
9
|
+
from: config[:from] ||
|
|
10
|
+
(ENV['USER'] + '@' + File.new('/etc/hostname').readline.chomp),
|
|
11
|
+
subject: 'panoptimon update',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
matches = config[:match] \
|
|
15
|
+
? config[:match].map {|m| %r{#{m}}}
|
|
16
|
+
: nil
|
|
17
|
+
|
|
18
|
+
current = {} # per-granule
|
|
19
|
+
setup = ->() {
|
|
20
|
+
EM.add_periodic_timer(interval, ->(){
|
|
21
|
+
|
|
22
|
+
body = current.keys.count == 0 ? "---\n"
|
|
23
|
+
: current.keys.sort {|a,b| a <=> b}.map {|t|
|
|
24
|
+
next if current[t].keys.count == 0
|
|
25
|
+
"#{t}: #{JSON.generate(current[t])}"}.join("\n") + "\n"
|
|
26
|
+
msg = ::Mail::Message.new( mail_args.merge({ body: body }) )
|
|
27
|
+
begin;
|
|
28
|
+
::Net::SMTP.start( * smtp_args.values_at(
|
|
29
|
+
:address, :port, :domain,
|
|
30
|
+
:user_name, :password, :authentication
|
|
31
|
+
)) {|s| s.send_message(msg.to_s, msg.from[0], msg.to) }
|
|
32
|
+
rescue;
|
|
33
|
+
logger.warn("mail error: #{$!}")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
current = {}
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
->(metric) {
|
|
41
|
+
if setup; setup[] ; setup = nil; end
|
|
42
|
+
|
|
43
|
+
# collect / filter -> relay
|
|
44
|
+
t = Time.now.to_i
|
|
45
|
+
metric.each {|k,v|
|
|
46
|
+
next if matches and not(matches.find {|m| k =~ m})
|
|
47
|
+
store = current[t] ||= {}
|
|
48
|
+
store[k] = v # NOTE 1sec granularity / TODO conf[:granularity]?
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"file" : "/tmp/metrics.log"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Usage
|
|
2
|
+
|
|
3
|
+
The '/metrics.json' request path will return a json object representing
|
|
4
|
+
the current state of the metrics (TODO: maybe If-Modified-Since
|
|
5
|
+
support.)
|
|
6
|
+
|
|
7
|
+
To request only specific groups of metrics, append one or more metric
|
|
8
|
+
names separated by '&'. Metric names must match up to a '|' or the end.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
```none
|
|
12
|
+
$ GET 'http://localhost:8080/metrics.json'
|
|
13
|
+
{ ... } # everything
|
|
14
|
+
|
|
15
|
+
$ GET 'http://localhost:8080/metrics.json/cpu'
|
|
16
|
+
{"cpu|user":"3","cpu|system":"2","cpu|idle":"95","cpu|wait":"0"}
|
|
17
|
+
|
|
18
|
+
$ GET 'http://localhost:8080/metrics.json/cpu|user'
|
|
19
|
+
{"cpu|user":"2"}
|
|
20
|
+
|
|
21
|
+
$ GET 'http://localhost:8080/metrics.json/iostat|sda|util&disk|/dev/sda3|space_free&disk|/dev/sda3|files_free'
|
|
22
|
+
{"disk|/dev/sda3|space_free":3.628937,"disk|/dev/sda3|files_free":336963,"iostat|sda|util":0.0}
|
|
23
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
monitor.enable_cache
|
|
3
|
+
monitor.http.mount('/metrics.json', ->(env) {
|
|
4
|
+
metrics = monitor.cached
|
|
5
|
+
match = env['PATH_INFO']
|
|
6
|
+
match = (match.length == 0 || match == '/') ? nil :
|
|
7
|
+
%r{^(?:#{
|
|
8
|
+
match.sub(/^\//, '').split(/&/).map {|it|
|
|
9
|
+
Regexp.escape(Rack::Utils.unescape(it))
|
|
10
|
+
}.join('|')
|
|
11
|
+
})(?:\||$)}
|
|
12
|
+
json = JSON.generate(
|
|
13
|
+
match ? metrics.select {|k,v| k =~ match} : metrics)
|
|
14
|
+
[200, {'Content-Type' => 'text/json'}, [json]]
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
return nil
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gem/riemann-client
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'riemann/client'
|
|
3
|
+
|
|
4
|
+
c = Riemann::Client.new(
|
|
5
|
+
host: config[:host] || 'localhost',
|
|
6
|
+
port: config[:port] || 5555,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
hostname = config[:hostname] || Socket.gethostname
|
|
10
|
+
|
|
11
|
+
->(metrics) {
|
|
12
|
+
c.send_maybe_recv(Riemann::Message.new(:events =>
|
|
13
|
+
metrics.keys.map {|k|
|
|
14
|
+
v = metrics[k]
|
|
15
|
+
v = v.respond_to?(:to_f) ? v.to_f : v ? 1 : 0
|
|
16
|
+
Riemann::Event.new(
|
|
17
|
+
host: hostname,
|
|
18
|
+
service: k, metric: v,
|
|
19
|
+
# TODO tags,description,ttl,state configurable?
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
))
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gem/thin
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'erb'
|
|
2
|
+
require 'cgi'
|
|
3
|
+
|
|
4
|
+
start = Time.now
|
|
5
|
+
template = ERB.new <<EOT
|
|
6
|
+
<html>
|
|
7
|
+
<head><title>Panoptimon</title></head>
|
|
8
|
+
<body>
|
|
9
|
+
<p>
|
|
10
|
+
<%= Time.now %> - running: <%= ((Time.now - start) / 60**2).round(2) %> hours
|
|
11
|
+
(<%= (%x{ps -o rss= -p #{Process.pid}}.to_f/1024).round(2) %> MB)
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<h1>Config</h1>
|
|
15
|
+
<p><%= CGI.escapeHTML(JSON.pretty_generate(m.config.marshal_dump)).
|
|
16
|
+
gsub(/\n /, '<br> ').sub(/\n}/, '<br>}') %></p>
|
|
17
|
+
|
|
18
|
+
<h1>Plugins</h1>
|
|
19
|
+
<table style="padding:1">
|
|
20
|
+
<% m.loaded_plugins.each do |k,v| %>
|
|
21
|
+
<tr><td><%= CGI.escapeHTML(k) %></td>
|
|
22
|
+
<td><%= CGI.escapeHTML(JSON.generate(v)) %></td></tr></tr>
|
|
23
|
+
<% end %>
|
|
24
|
+
</table>
|
|
25
|
+
|
|
26
|
+
<h1>Collectors</h1>
|
|
27
|
+
<table style="padding:3">
|
|
28
|
+
<% m.collectors.each do |c| %>
|
|
29
|
+
<tr><td><%= CGI.escapeHTML(c.name) %></td>
|
|
30
|
+
<td><%= CGI.escapeHTML(JSON.generate(c.config)) %></td>
|
|
31
|
+
<td><%= c.last_run_time %></tr>
|
|
32
|
+
<% end %>
|
|
33
|
+
</table>
|
|
34
|
+
|
|
35
|
+
<h1>Current Metrics</h1>
|
|
36
|
+
<table border="1">
|
|
37
|
+
<thead><tr><th>name</th><th>value</th></thead>
|
|
38
|
+
<% m.cached.each do |k,v| %>
|
|
39
|
+
<tr><td><%= CGI.escapeHTML(k) %></td>
|
|
40
|
+
<td><%= CGI.escapeHTML(v.to_s) %></td></tr>
|
|
41
|
+
<% end %>
|
|
42
|
+
</table>
|
|
43
|
+
</body>
|
|
44
|
+
</html>
|
|
45
|
+
EOT
|
|
46
|
+
render = ->(m) {
|
|
47
|
+
template.result(binding)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
monitor.enable_cache
|
|
51
|
+
monitor.http.match('/$', ->(env) {
|
|
52
|
+
monitor.logger.debug "passed me #{env}"
|
|
53
|
+
env['rack.logger'].debug "status page request for #{env['REMOTE_ADDR']}"
|
|
54
|
+
# NOTE ^- is the same as monitor.logger
|
|
55
|
+
|
|
56
|
+
[200, {'Content-Type' => 'text/html'}, [render.call(monitor)]]
|
|
57
|
+
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return nil # no per-metric callback
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
cmd_args = ARGV[0]
|
|
5
|
+
c = JSON.parse(ARGV[1])
|
|
6
|
+
|
|
7
|
+
$stdout.sync = true
|
|
8
|
+
cmd_output = %x{ df #{cmd_args} }
|
|
9
|
+
|
|
10
|
+
lines = cmd_output.split(/\n/)
|
|
11
|
+
|
|
12
|
+
headers = lines.shift()
|
|
13
|
+
output = {}
|
|
14
|
+
|
|
15
|
+
lines.each do |line|
|
|
16
|
+
(fs, size, used, avail, use, mounted_on) = line.split
|
|
17
|
+
h = { :fs => fs, :size => size, :used => used, :avail => avail, :percentage => use, :mounted_on => mounted_on }
|
|
18
|
+
output[fs] = { :size => size, :used => used, :free => avail }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
puts JSON.generate(:output => output)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"file" : "/tmp/metrics.log"}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"x" : 1, "y" : 2, "z" : 3}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
require 'json'
|
|
3
|
+
expect = JSON.parse(File.new('sample_configs/passthru/collectors/cat/collect_this.json').read)
|
|
4
|
+
expect.keys.each {|k| expect['cat|'+k] = expect.delete(k)}
|
|
5
|
+
expect = JSON.generate(expect)
|
|
6
|
+
return ->(m) {
|
|
7
|
+
return if m['beep|beep'] # TODO complain if it beeps twice
|
|
8
|
+
monitor.logger.debug expect
|
|
9
|
+
monitor.logger.debug { JSON.generate(m) }
|
|
10
|
+
if JSON.generate(m) == expect
|
|
11
|
+
puts 'ok'
|
|
12
|
+
monitor.stop
|
|
13
|
+
else
|
|
14
|
+
puts 'not ok'
|
|
15
|
+
end
|
|
16
|
+
}
|
|
17
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"collector_interval" : 1}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
return ->(metric) { raise 'error error trouble bubble' }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
File without changes
|