eye 0.4.2 → 0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGES.md +11 -0
  5. data/README.md +3 -3
  6. data/Rakefile +8 -0
  7. data/bin/eye +0 -316
  8. data/bin/loader_eye +3 -3
  9. data/examples/test.eye +2 -2
  10. data/eye.gemspec +4 -9
  11. data/lib/eye.rb +4 -2
  12. data/lib/eye/application.rb +3 -3
  13. data/lib/eye/checker.rb +14 -5
  14. data/lib/eye/checker/cputime.rb +23 -0
  15. data/lib/eye/checker/file_touched.rb +15 -0
  16. data/lib/eye/checker/http.rb +7 -9
  17. data/lib/eye/checker/memory.rb +1 -1
  18. data/lib/eye/checker/runtime.rb +28 -0
  19. data/lib/eye/checker/socket.rb +4 -4
  20. data/lib/eye/cli.rb +166 -0
  21. data/lib/eye/cli/commands.rb +79 -0
  22. data/lib/eye/cli/render.rb +137 -0
  23. data/lib/eye/cli/server.rb +85 -0
  24. data/lib/eye/client.rb +3 -3
  25. data/lib/eye/config.rb +17 -14
  26. data/lib/eye/controller.rb +6 -10
  27. data/lib/eye/controller/commands.rb +6 -10
  28. data/lib/eye/controller/helpers.rb +1 -1
  29. data/lib/eye/controller/send_command.rb +5 -2
  30. data/lib/eye/controller/status.rb +37 -105
  31. data/lib/eye/dsl.rb +1 -1
  32. data/lib/eye/dsl/application_opts.rb +6 -2
  33. data/lib/eye/dsl/child_process_opts.rb +3 -2
  34. data/lib/eye/dsl/config_opts.rb +2 -2
  35. data/lib/eye/dsl/group_opts.rb +2 -1
  36. data/lib/eye/dsl/main.rb +4 -2
  37. data/lib/eye/dsl/opts.rb +11 -4
  38. data/lib/eye/dsl/validation.rb +49 -43
  39. data/lib/eye/group.rb +1 -1
  40. data/lib/eye/loader.rb +5 -9
  41. data/lib/eye/{settings.rb → local.rb} +1 -1
  42. data/lib/eye/logger.rb +5 -0
  43. data/lib/eye/notify.rb +12 -6
  44. data/lib/eye/notify/jabber.rb +2 -2
  45. data/lib/eye/process/child.rb +3 -1
  46. data/lib/eye/process/commands.rb +2 -2
  47. data/lib/eye/process/controller.rb +1 -1
  48. data/lib/eye/process/trigger.rb +1 -1
  49. data/lib/eye/sigar.rb +5 -0
  50. data/lib/eye/system.rb +7 -6
  51. data/lib/eye/system_resources.rb +46 -41
  52. data/lib/eye/trigger.rb +15 -8
  53. data/lib/eye/trigger/flapping.rb +1 -1
  54. data/lib/eye/trigger/stop_childs.rb +1 -1
  55. data/lib/eye/trigger/transition.rb +15 -0
  56. data/lib/eye/utils.rb +12 -0
  57. data/lib/eye/utils/leak_19.rb +7 -0
  58. data/lib/eye/utils/mini_active_support.rb +106 -0
  59. metadata +24 -15
  60. data/lib/eye/controller/show_history.rb +0 -63
  61. data/lib/eye/trigger/state.rb +0 -11
data/lib/eye.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Eye
2
- VERSION = "0.4.2"
2
+ VERSION = "0.5"
3
3
  ABOUT = "Eye v#{VERSION} (c) 2012-2013 @kostya"
4
4
  PROCLINE = "eye monitoring v#{VERSION}"
5
5
 
@@ -14,13 +14,15 @@ module Eye
14
14
  autoload :Group, 'eye/group'
15
15
  autoload :Dsl, 'eye/dsl'
16
16
  autoload :Application, 'eye/application'
17
- autoload :Settings, 'eye/settings'
17
+ autoload :Local, 'eye/local'
18
18
  autoload :Client, 'eye/client'
19
19
  autoload :Utils, 'eye/utils'
20
20
  autoload :Notify, 'eye/notify'
21
21
  autoload :Config, 'eye/config'
22
22
  autoload :Reason, 'eye/reason'
23
+ autoload :Sigar, 'eye/sigar'
23
24
 
24
25
  autoload :Controller, 'eye/controller'
