panoptimon 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +29 -0
  5. data/README.md +78 -0
  6. data/Rakefile +14 -0
  7. data/bin/panoptimon +118 -0
  8. data/collectors/cpu/cpu +39 -0
  9. data/collectors/cpu/cpu.json +1 -0
  10. data/collectors/disk/disk +37 -0
  11. data/collectors/disk/disk.json +1 -0
  12. data/collectors/disk/requires +1 -0
  13. data/collectors/disk_free/disk_free +15 -0
  14. data/collectors/disk_free/disk_free.json +1 -0
  15. data/collectors/dns/README.md +33 -0
  16. data/collectors/dns/dns +9 -0
  17. data/collectors/dns/dns.json +7 -0
  18. data/collectors/dns/lib/panoptimon-collector-dns/dns.rb +43 -0
  19. data/collectors/dns/lib/panoptimon-collector-dns.rb +2 -0
  20. data/collectors/dns/panoptimon-collector-dns.gemspec +4 -0
  21. data/collectors/files/README.md +32 -0
  22. data/collectors/files/files +129 -0
  23. data/collectors/files/files.json +14 -0
  24. data/collectors/files/spec/files_spec.rb +57 -0
  25. data/collectors/haproxy/README.md +40 -0
  26. data/collectors/haproxy/haproxy +16 -0
  27. data/collectors/haproxy/haproxy.json +3 -0
  28. data/collectors/haproxy/lib/panoptimon-collector-haproxy/haproxy.rb +149 -0
  29. data/collectors/haproxy/lib/panoptimon-collector-haproxy.rb +1 -0
  30. data/collectors/haproxy/notes.txt +13 -0
  31. data/collectors/haproxy/spec/haproxy_spec.rb +98 -0
  32. data/collectors/haproxy/spec/haproxy_spec.rb-get.html +22 -0
  33. data/collectors/haproxy/spec/haproxy_spec.rb-show_info.txt +21 -0
  34. data/collectors/haproxy/spec/haproxy_spec.rb-show_stat.csv +25 -0
  35. data/collectors/haproxy/spec/haproxy_spec.rb-show_stat2.csv +11 -0
  36. data/collectors/http/README.md +49 -0
  37. data/collectors/http/http +27 -0
  38. data/collectors/http/http.json +3 -0
  39. data/collectors/http/lib/panoptimon-collector-http/http.rb +74 -0
  40. data/collectors/http/lib/panoptimon-collector-http/version.rb +7 -0
  41. data/collectors/http/lib/panoptimon-collector-http.rb +2 -0
  42. data/collectors/interfaces/interfaces +33 -0
  43. data/collectors/interfaces/interfaces.json +1 -0
  44. data/collectors/iostat/iostat +53 -0
  45. data/collectors/iostat/iostat.json +1 -0
  46. data/collectors/json/README.md +27 -0
  47. data/collectors/json/json +37 -0
  48. data/collectors/json/json.json +1 -0
  49. data/collectors/load/load +15 -0
  50. data/collectors/load/load.json +1 -0
  51. data/collectors/memcached/memcached +55 -0
  52. data/collectors/memcached/memcached.json +7 -0
  53. data/collectors/memcached/test-notes.txt +3 -0
  54. data/collectors/memory/memory +33 -0
  55. data/collectors/memory/memory.json +1 -0
  56. data/collectors/mysql_status/mysql_status +52 -0
  57. data/collectors/mysql_status/mysql_status.json +4 -0
  58. data/collectors/network/network +67 -0
  59. data/collectors/network/network.json +18 -0
  60. data/collectors/nginx/README.md +32 -0
  61. data/collectors/nginx/lib/panoptimon-collector-nginx/nginx.rb +45 -0
  62. data/collectors/nginx/lib/panoptimon-collector-nginx.rb +2 -0
  63. data/collectors/nginx/nginx +11 -0
  64. data/collectors/nginx/nginx.json +3 -0
  65. data/collectors/nginx/panoptimon-collector-nginx.gemspec +4 -0
  66. data/collectors/ping/README.md +54 -0
  67. data/collectors/ping/ping +57 -0
  68. data/collectors/ping/ping.json +7 -0
  69. data/collectors/process/README.md +36 -0
  70. data/collectors/process/process +61 -0
  71. data/collectors/process/process.json +7 -0
  72. data/collectors/service/README.md +51 -0
  73. data/collectors/service/samples/.gitignore +1 -0
  74. data/collectors/service/samples/data/disconnect +11 -0
  75. data/collectors/service/samples/data/flappy +7 -0
  76. data/collectors/service/samples/data/solid +18 -0
  77. data/collectors/service/samples/replay +27 -0
  78. data/collectors/service/service +86 -0
  79. data/collectors/service/service.json +7 -0
  80. data/collectors/smtp/lib/panoptimon-collector-smtp/smtp.rb +30 -0
  81. data/collectors/smtp/lib/panoptimon-collector-smtp.rb +1 -0
  82. data/collectors/smtp/smtp +27 -0
  83. data/collectors/smtp/smtp.json +10 -0
  84. data/collectors/socket/README.md +36 -0
  85. data/collectors/socket/lib/panoptimon-collector-socket/socket.rb +38 -0
  86. data/collectors/socket/lib/panoptimon-collector-socket/tcp.rb +34 -0
  87. data/collectors/socket/lib/panoptimon-collector-socket/unix.rb +28 -0
  88. data/collectors/socket/lib/panoptimon-collector-socket.rb +3 -0
  89. data/collectors/socket/socket +13 -0
  90. data/collectors/socket/socket.json +16 -0
  91. data/collectors/socket/tests/tcp_spec.rb +21 -0
  92. data/collectors/socket/tests/unix_spec.rb +35 -0
  93. data/collectors/ssh/README.md +27 -0
  94. data/collectors/ssh/ssh +41 -0
  95. data/collectors/ssh/ssh.json +3 -0
  96. data/lib/panoptimon/collector.rb +135 -0
  97. data/lib/panoptimon/eventmonkeypatch/popen3.rb +40 -0
  98. data/lib/panoptimon/http.rb +63 -0
  99. data/lib/panoptimon/logger.rb +19 -0
  100. data/lib/panoptimon/monitor.rb +154 -0
  101. data/lib/panoptimon/util/string-with-as_number.rb +5 -0
  102. data/lib/panoptimon/util.rb +23 -0
  103. data/lib/panoptimon/version.rb +5 -0
  104. data/lib/panoptimon.rb +144 -0
  105. data/misc/collector_setup.rb +23 -0
  106. data/misc/monitor_setup.rb +25 -0
  107. data/misc/plugins_setup.rb +25 -0
  108. data/misc/riemann-cli.rb +33 -0
  109. data/panoptimon.gemspec +33 -0
  110. data/plugins/daemon_health/README.md +31 -0
  111. data/plugins/daemon_health/daemon_health.json +4 -0
  112. data/plugins/daemon_health/daemon_health.rb +34 -0
  113. data/plugins/daemon_health/lib/panoptimon-plugin-daemon_health/rollup.rb +64 -0
  114. data/plugins/daemon_health/panoptimon-plugin-daemon_health.gemspec +10 -0
  115. data/plugins/daemon_health/spec/moving_avg_spec.rb +24 -0
  116. data/plugins/email/README.md +30 -0
  117. data/plugins/email/email.json +3 -0
  118. data/plugins/email/email.rb +52 -0
  119. data/plugins/log_to_file/log_to_file.json +1 -0
  120. data/plugins/log_to_file/log_to_file.rb +8 -0
  121. data/plugins/log_to_logger/log_to_logger.json +3 -0
  122. data/plugins/log_to_logger/log_to_logger.rb +7 -0
  123. data/plugins/metrics_http/README.md +23 -0
  124. data/plugins/metrics_http/metrics_http.json +1 -0
  125. data/plugins/metrics_http/metrics_http.rb +17 -0
  126. data/plugins/riemann_stream/requires +1 -0
  127. data/plugins/riemann_stream/riemann_stream.json +3 -0
  128. data/plugins/riemann_stream/riemann_stream.rb +23 -0
  129. data/plugins/status_http/requires +1 -0
  130. data/plugins/status_http/status_http.json +1 -0
  131. data/plugins/status_http/status_http.rb +60 -0
  132. data/sample_configs/1/collectors/alls_well.json +6 -0
  133. data/sample_configs/1/collectors/clock/clock +12 -0
  134. data/sample_configs/1/collectors/clock.json +1 -0
  135. data/sample_configs/1/collectors/df/df.json +6 -0
  136. data/sample_configs/1/collectors/df/wrap_df +21 -0
  137. data/sample_configs/1/collectors/load.json +1 -0
  138. data/sample_configs/1/panoptimon.json +4 -0
  139. data/sample_configs/1/plugins/isup/isup.rb +3 -0
  140. data/sample_configs/1/plugins/isup.json +1 -0
  141. data/sample_configs/1/plugins/log_to_file.json +1 -0
  142. data/sample_configs/err_handler/collectors/fail.json +4 -0
  143. data/sample_configs/err_handler/collectors/noisy.json +5 -0
  144. data/sample_configs/err_handler/collectors/noisy_failure.json +5 -0
  145. data/sample_configs/err_handler/collectors/notfound.json +4 -0
  146. data/sample_configs/err_handler/panoptimon.json +3 -0
  147. data/sample_configs/err_handler/plugins/.exists +0 -0
  148. data/sample_configs/passthru/collectors/beep.json +5 -0
  149. data/sample_configs/passthru/collectors/cat/collect_this.json +1 -0
  150. data/sample_configs/passthru/collectors/cat.json +5 -0
  151. data/sample_configs/passthru/panoptimon.json +3 -0
  152. data/sample_configs/passthru/plugins/okcat/okcat.rb +17 -0
  153. data/sample_configs/passthru/plugins/okcat.json +1 -0
  154. data/sample_configs/plugin_error/collectors/beep.json +5 -0
  155. data/sample_configs/plugin_error/panoptimon.json +1 -0
  156. data/sample_configs/plugin_error/plugins/error_always/error_always.rb +1 -0
  157. data/sample_configs/plugin_error/plugins/error_always.json +1 -0
  158. data/sample_configs/slow/collectors/slowbeep.json +6 -0
  159. data/sample_configs/slow/panoptimon.json +1 -0
  160. data/sample_configs/slow/plugins/.exists +0 -0
  161. data/sample_configs/timeout_newline/collectors/slow_lf.json +8 -0
  162. data/sample_configs/timeout_newline/panoptimon.json +1 -0
  163. data/sample_configs/timeout_newline/plugins/.exists +0 -0
  164. data/sample_configs/timeout_not/collectors/slowly.json +7 -0
  165. data/sample_configs/timeout_not/panoptimon.json +1 -0
  166. data/sample_configs/timeout_not/plugins/.exists +0 -0
  167. data/spec/collector/config_spec.rb +30 -0
  168. data/spec/collector/initialize_spec.rb +24 -0
  169. data/spec/collector/metric_spec.rb +22 -0
  170. data/spec/passthru_spec.rb +13 -0
  171. data/spec/util_spec.rb +37 -0
  172. data/tools/link_and_enable +37 -0
  173. data/tools/metricify +8 -0
  174. metadata +319 -0
