reel-eye 0.4.1 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +4 -0
- data/CHANGES.md +17 -1
- data/README.md +3 -3
- data/Rakefile +8 -0
- data/bin/eye +0 -316
- data/bin/loader_eye +3 -3
- data/examples/test.eye +2 -2
- data/eye.gemspec +6 -10
- data/lib/eye.rb +4 -2
- data/lib/eye/application.rb +3 -3
- data/lib/eye/checker.rb +35 -7
- data/lib/eye/checker/cputime.rb +23 -0
- data/lib/eye/checker/file_touched.rb +15 -0
- data/lib/eye/checker/http.rb +7 -9
- data/lib/eye/checker/memory.rb +1 -1
- data/lib/eye/checker/runtime.rb +28 -0
- data/lib/eye/checker/socket.rb +4 -4
- data/lib/eye/cli.rb +166 -0
- data/lib/eye/cli/commands.rb +79 -0
- data/lib/eye/cli/render.rb +137 -0
- data/lib/eye/cli/server.rb +85 -0
- data/lib/eye/client.rb +3 -3
- data/lib/eye/config.rb +17 -14
- data/lib/eye/controller.rb +6 -10
- data/lib/eye/controller/commands.rb +6 -10
- data/lib/eye/controller/helpers.rb +1 -1
- data/lib/eye/controller/send_command.rb +5 -2
- data/lib/eye/controller/status.rb +38 -106
- data/lib/eye/dsl.rb +1 -1
- data/lib/eye/dsl/application_opts.rb +6 -2
- data/lib/eye/dsl/child_process_opts.rb +3 -2
- data/lib/eye/dsl/config_opts.rb +2 -2
- data/lib/eye/dsl/group_opts.rb +2 -1
- data/lib/eye/dsl/main.rb +4 -2
- data/lib/eye/dsl/opts.rb +11 -4
- data/lib/eye/dsl/validation.rb +49 -43
- data/lib/eye/group.rb +1 -1
- data/lib/eye/http.rb +4 -9
- data/lib/eye/http/router.rb +1 -1
- data/lib/eye/loader.rb +5 -9
- data/lib/eye/{settings.rb → local.rb} +1 -1
- data/lib/eye/logger.rb +5 -0
- data/lib/eye/notify.rb +12 -6
- data/lib/eye/notify/jabber.rb +2 -2
- data/lib/eye/process/child.rb +3 -1
- data/lib/eye/process/commands.rb +2 -2
- data/lib/eye/process/controller.rb +1 -1
- data/lib/eye/process/trigger.rb +1 -1
- data/lib/eye/sigar.rb +5 -0
- data/lib/eye/system.rb +8 -7
- data/lib/eye/system_resources.rb +46 -41
- data/lib/eye/trigger.rb +15 -8
- data/lib/eye/trigger/flapping.rb +1 -1
- data/lib/eye/trigger/stop_childs.rb +1 -1
- data/lib/eye/trigger/transition.rb +15 -0
- data/lib/eye/utils.rb +12 -0
- data/lib/eye/utils/leak_19.rb +7 -0
- data/lib/eye/utils/mini_active_support.rb +106 -0
- metadata +40 -17
- data/lib/eye/controller/show_history.rb +0 -63
- data/lib/eye/trigger/state.rb +0 -11
data/lib/eye.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Eye
|
2
|
-
VERSION = "0.
|
2
|
+
VERSION = "0.5"
|
3
3
|
ABOUT = "ReelEye v#{VERSION} (c) 2012-2013 @kostya"
|
4
4
|
PROCLINE = "reel-eye monitoring v#{VERSION}"
|
5
5
|
|
@@ -14,15 +14,17 @@ module Eye
|
|
14
14
|
autoload :Group, 'eye/group'
|
15
15
|
autoload :Dsl, 'eye/dsl'
|
16
16
|
autoload :Application, 'eye/application'
|
17
|
-
autoload :
|
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'
|
26
27
|
|
27
28
|
autoload :Http, 'eye/http'
|
29
|
+
autoload :Cli, 'eye/cli'
|
28
30
|
end
|
data/lib/eye/application.rb
CHANGED
@@ -33,14 +33,14 @@ class Eye::Application
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def status_data_short
|
36
|
-
h = Hash.new
|
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
|
-
|
43
|
-
{ name: @name, type: :application, state: str}
|
43
|
+
{ name: @name, type: :application, states: h}
|
44
44
|
end
|
45
45
|
|
46
46
|
def debug_data
|
data/lib/eye/checker.rb
CHANGED
@@ -1,23 +1,28 @@
|
|
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 =>
|
12
|
-
:ctime =>
|
13
|
-
: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]
|
24
|
+
param :initial_grace, [Fixnum, Float]
|
25
|
+
param :skip_initial_fails, [TrueClass, FalseClass]
|
21
26
|
|
22
27
|
def self.name_and_class(type)
|
23
28
|
type = type.to_sym
|
@@ -32,6 +37,9 @@ class Eye::Checker
|
|
32
37
|
def self.get_class(type)
|
33
38
|
klass = eval("Eye::Checker::#{TYPES[type]}") rescue nil
|
34
39
|
raise "Unknown checker #{type}" unless klass
|
40
|
+
if deps = klass.depends_on
|
41
|
+
Array(deps).each { |d| require d }
|
42
|
+
end
|
35
43
|
klass
|
36
44
|
end
|
37
45
|
|
@@ -50,9 +58,10 @@ class Eye::Checker
|
|
50
58
|
def initialize(pid, options = {}, process = nil)
|
51
59
|
@process = process
|
52
60
|
@pid = pid
|
53
|
-
@options = options
|
61
|
+
@options = options.dup
|
54
62
|
@type = options[:type]
|
55
63
|
@full_name = @process.full_name if @process
|
64
|
+
@initialized_at = Time.now
|
56
65
|
|
57
66
|
debug "create checker, with #{options}"
|
58
67
|
|
@@ -83,8 +92,16 @@ class Eye::Checker
|
|
83
92
|
end
|
84
93
|
|
85
94
|
def check
|
95
|
+
if initial_grace && (Time.now - @initialized_at < initial_grace)
|
96
|
+
debug 'skipped initial grace'
|
97
|
+
return true
|
98
|
+
else
|
99
|
+
@options[:initial_grace] = nil
|
100
|
+
end
|
101
|
+
|
86
102
|
@value = get_value_safe
|
87
|
-
@
|
103
|
+
@good_value = good?(value)
|
104
|
+
@values << {:value => @value, :good => @good_value}
|
88
105
|
|
89
106
|
result = true
|
90
107
|
@check_count += 1
|
@@ -94,6 +111,14 @@ class Eye::Checker
|
|
94
111
|
result = false if bad_count >= min_tries
|
95
112
|
end
|
96
113
|
|
114
|
+
if skip_initial_fails
|
115
|
+
if @good_value
|
116
|
+
@options[:skip_initial_fails] = nil
|
117
|
+
else
|
118
|
+
result = true
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
97
122
|
info "#{last_human_values} => #{result ? 'OK' : 'Fail'}"
|
98
123
|
result
|
99
124
|
|
@@ -166,12 +191,15 @@ class Eye::Checker
|
|
166
191
|
end
|
167
192
|
|
168
193
|
def self.register(base)
|
169
|
-
name = base.to_s.gsub(
|
194
|
+
name = base.to_s.gsub('Eye::Checker::', '')
|
170
195
|
type = name.underscore.to_sym
|
171
196
|
Eye::Checker::TYPES[type] = name
|
172
197
|
Eye::Checker.const_set(name, base)
|
173
198
|
end
|
174
199
|
|
200
|
+
def self.depends_on
|
201
|
+
end
|
202
|
+
|
175
203
|
class CustomCell < Eye::Checker
|
176
204
|
def self.inherited(base)
|
177
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
|
data/lib/eye/checker/http.rb
CHANGED
@@ -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)
|
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
|
59
|
-
matched = if
|
60
|
-
|
56
|
+
if pattern
|
57
|
+
matched = if pattern.is_a?(Regexp)
|
58
|
+
pattern === value[:result].body
|
61
59
|
else
|
62
|
-
value[:result].body.include?(
|
60
|
+
value[:result].body.include?(pattern.to_s)
|
63
61
|
end
|
64
|
-
value[:notice] = "missing '#{
|
62
|
+
value[:notice] = "missing '#{pattern.to_s}'" unless matched
|
65
63
|
matched
|
66
64
|
else
|
67
65
|
true
|
data/lib/eye/checker/memory.rb
CHANGED
@@ -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
|
data/lib/eye/checker/socket.rb
CHANGED
@@ -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] =
|
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
|
-
|
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
|
data/lib/eye/cli.rb
ADDED
@@ -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
|