25
26
  autoload :Control, 'eye/control'
27
+ autoload :Cli, 'eye/cli'
26
28
  end
@@ -33,14 +33,14 @@ class Eye::Application
33
33
  end
34
34
 
35
35
  def status_data_short
36
- h = Hash.new 0
36
+ h = Hash.new
37
37
  @groups.each do |c|
38
38
  c.processes.each do |p|
39
+ h[p.state] ||= 0
39
40
  h[p.state] += 1
40
41
  end
41
42
  end
42
- str = h.sort_by{|a,b| a}.map{|k, v| "#{k}:#{v}" } * ', '
43
- { name: @name, type: :application, state: str}
43
+ { name: @name, type: :application, states: h}
44
44
  end
45
45
 
46
46
  def debug_data
@@ -1,20 +1,23 @@
1
1
  class Eye::Checker
2
+ include Eye::Dsl::Validation
2
3
 
3
4
  autoload :Memory, 'eye/checker/memory'
4
5
  autoload :Cpu, 'eye/checker/cpu'
5
6
  autoload :Http, 'eye/checker/http'
6
7
  autoload :FileCTime, 'eye/checker/file_ctime'
7
8
  autoload :FileSize, 'eye/checker/file_size'
9
+ autoload :FileTouched,'eye/checker/file_touched'
8
10
  autoload :Socket, 'eye/checker/socket'
9
11
  autoload :Nop, 'eye/checker/nop'
12
+ autoload :Runtime, 'eye/checker/runtime'
13
+ autoload :Cputime, 'eye/checker/cputime'
10
14
 
11
- TYPES = {:memory => "Memory", :cpu => "Cpu", :http => "Http",
12
- :ctime => "FileCTime", :fsize => "FileSize", :socket => "Socket",
13
- :nop => "Nop" }
15
+ TYPES = {:memory => 'Memory', :cpu => 'Cpu', :http => 'Http',
16
+ :ctime => 'FileCTime', :fsize => 'FileSize', :file_touched => 'FileTouched',
17
+ :socket => 'Socket', :nop => 'Nop', :runtime => 'Runtime', :cputime => 'Cputime' }
14
18
 
15
19
  attr_accessor :value, :values, :options, :pid, :type, :check_count, :process
16
20
 
17
- extend Eye::Dsl::Validation
18
21
  param :every, [Fixnum, Float], false, 5
19
22
  param :times, [Fixnum, Array], nil, 1
20
23
  param :fires, [Symbol, Array], nil, nil, [:stop, :restart, :unmonitor, :nothing, :start, :delete]
@@ -34,6 +37,9 @@ class Eye::Checker
34
37
  def self.get_class(type)
35
38
  klass = eval("Eye::Checker::#{TYPES[type]}") rescue nil
36
39
  raise "Unknown checker #{type}" unless klass
40
+ if deps = klass.depends_on
41
+ Array(deps).each { |d| require d }
42
+ end
37
43
  klass
38
44
  end
39
45
 
@@ -185,12 +191,15 @@ class Eye::Checker
185
191
  end
186
192
 
187
193
  def self.register(base)
188
- name = base.to_s.gsub("Eye::Checker::", '')
194
+ name = base.to_s.gsub('Eye::Checker::', '')
189
195
  type = name.underscore.to_sym
190
196
  Eye::Checker::TYPES[type] = name
191
197
  Eye::Checker.const_set(name, base)
192
198
  end
193
199
 
200
+ def self.depends_on
201
+ end
202
+
194
203
  class CustomCell < Eye::Checker
195
204
  def self.inherited(base)
196
205
  super
@@ -0,0 +1,23 @@
1
+ class Eye::Checker::Cputime < Eye::Checker
2
+
3
+ # check :cputime, :every => 1.minute, :below => 120.minutes
4
+
5
+ param :below, [Fixnum, Float], true
6
+
7
+ def get_value
8
+ Eye::SystemResources.cputime(@pid).to_f
9
+ end
10
+
11
+ def human_value(value)
12
+ "#{value / 60}m"
13
+ end
14
+
15
+ def good?(value)
16
+ if below
17
+ value < below
18
+ else
19
+ true
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,15 @@
1
+ class Eye::Checker::FileTouched < Eye::Checker
2
+
3
+ param :file, [String], true
4
+ param :delete, [TrueClass, FalseClass]
5
+
6
+ def get_value
7
+ File.exists?(file)
8
+ end
9
+
10
+ def good?(value)
11
+ File.delete(file) if value && delete
12
+ !value
13
+ end
14
+
15
+ end
@@ -7,7 +7,7 @@ class Eye::Checker::Http < Eye::Checker::Defer
7
7
 