@@ -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,4 @@
1
+ {
2
+ "interval" : 10,
3
+ "periods" : [900,60,20,10]
4
+ }
@@ -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,10 @@
1
+ Gem::Specification.new { |s|
2
+ s.name = 'panoptimon-plugin-daemon_health'
3
+ s.version = '0.0.1'
4
+ s.summary = 'report internal daemon metrics'
5
+ s.authors = ['Eric Wilhelm']
6
+
7
+ s.files = `git ls-files`.split($\)
8
+
9
+ s.add_dependency 'rusage'
10
+ }
@@ -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,3 @@
1
+ {
2
+
3
+ }
@@ -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,8 @@
1
+ require "json"
2
+ fh = File.new(config[:file] || raise("must have filename"), 'a')
3
+ ->(metric) {
4
+ # TODO handle failed writes / disk full?
5
+ fh.puts "#{Time.now.to_i} #{JSON.generate(metric)}"
6
+ # TODO flush on timer / only log once per second?
7
+ fh.flush
8
+ }
@@ -0,0 +1,3 @@
1
+ {"level" : "debug",
2
+ "prefix" : "log_to_logger: ",
3
+ "json" : false}
@@ -0,0 +1,7 @@
1
+ level = config[:level] || 'debug'
2
+ ->(metric) {
3
+ monitor.logger.send(level) {
4
+ (config[:prefix] || '') +
5
+ (config[:json] ? JSON::generate(metric) : metric.inspect)
6
+ }
7
+ }
@@ -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,3 @@
1
+ {
2
+ "host": "localhost",
3
+ "port": 5555 }
@@ -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>&nbsp;&nbsp;').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,6 @@
1
+ {
2
+ "description": "json-only collector example",
3
+ "interval": 3.0,
4
+ "exec": "/bin/echo",
5
+ "args": ["-e", "{\"everything_ok\" : true}\\n\\c"]
6
+ }
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+ c = JSON.parse(ARGV[0])
5
+
6
+ $stdout.sync = true
7
+
8
+ $stderr.puts "interval: #{c['interval']}"
9
+ while true
10
+ puts %Q{{"time" : #{Time.now.to_i}}}
11
+ sleep c['interval'] || 1
12
+ end
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1,6 @@
1
+ {
2
+ "description": "df collector example",
3
+ "interval": 3.0,
4
+ "exec": "wrap_df",
5
+ "args": ["-kh"]
6
+ }
@@ -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,4 @@
1
+ {
2
+ "collector_interval" : 360,
3
+ "collector_timeout": 10
4
+ }
@@ -0,0 +1,3 @@
1
+ # TODO this should just hook into the EM loop and not do anything
2
+ # with metrics?
3
+ return nil
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1 @@
1
+ {"file" : "/tmp/metrics.log"}
@@ -0,0 +1,4 @@
1
+ {
2
+ "description" : "failing collector",
3
+ "exec" : "/bin/false"
4
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "description" : "a noisy collector",
3
+ "exec" : "/usr/bin/perl",
4
+ "args" : ["-e", "$|=1; while() {sleep 1; warn qq/noises\\n/; print qq/{\"ok\" : true}\\n/}"]
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "description" : "noisy failure",
3
+ "exec" : "/usr/bin/perl",
4
+ "args" : ["-e", "die q/cannot frobnate/"]
5
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "description" : "command does not exist",
3
+ "exec" : "/probablynosuchfileordirectoryinthislocationatleastwehopeso"
4
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "collector_interval" : 1
3
+ }
File without changes
@@ -0,0 +1,5 @@
1
+ {
2
+ "description" : "beeper",
3
+ "exec" : "/bin/echo",
4
+ "args" : ["-e", "{\"beep\" : 1}\\n\\c"]
5
+ }
@@ -0,0 +1 @@
1
+ {"x" : 1, "y" : 2, "z" : 3}
@@ -0,0 +1,5 @@
1
+ {
2
+ "description" : "static pass-thru collection for testing",
3
+ "exec" : "/usr/bin/perl",
4
+ "args" : ["-e", "print q{}.<>", "sample_configs/passthru/collectors/cat/collect_this.json"]
5
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "collector_interval" : 1
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,5 @@
1
+ {
2
+ "description" : "beeper",
3
+ "exec" : "/bin/echo",
4
+ "args" : ["-e", "{\"beep\" : 1}\\n\\c"]
5
+ }
@@ -0,0 +1 @@
1
+ {"collector_interval" : 1}
@@ -0,0 +1 @@
1
+ return ->(metric) { raise 'error error trouble bubble' }
@@ -0,0 +1,6 @@
1
+ {
2
+ "description" : "too slow",
3
+ "timeout" : 2,
4
+ "exec" : "/usr/bin/perl",
5
+ "args" : ["-e", "$|=1; while() {sleep 3; print qq/{\"ok\" : true}\\n/}"]
6
+ }
@@ -0,0 +1 @@
1
+ {}
File without changes