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.
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