8
8
  param :url, String, true
9
9
  param :pattern, [String, Regexp]
10
- param :kind
10
+ param :kind, [String, Fixnum, Symbol]
11
11
  param :timeout, [Fixnum, Float]
12
12
  param :open_timeout, [Fixnum, Float]
13
13
  param :read_timeout, [Fixnum, Float]
@@ -17,8 +17,7 @@ class Eye::Checker::Http < Eye::Checker::Defer
17
17
  def initialize(*args)
18
18
  super
19
19
 
20
- @uri = URI.parse(url) rescue URI.parse('http://127.0.0.1')
21
- @pattern = pattern
20
+ @uri = URI.parse(url)
22
21
  @kind = case kind
23
22
  when Fixnum then Net::HTTPResponse::CODE_TO_OBJ[kind]
24
23
  when String, Symbol then Net.const_get("HTTP#{kind.to_s.camelize}") rescue Net::HTTPSuccess
@@ -40,7 +39,6 @@ class Eye::Checker::Http < Eye::Checker::Defer
40
39
  mes = ex.class.is_a?(Net::OpenTimeout) ? "OpenTimeout<#{@open_timeout}>" : "ReadTimeout<#{@read_timeout}>"
41
40
  {:exception => mes}
42
41
  else
43
- error "#{ex.message}"
44
42
  {:exception => "Timeout<#{@open_timeout},#{@read_timeout}>"}
45
43
  end
46
44
 
@@ -55,13 +53,13 @@ class Eye::Checker::Http < Eye::Checker::Defer
55
53
  return false
56
54
  end
57
55
 
58
- if @pattern
59
- matched = if @pattern.is_a?(Regexp)
60
- @pattern === value[:result].body
56
+ if pattern
57
+ matched = if pattern.is_a?(Regexp)
58
+ pattern === value[:result].body
61
59
  else
62
- value[:result].body.include?(@pattern.to_s)
60
+ value[:result].body.include?(pattern.to_s)
63
61
  end
64
- value[:notice] = "missing '#{@pattern.to_s}'" unless matched
62
+ value[:notice] = "missing '#{pattern.to_s}'" unless matched
65
63
  matched
66
64
  else
67
65
  true
@@ -9,7 +9,7 @@ class Eye::Checker::Memory < Eye::Checker
9
9
  end
10
10
 
11
11
  def get_value
12
- Eye::SystemResources.memory(@pid).to_i * 1024
12
+ Eye::SystemResources.memory(@pid).to_i
13
13
  end
14
14
 
15
15
  def human_value(value)
@@ -0,0 +1,28 @@
1
+ class Eye::Checker::Runtime < Eye::Checker
2
+
3
+ # check :runtime, :every => 1.minute, :below => 120.minutes
4
+
5
+ param :below, [Fixnum, Float], true
6
+
7
+ def get_value
8
+ st = Eye::SystemResources.start_time(@pid)
9
+ if st
10
+ Time.now.to_i - st.to_i
11
+ else
12
+ 0
13
+ end
14
+ end
15
+
16
+ def human_value(value)
17
+ "#{value / 60}m"
18
+ end
19
+
20
+ def good?(value)
21
+ if below
22
+ value < below
23
+ else
24
+ true
25
+ end
26
+ end
27
+
28
+ end
@@ -80,7 +80,7 @@ class Eye::Checker::Socket < Eye::Checker::Defer
80
80
 
81
81
  unless match
82
82
  warn "proc #{expect_data} not matched (#{value[:result].truncate(30)}) answer"
83
- value[:notice] = "missing proc validation"
83
+ value[:notice] = 'missing proc validation'
84
84
  end
85
85
 
86
86
  return match
@@ -104,7 +104,7 @@ class Eye::Checker::Socket < Eye::Checker::Defer
104
104
  value[:exception]
105
105
  else
106
106
  if value[:result] == :listen
107
- "listen"
107
+ 'listen'
108
108
  else
109
109
  res = "#{value[:result].to_s.size}b"
110
110
  res += "<#{value[:notice]}>" if value[:notice]
@@ -138,7 +138,7 @@ private
138
138
  def _read_data(socket)
139
139
  case protocol
140
140
  when :em_object
141
- content = ""
141
+ content = ''
142
142
  msg_size = socket.recv(4).unpack('N')[0] rescue 0
