reel-eye 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/.travis.yml +1 -0
- data/CHANGES.md +19 -0
- data/Gemfile +1 -0
- data/README.md +10 -8
- data/bin/eye +30 -45
- data/examples/notify.eye +3 -2
- data/examples/puma.eye +2 -2
- data/examples/rbenv.eye +2 -2
- data/examples/sidekiq.eye +2 -2
- data/examples/thin-farm.eye +1 -0
- data/examples/unicorn.eye +1 -1
- data/eye.gemspec +1 -1
- data/lib/eye.rb +1 -1
- data/lib/eye/application.rb +1 -1
- data/lib/eye/checker.rb +10 -0
- data/lib/eye/controller/commands.rb +0 -5
- data/lib/eye/controller/helpers.rb +15 -1
- data/lib/eye/controller/load.rb +3 -0
- data/lib/eye/controller/options.rb +1 -1
- data/lib/eye/controller/send_command.rb +44 -14
- data/lib/eye/controller/status.rb +4 -8
- data/lib/eye/dsl/application_opts.rb +6 -2
- data/lib/eye/dsl/group_opts.rb +7 -6
- data/lib/eye/dsl/main.rb +1 -1
- data/lib/eye/dsl/opts.rb +22 -15
- data/lib/eye/dsl/process_opts.rb +3 -6
- data/lib/eye/dsl/pure_opts.rb +12 -1
- data/lib/eye/group.rb +1 -1
- data/lib/eye/http.rb +3 -0
- data/lib/eye/loader.rb +0 -10
- data/lib/eye/logger.rb +4 -4
- data/lib/eye/process/scheduler.rb +4 -0
- data/lib/eye/process/validate.rb +9 -1
- data/lib/eye/process/watchers.rb +3 -3
- data/lib/eye/settings.rb +17 -5
- data/lib/eye/system.rb +9 -0
- data/lib/eye/utils/alive_array.rb +23 -1
- metadata +267 -231
- checksums.yaml +0 -7
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGES.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
0.3.3.dev
|
2
|
+
---------
|
3
|
+
|
4
|
+
0.3.2
|
5
|
+
---------
|
6
|
+
* improve matching targers
|
7
|
+
* possibility to add many checkers with the same type per process (ex: checks :http_2, ...)
|
8
|
+
* add uid,gid options (only for ruby 2.0)
|
9
|
+
|
10
|
+
0.3.1
|
11
|
+
-----
|
12
|
+
* load multiple configs (folder,...) now not breaks on first error (each config loads separately)
|
13
|
+
* load ~/.eyeconfig with first eye load
|
14
|
+
* some concurrency fixes
|
15
|
+
* custom checker
|
16
|
+
|
17
|
+
0.3
|
18
|
+
---
|
19
|
+
* stable version
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
Eye
|
1
|
+
Eye
|
2
2
|
===
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/eye.png)](http://rubygems.org/gems/eye)
|
4
|
+
[![Build Status](https://secure.travis-ci.org/kostya/eye.png?branch=master)](http://travis-ci.org/kostya/eye)
|
5
|
+
[![Coverage Status](https://coveralls.io/repos/kostya/eye/badge.png?branch=master)](https://coveralls.io/r/kostya/eye?branch=master)
|
3
6
|
|
4
7
|
Process monitoring tool. An alternative to God and Bluepill. With Bluepill like config syntax. Requires MRI Ruby >= 1.9.3-p194. Uses Celluloid and Celluloid::IO.
|
5
8
|
|
@@ -150,6 +153,10 @@ Check config syntax:
|
|
150
153
|
|
151
154
|
$ eye c(heck) examples/test.eye
|
152
155
|
|
156
|
+
Config explain (for debug):
|
157
|
+
|
158
|
+
$ eye e(xplain) examples/test.eye
|
159
|
+
|
153
160
|
Log tracing:
|
154
161
|
|
155
162
|
$ eye trace
|
@@ -160,11 +167,6 @@ Quit monitoring:
|
|
160
167
|
|
161
168
|
$ eye q(uit)
|
162
169
|
|
163
|
-
Config
|
164
|
-
|
165
|
-
$ eye explain examples/test.eye
|
166
|
-
|
167
|
-
|
168
|
-
### Config api:
|
170
|
+
### Config options:
|
169
171
|
|
170
|
-
Waiting for pull requests ..., until that you can read `examples` and `spec/dsl` folders.
|
172
|
+
Waiting for pull requests here..., until that you can read `examples` and `spec/dsl` folders.
|
data/bin/eye
CHANGED
@@ -10,9 +10,7 @@ class Cli < Thor
|
|
10
10
|
|
11
11
|
desc "info [MASK]", "show process statuses"
|
12
12
|
def info(mask = nil)
|
13
|
-
|
14
|
-
puts res if res && !res.empty?
|
15
|
-
puts
|
13
|
+
print cmd(:info, mask)
|
16
14
|
end
|
17
15
|
|
18
16
|
desc "status ", "show process statuses"
|
@@ -25,31 +23,22 @@ class Cli < Thor
|
|
25
23
|
method_option :config, :type => :boolean, :aliases => "-c"
|
26
24
|
method_option :show_processes, :type => :boolean, :aliases => "-p"
|
27
25
|
def xinfo
|
28
|
-
|
29
|
-
puts res if res && !res.empty?
|
30
|
-
puts
|
26
|
+
print cmd(:xinfo, options[:config], options[:show_processes])
|
31
27
|
end
|
32
28
|
|
33
29
|
desc "oinfo", "onelined info"
|
34
30
|
def oinfo
|
35
|
-
|
36
|
-
puts res if res && !res.empty?
|
37
|
-
puts
|
31
|
+
print cmd(:oinfo)
|
38
32
|
end
|
39
33
|
|
40
34
|
desc "load [CONF, ...]", "load config (and start server if needed) (-f for foregraund start)"
|
41
35
|
method_option :foregraund, :type => :boolean, :aliases => "-f"
|
42
|
-
method_option :logger, :type => :string, :aliases => "-l"
|
43
36
|
def load(*configs)
|
44
37
|
configs.map!{ |c| File.expand_path(c) } if !configs.empty?
|
45
38
|
|
46
39
|
if options[:foregraund]
|
47
40
|
# in foregraund we stop another server, and run just 1 current config version
|
48
|
-
if configs.size != 1
|
49
|
-
say "foregraund expected only one config", :red
|
50
|
-
exit 1
|
51
|
-
end
|
52
|
-
|
41
|
+
error!("foregraund expected only one config") if configs.size != 1
|
53
42
|
server_start_foregraund(configs.first)
|
54
43
|
|
55
44
|
elsif server_started?
|
@@ -90,9 +79,7 @@ class Cli < Thor
|
|
90
79
|
|
91
80
|
desc "history TARGET[,...]", "show process states history"
|
92
81
|
def history(*targets)
|
93
|
-
|
94
|
-
puts res if res && !res.empty?
|
95
|
-
puts
|
82
|
+
print cmd(:history, *targets)
|
96
83
|
end
|
97
84
|
|
98
85
|
desc "trace [TARGET]", "tracing log for app,group or process"
|
@@ -146,6 +133,16 @@ class Cli < Thor
|
|
146
133
|
|
147
134
|
private
|
148
135
|
|
136
|
+
def error!(msg)
|
137
|
+
say msg, :red
|
138
|
+
exit 1
|
139
|
+
end
|
140
|
+
|
141
|
+
def print(msg, new_line = true)
|
142
|
+
say msg if msg && !msg.empty?
|
143
|
+
say if new_line
|
144
|
+
end
|
145
|
+
|
149
146
|
def client
|
150
147
|
@client ||= Eye::Client.new(Eye::Settings.socket_path)
|
151
148
|
end
|
@@ -160,11 +157,9 @@ private
|
|
160
157
|
res = _cmd(cmd, *args)
|
161
158
|
|
162
159
|
if res == :not_started
|
163
|
-
|
164
|
-
exit 1
|
160
|
+
error! "eye monitoring not found, did you start it?"
|
165
161
|
elsif res == :timeouted
|
166
|
-
|
167
|
-
exit 1
|
162
|
+
error! "eye does not answer, timeouted..."
|
168
163
|
end
|
169
164
|
|
170
165
|
res
|
@@ -175,18 +170,12 @@ private
|
|
175
170
|
end
|
176
171
|
|
177
172
|
def say_load_result(res = {}, opts = {})
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
say_filename = (res.size > 1)
|
185
|
-
say "eye started!", :green if opts[:started]
|
186
|
-
res.each do |filename, _res|
|
187
|
-
say "#{filename}: ", nil, true if say_filename
|
188
|
-
show_load_message(_res, opts)
|
189
|
-
end
|
173
|
+
error!(res) unless res.is_a?(Hash)
|
174
|
+
say_filename = (res.size > 1)
|
175
|
+
say "eye started!", :green if opts[:started]
|
176
|
+
res.each do |filename, _res|
|
177
|
+
say "#{filename}: ", nil, true if say_filename
|
178
|
+
show_load_message(_res, opts)
|
190
179
|
end
|
191
180
|
end
|
192
181
|
|
@@ -212,11 +201,11 @@ private
|
|
212
201
|
def send_command(_cmd, *args)
|
213
202
|
res = cmd(_cmd, *args)
|
214
203
|
if res == :unknown_command
|
215
|
-
|
204
|
+
error! "unknown command :#{_cmd}"
|
216
205
|
elsif res == :corrupred_marshal
|
217
|
-
|
206
|
+
error! "something crazy wrong, check eye logs!"
|
218
207
|
elsif res == []
|
219
|
-
|
208
|
+
error! "command :#{_cmd}, targets not found!"
|
220
209
|
else
|
221
210
|
say "command :#{_cmd} sended to [#{res * ", "}]"
|
222
211
|
end
|
@@ -227,7 +216,7 @@ private
|
|
227
216
|
if log_file && File.exists?(log_file)
|
228
217
|
Process.exec "tail -n 100 -f #{log_file} | grep '#{tag}'"
|
229
218
|
else
|
230
|
-
|
219
|
+
error! "log file not found #{log_file.inspect}"
|
231
220
|
end
|
232
221
|
end
|
233
222
|
|
@@ -250,8 +239,7 @@ private
|
|
250
239
|
|
251
240
|
def ensure_loader_path
|
252
241
|
unless loader_path
|
253
|
-
|
254
|
-
exit 1
|
242
|
+
error! "start monitoring needs to run under ruby with installed gem 'eye'"
|
255
243
|
end
|
256
244
|
end
|
257
245
|
|
@@ -275,18 +263,15 @@ private
|
|
275
263
|
Eye::Settings.ensure_eye_dir
|
276
264
|
|
277
265
|
ensure_stop_previous_server
|
278
|
-
|
279
|
-
args = []
|
280
|
-
args += ['-l', options[:logger]] if options[:logger]
|
281
266
|
|
267
|
+
args = []
|
282
268
|
pid = Process.spawn(ruby_path, loader_path, *args, :out => '/dev/null', :err => '/dev/null', :in => '/dev/null',
|
283
269
|
:chdir => '/', :pgroup => true)
|
284
270
|
Process.detach(pid)
|
285
271
|
File.open(Eye::Settings.pid_path, 'w'){|f| f.write(pid) }
|
286
272
|
|
287
273
|
unless wait_server
|
288
|
-
|
289
|
-
exit 1
|
274
|
+
error! "server not runned in 15 seconds, something crazy wrong"
|
290
275
|
end
|
291
276
|
|
292
277
|
configs.unshift(Eye::Settings.eyeconfig) if File.exists?(Eye::Settings.eyeconfig)
|
data/examples/notify.eye
CHANGED
data/examples/puma.eye
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
RUBY = '/usr/local/ruby/1.9.3-p392/bin/ruby'
|
2
2
|
RAILS_ENV = 'production'
|
3
|
-
ROOT =
|
3
|
+
ROOT = File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
4
4
|
CURRENT = File.expand_path(File.join(ROOT, %w{current}))
|
5
5
|
LOGS = File.expand_path(File.join(ROOT, %w{shared log}))
|
6
6
|
PIDS = File.expand_path(File.join(ROOT, %w{shared pids}))
|
@@ -12,7 +12,7 @@ end
|
|
12
12
|
|
13
13
|
Eye.application :super_app do
|
14
14
|
env 'RAILS_ENV' => RAILS_ENV
|
15
|
-
working_dir
|
15
|
+
working_dir ROOT
|
16
16
|
triggers :flapping, :times => 10, :within => 1.minute
|
17
17
|
|
18
18
|
process :puma do
|
data/examples/rbenv.eye
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Eye.application "rbenv_example" do
|
2
2
|
env 'RBENV_ROOT' => '/usr/local/rbenv', 'PATH' => "/usr/local/rbenv/shims:/usr/local/rbenv/bin:#{ENV['PATH']}"
|
3
|
-
working_dir
|
3
|
+
working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
4
4
|
|
5
5
|
process "some_process" do
|
6
6
|
pid_file "some.pid"
|
@@ -8,4 +8,4 @@ Eye.application "rbenv_example" do
|
|
8
8
|
daemonize true
|
9
9
|
stdall "some.log"
|
10
10
|
end
|
11
|
-
end
|
11
|
+
end
|
data/examples/sidekiq.eye
CHANGED
@@ -16,8 +16,8 @@ def sidekiq_process(proxy, name)
|
|
16
16
|
end
|
17
17
|
|
18
18
|
Eye.application :sidekiq_test do
|
19
|
-
working_dir
|
19
|
+
working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
20
20
|
env "RAILS_ENV" => 'production'
|
21
21
|
|
22
22
|
sidekiq_process self, :sidekiq
|
23
|
-
end
|
23
|
+
end
|
data/examples/thin-farm.eye
CHANGED
data/examples/unicorn.eye
CHANGED
@@ -5,7 +5,7 @@ RAILS_ENV = 'production'
|
|
5
5
|
|
6
6
|
Eye.application "rails_unicorn" do
|
7
7
|
env "RAILS_ENV" => RAILS_ENV, "PATH" => "#{File.dirname(RUBY)}:#{ENV['PATH']}"
|
8
|
-
working_dir
|
8
|
+
working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
9
9
|
|
10
10
|
process("unicorn") do
|
11
11
|
pid_file "tmp/pids/unicorn.pid"
|
data/eye.gemspec
CHANGED
@@ -30,7 +30,6 @@ Gem::Specification.new do |gem|
|
|
30
30
|
|
31
31
|
gem.add_development_dependency 'rake'
|
32
32
|
gem.add_development_dependency 'rspec'
|
33
|
-
gem.add_development_dependency 'simplecov'
|
34
33
|
gem.add_development_dependency 'rr'
|
35
34
|
gem.add_development_dependency 'ruby-graphviz'
|
36
35
|
gem.add_development_dependency 'forking'
|
@@ -39,4 +38,5 @@ Gem::Specification.new do |gem|
|
|
39
38
|
gem.add_development_dependency 'sinatra'
|
40
39
|
gem.add_development_dependency 'thin'
|
41
40
|
gem.add_development_dependency 'xmpp4r'
|
41
|
+
gem.add_development_dependency 'coveralls'
|
42
42
|
end
|
data/lib/eye.rb
CHANGED
data/lib/eye/application.rb
CHANGED
data/lib/eye/checker.rb
CHANGED
@@ -13,6 +13,16 @@ class Eye::Checker
|
|
13
13
|
|
14
14
|
attr_accessor :value, :values, :options, :pid, :type, :check_count
|
15
15
|
|
16
|
+
def self.name_and_class(type)
|
17
|
+
type = type.to_sym
|
18
|
+
return {:name => type, :type => type} if TYPES[type]
|
19
|
+
|
20
|
+
if type =~ /\A(.*?)_?[0-9]+\z/
|
21
|
+
ctype = $1.to_sym
|
22
|
+
return {:name => type, :type => ctype} if TYPES[ctype]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
16
26
|
def self.get_class(type)
|
17
27
|
klass = eval("Eye::Checker::#{TYPES[type]}") rescue nil
|
18
28
|
raise "Unknown checker #{type}" unless klass
|
@@ -6,10 +6,24 @@ module Eye::Controller::Helpers
|
|
6
6
|
$0 = str
|
7
7
|
end
|
8
8
|
|
9
|
+
def save_cache
|
10
|
+
File.open(Eye::Settings.cache_path, 'w') { |f| f.write(cache_str) }
|
11
|
+
rescue => ex
|
12
|
+
warn "save cache crashed with #{ex.message}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def cache_str
|
16
|
+
all_processes.map{ |p| "#{p.full_name}=#{p.state}" } * "\n"
|
17
|
+
end
|
18
|
+
|
9
19
|
def process_by_name(name)
|
10
20
|
all_processes.detect{|c| c.name == name}
|
11
21
|
end
|
12
22
|
|
23
|
+
def process_by_full_name(name)
|
24
|
+
all_processes.detect{|c| c.full_name == name }
|
25
|
+
end
|
26
|
+
|
13
27
|
def group_by_name(name)
|
14
28
|
all_groups.detect{|c| c.name == name}
|
15
29
|
end
|
@@ -58,4 +72,4 @@ module Eye::Controller::Helpers
|
|
58
72
|
res
|
59
73
|
end
|
60
74
|
|
61
|
-
end
|
75
|
+
end
|
data/lib/eye/controller/load.rb
CHANGED
@@ -22,6 +22,7 @@ module Eye::Controller::Load
|
|
22
22
|
end
|
23
23
|
|
24
24
|
set_proc_line
|
25
|
+
save_cache
|
25
26
|
|
26
27
|
res
|
27
28
|
end
|
@@ -35,6 +36,8 @@ private
|
|
35
36
|
{ :error => false, :config => yield }
|
36
37
|
|
37
38
|
rescue Eye::Dsl::Error, Exception, NoMethodError => ex
|
39
|
+
raise if ex.class.to_s.include?('RR') # skip RR exceptions
|
40
|
+
|
38
41
|
error "load: config error <#{filename}>: #{ex.message}"
|
39
42
|
|
40
43
|
# filter backtrace for user output
|
@@ -18,7 +18,7 @@ module Eye::Controller::Options
|
|
18
18
|
def set_opt_http(params = {})
|
19
19
|
if params[:enable]
|
20
20
|
if @http
|
21
|
-
if params[:host] != @http.host || params[:
|
21
|
+
if params[:host] != @http.host || params[:port].to_i != @http.port
|
22
22
|
stop_http
|
23
23
|
start_http(params[:host], params[:port])
|
24
24
|
end
|
@@ -4,7 +4,9 @@ module Eye::Controller::SendCommand
|
|
4
4
|
matched_objects(*obj_strs) do |obj|
|
5
5
|
if command.to_sym == :delete
|
6
6
|
remove_object_from_tree(obj)
|
7
|
-
|
7
|
+
|
8
|
+
set_proc_line
|
9
|
+
save_cache
|
8
10
|
end
|
9
11
|
|
10
12
|
obj.send_command(command)
|
@@ -61,32 +63,44 @@ private
|
|
61
63
|
return [] if obj_strs.blank?
|
62
64
|
return @applications.dup if obj_strs.size == 1 && (obj_strs[0].strip == 'all' || obj_strs[0].strip == '*')
|
63
65
|
|
64
|
-
res =
|
66
|
+
res = Eye::Utils::AliveArray.new
|
65
67
|
obj_strs.map{|c| c.split(",")}.flatten.each do |mask|
|
66
|
-
res += find_objects_by_mask(mask)
|
68
|
+
res += find_objects_by_mask(mask.to_s.strip)
|
67
69
|
end
|
70
|
+
res
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_objects_by_mask(mask)
|
74
|
+
res = find_all_objects_by_mask(mask)
|
68
75
|
|
69
76
|
if res.size > 1
|
70
|
-
|
77
|
+
final = Eye::Utils::AliveArray.new
|
71
78
|
|
72
|
-
|
79
|
+
if mask[-1] != '*'
|
80
|
+
# try to find exactly matched
|
81
|
+
r = right_regexp(mask)
|
82
|
+
res.each do |obj|
|
83
|
+
final << obj if obj.full_name =~ r
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
return final if final.present?
|
88
|
+
|
89
|
+
# remove inherited targets
|
73
90
|
res.each do |obj|
|
74
91
|
sub_object = res.any?{|a| a.sub_object?(obj) }
|
75
92
|
final << obj unless sub_object
|
76
93
|
end
|
77
94
|
|
78
95
|
res = final
|
79
|
-
end
|
96
|
+
end
|
80
97
|
|
81
|
-
res
|
98
|
+
res
|
82
99
|
end
|
83
100
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
res = []
|
88
|
-
str = Regexp.escape(mask).gsub('\*', '.*?')
|
89
|
-
r = %r{\A#{str}}
|
101
|
+
def find_all_objects_by_mask(mask)
|
102
|
+
res = Eye::Utils::AliveArray
|
103
|
+
r = left_regexp(mask)
|
90
104
|
|
91
105
|
# find app
|
92
106
|
res = @applications.select{|a| a.name =~ r || a.full_name =~ r }
|
@@ -102,9 +116,16 @@ private
|
|
102
116
|
gr.processes.each do |p|
|
103
117
|
res << p if p.name =~ r || p.full_name =~ r
|
104
118
|
|
119
|
+
# child matching
|
105
120
|
if p.childs.present?
|
106
|
-
|
121
|
+
childs = p.childs.values
|
122
|
+
res += childs.select do |ch|
|
123
|
+
name = ch.name rescue ''
|
124
|
+
full_name = ch.full_name rescue ''
|
125
|
+
name =~ r || full_name =~ r
|
126
|
+
end
|
107
127
|
end
|
128
|
+
|
108
129
|
end
|
109
130
|
end
|
110
131
|
end
|
@@ -112,4 +133,13 @@ private
|
|
112
133
|
res
|
113
134
|
end
|
114
135
|
|
136
|
+
def left_regexp(mask)
|
137
|
+
str = Regexp.escape(mask).gsub('\*', '.*?')
|
138
|
+
%r|\A#{str}|
|
139
|
+
end
|
140
|
+
|
141
|
+
def right_regexp(mask)
|
142
|
+
str = Regexp.escape(mask).gsub('\*', '.*?')
|
143
|
+
%r|#{str}\z|
|
144
|
+
end
|
115
145
|
end
|