143
143
  content << socket.recv(msg_size - content.length) while content.length < msg_size
144
144
  if content.present?
@@ -149,4 +149,4 @@ private
149
149
  end
150
150
  end
151
151
 
152
- end
152
+ end
@@ -0,0 +1,166 @@
1
+ gem 'thor'
2
+ require 'thor'
3
+
4
+ class Eye::Cli < Thor
5
+ autoload :Server, 'eye/cli/server'
6
+ autoload :Commands, 'eye/cli/commands'
7
+ autoload :Render, 'eye/cli/render'
8
+
9
+ include Eye::Cli::Server
10
+ include Eye::Cli::Commands
11
+ include Eye::Cli::Render
12
+
13
+ desc "info [MASK]", "processes info"
14
+ def info(mask = nil)
15
+ res = cmd(:info_data, *Array(mask))
16
+ say render_info(res)
17
+ say
18
+ end
19
+
20
+ desc "status", "processes info (deprecated)"
21
+ def status
22
+ say ":status is deprecated, use :info instead", :yellow
23
+ info
24
+ end
25
+
26
+ desc "xinfo", "eye-deamon info (-c show current config)"
27
+ method_option :config, :type => :boolean, :aliases => "-c"
28
+ def xinfo
29
+ res = cmd(:debug_data, :config => options[:config])
30
+ say render_debug_info(res)
31
+ say
32
+ end
33
+
34
+ desc "oinfo", "onelined info"
35
+ def oinfo
36
+ res = cmd(:short_data)
37
+ say render_info(res)
38
+ say
39
+ end
40
+
41
+ desc "history [MASK,...]", "processes history"
42
+ def history(*masks)
43
+ res = cmd(:history_data, *masks)
44
+ say render_history(res)
45
+ say
46
+ end
47
+
48
+ desc "load [CONF, ...]", "load config (start eye-daemon if not) (-f foreground start)"
49
+ method_option :foreground, :type => :boolean, :aliases => "-f"
50
+ def load(*configs)
51
+ configs.map!{ |c| File.expand_path(c) } if !configs.empty?
52
+
53
+ if options[:foreground]
54
+ # in foreground we stop another server, and run just 1 current config version
55
+ error!("foreground expected only one config") if configs.size != 1
56
+ server_start_foreground(configs.first)
57
+
58
+ elsif server_started?
59
+ say_load_result cmd(:load, *configs)
60
+
61
+ else
62
+ server_start(configs)
63
+
64
+ end
65
+ end
66
+
67
+ desc "quit", "eye-daemon quit"
68
+ def quit
69
+ res = _cmd(:quit)
70
+
71
+ # if eye server got crazy, stop by force
72
+ ensure_stop_previous_server if res != :corrupted_data
73
+
74
+ # remove pid_file
75
+ File.delete(Eye::Local.pid_path) if File.exists?(Eye::Local.pid_path)
76
+
77
+ say "quit...", :yellow
78
+ end
79
+
80
+ [:start, :stop, :restart, :unmonitor, :monitor, :delete, :match].each do |_cmd|
81
+ desc "#{_cmd} MASK[,...]", "#{_cmd} app,group or process"
82
+ define_method(_cmd) do |*masks|
83
+ send_command(_cmd, *masks)
84
+ end
85
+ end
86
+
87
+ desc "signal SIG MASK[,...]", "send signal to app,group or process"
88
+ def signal(sig, *masks)
89
+ send_command(:signal, sig, *masks)
90
+ end
91
+
92
+ desc "break MASK[,...]", "break chain executing"
93
+ def break(*masks)
94
+ send_command(:break_chain, *masks)
95
+ end
96
+
97
+ desc "trace [MASK]", "tracing log(tail + grep) for app,group or process"
98
+ def trace(mask = "")
99
+ log_trace(mask)
100
+ end
101
+
102
+ map ["-v", "--version"] => :version
103
+ desc "version", "version"
104
+ def version
105
+ say Eye::ABOUT
106
+ end
107
+
108
+ desc "check CONF", "check config file syntax"
109
+ method_option :host, :type => :string, :aliases => "-h"
110
+ method_option :verbose, :type => :boolean, :aliases => "-v"
111
+ def check(conf)
112
+ conf = File.expand_path(conf) if conf && !conf.empty?
113
+
114
+ Eye::System.host = options[:host] if options[:host]
115
+ Eye::Dsl.verbose = options[:verbose]
116
+
117
+ say_load_result Eye::Controller.new.check(conf), :syntax => true
118
+ end
119
+
120
+ desc "explain CONF", "explain config tree"
121
+ method_option :host, :type => :string, :aliases => "-h"
122
+ method_option :verbose, :type => :boolean, :aliases => "-v"
123
+ def explain(conf)
124
+ conf = File.expand_path(conf) if conf && !conf.empty?
125
+
126
+ Eye::System.host = options[:host] if options[:host]
127
+ Eye::Dsl.verbose = options[:verbose]
128
+
129
+ say_load_result Eye::Controller.new.explain(conf), :print_config => true, :syntax => true
130
+ end
131
+
132
+ desc "watch [MASK]", "interactive processes info"
133
+ def watch(*args)
134
+ cmd = if `watch --version 2>&1`.chop > '0.2.0'
135
+ "watch -n 1 --color #{$0} i #{args * ' '}"
136
+ else
137
+ "watch -n 1 #{$0} i #{args * ' '}"
138
+ end
139
+
140
+ pid = Process.spawn(cmd)
141
+ Process.waitpid(pid)
142
+ rescue Interrupt
143
+ end
144
+
145
+ private
146
+
147
+ def error!(msg)
148
+ say msg, :red
149
+ exit 1
150
+ end
151
+
152
+ def print(msg, new_line = true)
153
+ say msg if msg && !msg.empty?
154
+ say if new_line
155
+ end
156
+
157
+ def log_trace(tag = '')
158
+ log_file = cmd(:logger_dev)
159
+ if log_file && File.exists?(log_file)
160
+ Process.exec "tail -n 100 -f #{log_file} | grep '#{tag}'"
161
+ else
162
+ error! "log file not found #{log_file.inspect}"
163
+ end
164
+ end
165
+
166
+ end
@@ -0,0 +1,79 @@
1
+ module Eye::Cli::Commands
2
+ private
3
+
4
+ def client
5
+ @client ||= Eye::Client.new(Eye::Local.socket_path)
6
+ end
7
+
8
+ def _cmd(cmd, *args)
9
+ client.command(cmd, *args)
10
+ rescue Errno::ECONNREFUSED, Errno::ENOENT
11
+ :not_started
12
+ end
13
+
14
+ def cmd(cmd, *args)
15
+ res = _cmd(cmd, *args)
16
+
17
+ if res == :not_started
18
+ error! "socket(#{Eye::Local.socket_path}) not found, did you `eye load`?"
19
+ elsif res == :timeouted
20
+ error! 'eye does not answer, timeouted...'
21
+ end
22
+
23
+ res
24
+ end
25
+
26
+ def say_load_result(res = {}, opts = {})
27
+ error!(res) unless res.is_a?(Hash)
28
+ say_filename = (res.size > 1)
29
+ say 'eye started!', :green if opts[:started]
30
+ error = false
31
+ res.each do |filename, _res|
32
+ say "#{filename}: ", nil, true if say_filename
33
+ show_load_message(_res, opts)
34
+ error = true if _res[:error]
35
+ end
36
+
37
+ exit(1) if error
38
+ end
39
+
40
+ def show_load_message(res, opts = {})
41
+ if res[:error]
42
+ say res[:message], :red
43
+ res[:backtrace].to_a.each{|line| say line, :red }
44
+ else
45
+ if opts[:syntax]
46
+ say 'config ok!', :green if !res[:empty]
47
+ else
48
+ say 'config loaded!', :green if !res[:empty]
49
+ end
50
+
51
+ if opts[:print_config]
52
+ require 'pp'
53
+ PP.pp res[:config], STDOUT, 150
54
+ end
55
+ end
56
+ end
57
+
58
+ def send_command(_cmd, *args)
59
+ res = cmd(_cmd, *args)
60
+ if res == :unknown_command
61
+ error! "unknown command :#{_cmd}"
62
+ elsif res == :corrupted_data
63
+ error! 'something crazy wrong, check eye logs!'
64
+ elsif res.is_a?(Hash)
65
+ if res[:error]
66
+ error! "Error: #{res[:error]}"
67
+ elsif res = res[:result]
68
+ if res == []
69
+ error! "command :#{_cmd}, objects not found!"
70
+ else
71
+ say "command :#{_cmd} sended to [#{res * ", "}]"
72
+ end
73
+ end
74
+ else
75
+ error! "unknown result #{res.inspect}"
76
+ end
77
+ end
78
+
79
+ end