ace-eye 0.6.1
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.
- checksums.yaml +7 -0
- data/.gitignore +38 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/CHANGES.md +77 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +212 -0
- data/Rakefile +35 -0
- data/bin/eye +5 -0
- data/bin/loader_eye +72 -0
- data/bin/runner +16 -0
- data/examples/dependency.eye +17 -0
- data/examples/notify.eye +19 -0
- data/examples/plugin/README.md +15 -0
- data/examples/plugin/main.eye +15 -0
- data/examples/plugin/plugin.rb +63 -0
- data/examples/process_thin.rb +29 -0
- data/examples/processes/em.rb +57 -0
- data/examples/processes/forking.rb +20 -0
- data/examples/processes/sample.rb +144 -0
- data/examples/processes/thin.ru +12 -0
- data/examples/puma.eye +29 -0
- data/examples/rbenv.eye +11 -0
- data/examples/sidekiq.eye +23 -0
- data/examples/test.eye +87 -0
- data/examples/thin-farm.eye +30 -0
- data/examples/unicorn.eye +39 -0
- data/eye.gemspec +40 -0
- data/lib/eye.rb +28 -0
- data/lib/eye/application.rb +73 -0
- data/lib/eye/checker.rb +258 -0
- data/lib/eye/checker/children_count.rb +44 -0
- data/lib/eye/checker/children_memory.rb +12 -0
- data/lib/eye/checker/cpu.rb +17 -0
- data/lib/eye/checker/cputime.rb +13 -0
- data/lib/eye/checker/file_ctime.rb +24 -0
- data/lib/eye/checker/file_size.rb +34 -0
- data/lib/eye/checker/file_touched.rb +15 -0
- data/lib/eye/checker/http.rb +96 -0
- data/lib/eye/checker/memory.rb +17 -0
- data/lib/eye/checker/nop.rb +6 -0
- data/lib/eye/checker/runtime.rb +18 -0
- data/lib/eye/checker/socket.rb +159 -0
- data/lib/eye/child_process.rb +101 -0
- data/lib/eye/cli.rb +185 -0
- data/lib/eye/cli/commands.rb +78 -0
- data/lib/eye/cli/render.rb +130 -0
- data/lib/eye/cli/server.rb +93 -0
- data/lib/eye/client.rb +32 -0
- data/lib/eye/config.rb +91 -0
- data/lib/eye/control.rb +2 -0
- data/lib/eye/controller.rb +54 -0
- data/lib/eye/controller/commands.rb +88 -0
- data/lib/eye/controller/helpers.rb +101 -0
- data/lib/eye/controller/load.rb +224 -0
- data/lib/eye/controller/options.rb +18 -0
- data/lib/eye/controller/send_command.rb +177 -0
- data/lib/eye/controller/status.rb +72 -0
- data/lib/eye/dsl.rb +53 -0
- data/lib/eye/dsl/application_opts.rb +39 -0
- data/lib/eye/dsl/chain.rb +12 -0
- data/lib/eye/dsl/child_process_opts.rb +13 -0
- data/lib/eye/dsl/config_opts.rb +55 -0
- data/lib/eye/dsl/group_opts.rb +32 -0
- data/lib/eye/dsl/helpers.rb +20 -0
- data/lib/eye/dsl/main.rb +51 -0
- data/lib/eye/dsl/opts.rb +151 -0
- data/lib/eye/dsl/process_opts.rb +36 -0
- data/lib/eye/dsl/pure_opts.rb +121 -0
- data/lib/eye/dsl/validation.rb +88 -0
- data/lib/eye/group.rb +140 -0
- data/lib/eye/group/chain.rb +81 -0
- data/lib/eye/loader.rb +10 -0
- data/lib/eye/local.rb +100 -0
- data/lib/eye/logger.rb +104 -0
- data/lib/eye/notify.rb +118 -0
- data/lib/eye/notify/jabber.rb +30 -0
- data/lib/eye/notify/mail.rb +48 -0
- data/lib/eye/process.rb +85 -0
- data/lib/eye/process/children.rb +60 -0
- data/lib/eye/process/commands.rb +280 -0
- data/lib/eye/process/config.rb +81 -0
- data/lib/eye/process/controller.rb +73 -0
- data/lib/eye/process/data.rb +78 -0
- data/lib/eye/process/monitor.rb +108 -0
- data/lib/eye/process/notify.rb +32 -0
- data/lib/eye/process/scheduler.rb +82 -0
- data/lib/eye/process/states.rb +86 -0
- data/lib/eye/process/states_history.rb +66 -0
- data/lib/eye/process/system.rb +97 -0
- data/lib/eye/process/trigger.rb +34 -0
- data/lib/eye/process/validate.rb +33 -0
- data/lib/eye/process/watchers.rb +66 -0
- data/lib/eye/reason.rb +20 -0
- data/lib/eye/server.rb +60 -0
- data/lib/eye/sigar.rb +5 -0
- data/lib/eye/system.rb +139 -0
- data/lib/eye/system_resources.rb +99 -0
- data/lib/eye/trigger.rb +136 -0
- data/lib/eye/trigger/check_dependency.rb +30 -0
- data/lib/eye/trigger/flapping.rb +41 -0
- data/lib/eye/trigger/stop_children.rb +17 -0
- data/lib/eye/trigger/transition.rb +15 -0
- data/lib/eye/trigger/wait_dependency.rb +49 -0
- data/lib/eye/utils.rb +45 -0
- data/lib/eye/utils/alive_array.rb +57 -0
- data/lib/eye/utils/celluloid_chain.rb +71 -0
- data/lib/eye/utils/celluloid_klass.rb +5 -0
- data/lib/eye/utils/leak_19.rb +10 -0
- data/lib/eye/utils/mini_active_support.rb +111 -0
- data/lib/eye/utils/pmap.rb +7 -0
- data/lib/eye/utils/tail.rb +20 -0
- metadata +398 -0
data/examples/rbenv.eye
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Eye.application "rbenv_example" do
|
2
|
+
env 'RBENV_ROOT' => '/usr/local/rbenv', 'PATH' => "/usr/local/rbenv/shims:/usr/local/rbenv/bin:#{ENV['PATH']}"
|
3
|
+
working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
4
|
+
|
5
|
+
process "some_process" do
|
6
|
+
pid_file "some.pid"
|
7
|
+
start_command "ruby some.rb"
|
8
|
+
daemonize true
|
9
|
+
stdall "some.log"
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Example: how to run sidekiq daemon
|
2
|
+
|
3
|
+
def sidekiq_process(proxy, name)
|
4
|
+
rails_env = proxy.env['RAILS_ENV']
|
5
|
+
|
6
|
+
proxy.process(name) do
|
7
|
+
start_command "ruby ./bin/sidekiq -e #{rails_env} -C ./config/sidekiq.#{rails_env}.yml"
|
8
|
+
pid_file "tmp/pids/#{name}.pid"
|
9
|
+
stdall "log/#{name}.log"
|
10
|
+
daemonize true
|
11
|
+
stop_signals [:QUIT, 5.seconds, :TERM, 5.seconds, :KILL]
|
12
|
+
|
13
|
+
check :cpu, :every => 30, :below => 100, :times => 5
|
14
|
+
check :memory, :every => 30, :below => 300.megabytes, :times => 5
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Eye.application :sidekiq_test do
|
19
|
+
working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
20
|
+
env "RAILS_ENV" => 'production'
|
21
|
+
|
22
|
+
sidekiq_process self, :sidekiq
|
23
|
+
end
|
data/examples/test.eye
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# load submodules, here just for example
|
2
|
+
Eye.load('./eye/*.rb')
|
3
|
+
|
4
|
+
# Eye self-configuration section
|
5
|
+
Eye.config do
|
6
|
+
logger '/tmp/eye.log'
|
7
|
+
end
|
8
|
+
|
9
|
+
# Adding application
|
10
|
+
Eye.application 'test' do
|
11
|
+
# All options inherits down to the config leafs.
|
12
|
+
# except `env`, which merging down
|
13
|
+
|
14
|
+
working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
15
|
+
stdall 'trash.log' # stdout,err logs for processes by default
|
16
|
+
env 'APP_ENV' => 'production' # global env for each processes
|
17
|
+
trigger :flapping, times: 10, within: 1.minute, retry_in: 10.minutes
|
18
|
+
check :cpu, every: 10.seconds, below: 100, times: 3 # global check for all processes
|
19
|
+
|
20
|
+
group 'samples' do
|
21
|
+
chain grace: 5.seconds # chained start-restart with 5s interval, one by one.
|
22
|
+
|
23
|
+
# eye daemonized process
|
24
|
+
process :sample1 do
|
25
|
+
pid_file '1.pid' # pid_path will be expanded with the working_dir
|
26
|
+
start_command 'ruby ./sample.rb'
|
27
|
+
|
28
|
+
# when no stop_command or stop_signals, default stop is [:TERM, 0.5, :KILL]
|
29
|
+
# default `restart` command is `stop; start`
|
30
|
+
|
31
|
+
daemonize true
|
32
|
+
stdall 'sample1.log'
|
33
|
+
|
34
|
+
# ensure the CPU is below 30% at least 3 out of the last 5 times checked
|
35
|
+
check :cpu, below: 30, times: [3, 5]
|
36
|
+
end
|
37
|
+
|
38
|
+
# self daemonized process
|
39
|
+
process :sample2 do
|
40
|
+
pid_file '2.pid'
|
41
|
+
start_command 'ruby ./sample.rb -d --pid 2.pid --log sample2.log'
|
42
|
+
stop_command 'kill -9 {PID}'
|
43
|
+
|
44
|
+
# ensure the memory is below 300Mb the last 3 times checked
|
45
|
+
check :memory, every: 20.seconds, below: 300.megabytes, times: 3
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# daemon with 3 children
|
50
|
+
process :forking do
|
51
|
+
pid_file 'forking.pid'
|
52
|
+
start_command 'ruby ./forking.rb start'
|
53
|
+
stop_command 'ruby forking.rb stop'
|
54
|
+
stdall 'forking.log'
|
55
|
+
|
56
|
+
start_timeout 10.seconds
|
57
|
+
stop_timeout 5.seconds
|
58
|
+
|
59
|
+
monitor_children do
|
60
|
+
restart_command 'kill -2 {PID}' # for this child process
|
61
|
+
check :memory, below: 300.megabytes, times: 3
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# eventmachine process, daemonized with eye
|
66
|
+
process :event_machine do |p|
|
67
|
+
pid_file 'em.pid'
|
68
|
+
start_command 'ruby em.rb'
|
69
|
+
stdout 'em.log'
|
70
|
+
daemonize true
|
71
|
+
stop_signals [:QUIT, 2.seconds, :KILL]
|
72
|
+
|
73
|
+
check :socket, addr: 'tcp://127.0.0.1:33221', every: 10.seconds, times: 2,
|
74
|
+
timeout: 1.second, send_data: 'ping', expect_data: /pong/
|
75
|
+
end
|
76
|
+
|
77
|
+
# thin process, self daemonized
|
78
|
+
process :thin do
|
79
|
+
pid_file 'thin.pid'
|
80
|
+
start_command 'bundle exec thin start -R thin.ru -p 33233 -d -l thin.log -P thin.pid'
|
81
|
+
stop_signals [:QUIT, 2.seconds, :TERM, 1.seconds, :KILL]
|
82
|
+
|
83
|
+
check :http, url: 'http://127.0.0.1:33233/hello', pattern: /World/,
|
84
|
+
every: 5.seconds, times: [2, 3], timeout: 1.second
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
RUBY = 'ruby'
|
2
|
+
BUNDLE = 'bundle'
|
3
|
+
|
4
|
+
Eye.load("process_thin.rb")
|
5
|
+
|
6
|
+
Eye.config do
|
7
|
+
logger "/tmp/eye.log"
|
8
|
+
end
|
9
|
+
|
10
|
+
Eye.app 'thin-farm' do
|
11
|
+
working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
12
|
+
env "RAILS_ENV" => "production"
|
13
|
+
|
14
|
+
stop_on_delete true # this option means, when we change pids and load config,
|
15
|
+
# deleted processes will be stops
|
16
|
+
|
17
|
+
trigger :flapping, :times => 10, :within => 1.minute
|
18
|
+
check :memory, :below => 60.megabytes, :every => 30.seconds, :times => 5
|
19
|
+
start_timeout 30.seconds
|
20
|
+
|
21
|
+
group :web do
|
22
|
+
chain :action => :restart, :grace => 5.seconds
|
23
|
+
chain :action => :start, :grace => 0.2.seconds
|
24
|
+
|
25
|
+
(5555..5560).each do |port|
|
26
|
+
thin self, port
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Example: now to run unicorn, and monitor its child processes
|
2
|
+
|
3
|
+
RUBY = '/usr/local/ruby/1.9.3/bin/ruby' # ruby on the server
|
4
|
+
RAILS_ENV = 'production'
|
5
|
+
|
6
|
+
Eye.application "rails_unicorn" do
|
7
|
+
env "RAILS_ENV" => RAILS_ENV
|
8
|
+
|
9
|
+
# unicorn requires to be `ruby` in path (for soft restart)
|
10
|
+
env "PATH" => "#{File.dirname(RUBY)}:#{ENV['PATH']}"
|
11
|
+
|
12
|
+
working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
13
|
+
|
14
|
+
process("unicorn") do
|
15
|
+
pid_file "tmp/pids/unicorn.pid"
|
16
|
+
start_command "#{RUBY} ./bin/unicorn -Dc ./config/unicorn.rb -E #{RAILS_ENV}"
|
17
|
+
stdall "log/unicorn.log"
|
18
|
+
|
19
|
+
# stop signals:
|
20
|
+
# http://unicorn.bogomips.org/SIGNALS.html
|
21
|
+
stop_signals [:TERM, 10.seconds]
|
22
|
+
|
23
|
+
# soft restart
|
24
|
+
restart_command "kill -USR2 {PID}"
|
25
|
+
|
26
|
+
check :cpu, :every => 30, :below => 80, :times => 3
|
27
|
+
check :memory, :every => 30, :below => 150.megabytes, :times => [3,5]
|
28
|
+
|
29
|
+
start_timeout 100.seconds
|
30
|
+
restart_grace 30.seconds
|
31
|
+
|
32
|
+
monitor_children do
|
33
|
+
stop_command "kill -QUIT {PID}"
|
34
|
+
check :cpu, :every => 30, :below => 80, :times => 3
|
35
|
+
check :memory, :every => 30, :below => 150.megabytes, :times => [3,5]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/eye.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path('../lib/eye', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.authors = "Konstantin Makarchev"
|
5
|
+
gem.email = "eye-rb@googlegroups.com"
|
6
|
+
|
7
|
+
gem.description = gem.summary = \
|
8
|
+
%q{FORK FOR TESTING. Process monitoring tool. Inspired from Bluepill and God. Requires Ruby(MRI) >= 1.9.3-p194. Uses Celluloid and Celluloid::IO.}
|
9
|
+
gem.homepage = "http://github.com/kostya/eye"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\).reject{|n| n =~ %r[png|gif\z]}.reject{|n| n =~ %r[^(test|spec|features)/]}
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
#gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "ace-eye"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Eye::VERSION
|
17
|
+
gem.license = "MIT"
|
18
|
+
|
19
|
+
gem.required_ruby_version = '>= 1.9.2'
|
20
|
+
gem.required_rubygems_version = '>= 1.3.6'
|
21
|
+
|
22
|
+
gem.add_dependency 'celluloid', '~> 0.15.0'
|
23
|
+
gem.add_dependency 'celluloid-io', '~> 0.15.0'
|
24
|
+
gem.add_dependency 'state_machine'
|
25
|
+
gem.add_dependency 'thor'
|
26
|
+
gem.add_dependency 'sigar', '~> 0.7.2'
|
27
|
+
|
28
|
+
gem.add_development_dependency 'rake'
|
29
|
+
gem.add_development_dependency 'rspec', '< 2.14'
|
30
|
+
gem.add_development_dependency 'rr'
|
31
|
+
gem.add_development_dependency 'ruby-graphviz'
|
32
|
+
gem.add_development_dependency 'forking'
|
33
|
+
gem.add_development_dependency 'fakeweb'
|
34
|
+
gem.add_development_dependency 'eventmachine', ">= 1.0.3"
|
35
|
+
gem.add_development_dependency 'sinatra'
|
36
|
+
gem.add_development_dependency 'thin'
|
37
|
+
gem.add_development_dependency 'xmpp4r'
|
38
|
+
gem.add_development_dependency 'coveralls'
|
39
|
+
gem.add_development_dependency 'simplecov', '>= 0.8.1'
|
40
|
+
end
|
data/lib/eye.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Eye
|
2
|
+
VERSION = "0.6.1"
|
3
|
+
ABOUT = "Eye v#{VERSION} (c) 2012-2014 @kostya"
|
4
|
+
PROCLINE = "eye monitoring v#{VERSION}"
|
5
|
+
|
6
|
+
autoload :Process, 'eye/process'
|
7
|
+
autoload :ChildProcess, 'eye/child_process'
|
8
|
+
autoload :Server, 'eye/server'
|
9
|
+
autoload :Logger, 'eye/logger'
|
10
|
+
autoload :System, 'eye/system'
|
11
|
+
autoload :SystemResources,'eye/system_resources'
|
12
|
+
autoload :Checker, 'eye/checker'
|
13
|
+
autoload :Trigger, 'eye/trigger'
|
14
|
+
autoload :Group, 'eye/group'
|
15
|
+
autoload :Dsl, 'eye/dsl'
|
16
|
+
autoload :Application, 'eye/application'
|
17
|
+
autoload :Local, 'eye/local'
|
18
|
+
autoload :Client, 'eye/client'
|
19
|
+
autoload :Utils, 'eye/utils'
|
20
|
+
autoload :Notify, 'eye/notify'
|
21
|
+
autoload :Config, 'eye/config'
|
22
|
+
autoload :Reason, 'eye/reason'
|
23
|
+
autoload :Sigar, 'eye/sigar'
|
24
|
+
|
25
|
+
autoload :Controller, 'eye/controller'
|
26
|
+
autoload :Control, 'eye/control'
|
27
|
+
autoload :Cli, 'eye/cli'
|
28
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
class Eye::Application
|
2
|
+
|
3
|
+
attr_reader :groups, :name
|
4
|
+
|
5
|
+
def initialize(name, config = {})
|
6
|
+
@groups = Eye::Utils::AliveArray.new
|
7
|
+
@name = name
|
8
|
+
@config = config
|
9
|
+
debug 'created'
|
10
|
+
end
|
11
|
+
|
12
|
+
def logger_tag
|
13
|
+
full_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def full_name
|
17
|
+
@name
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_group(group)
|
21
|
+
@groups << group
|
22
|
+
end
|
23
|
+
|
24
|
+
# sort processes in name order
|
25
|
+
def resort_groups
|
26
|
+
@groups = @groups.sort { |a, b| a.hidden ? 1 : (b.hidden ? -1 : (a.name <=> b.name)) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def status_data(debug = false)
|
30
|
+
h = { name: @name, type: :application, subtree: @groups.map{|gr| gr.status_data(debug) }}
|
31
|
+
h.merge!(debug: debug_data) if debug
|
32
|
+
h
|
33
|
+
end
|
34
|
+
|
35
|
+
def status_data_short
|
36
|
+
h = Hash.new
|
37
|
+
@groups.each do |c|
|
38
|
+
c.processes.each do |p|
|
39
|
+
h[p.state] ||= 0
|
40
|
+
h[p.state] += 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
{ name: @name, type: :application, states: h}
|
44
|
+
end
|
45
|
+
|
46
|
+
def debug_data
|
47
|
+
end
|
48
|
+
|
49
|
+
def send_command(command, *args)
|
50
|
+
info "send_command #{command}"
|
51
|
+
|
52
|
+
@groups.each do |group|
|
53
|
+
group.send_command(command, *args)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def alive?
|
58
|
+
true # emulate celluloid actor method
|
59
|
+
end
|
60
|
+
|
61
|
+
def sub_object?(obj)
|
62
|
+
res = @groups.include?(obj)
|
63
|
+
res = @groups.any?{|gr| gr.sub_object?(obj)} if !res
|
64
|
+
res
|
65
|
+
end
|
66
|
+
|
67
|
+
def processes
|
68
|
+
out = []
|
69
|
+
@groups.each{|gr| out += gr.processes.to_a }
|
70
|
+
Eye::Utils::AliveArray.new(out)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/lib/eye/checker.rb
ADDED
@@ -0,0 +1,258 @@
|
|
1
|
+
class Eye::Checker
|
2
|
+
include Eye::Dsl::Validation
|
3
|
+
|
4
|
+
autoload :Memory, 'eye/checker/memory'
|
5
|
+
autoload :Cpu, 'eye/checker/cpu'
|
6
|
+
autoload :Http, 'eye/checker/http'
|
7
|
+
autoload :FileCTime, 'eye/checker/file_ctime'
|
8
|
+
autoload :FileSize, 'eye/checker/file_size'
|
9
|
+
autoload :FileTouched,'eye/checker/file_touched'
|
10
|
+
autoload :Socket, 'eye/checker/socket'
|
11
|
+
autoload :Nop, 'eye/checker/nop'
|
12
|
+
autoload :Runtime, 'eye/checker/runtime'
|
13
|
+
autoload :Cputime, 'eye/checker/cputime'
|
14
|
+
autoload :ChildrenCount, 'eye/checker/children_count'
|
15
|
+
autoload :ChildrenMemory,'eye/checker/children_memory'
|
16
|
+
|
17
|
+
TYPES = {:memory => 'Memory', :cpu => 'Cpu', :http => 'Http',
|
18
|
+
:ctime => 'FileCTime', :fsize => 'FileSize', :file_touched => 'FileTouched',
|
19
|
+
:socket => 'Socket', :nop => 'Nop', :runtime => 'Runtime', :cputime => 'Cputime',
|
20
|
+
:children_count => "ChildrenCount", :children_memory => "ChildrenMemory" }
|
21
|
+
|
22
|
+
attr_accessor :value, :values, :options, :pid, :type, :check_count, :process
|
23
|
+
|
24
|
+
param :every, [Fixnum, Float], false, 5
|
25
|
+
param :times, [Fixnum, Array], nil, 1
|
26
|
+
param :fires, [Symbol, Array], nil, nil, [:stop, :restart, :unmonitor, :start, :delete, :nothing, :notify]
|
27
|
+
param :initial_grace, [Fixnum, Float]
|
28
|
+
param :skip_initial_fails, [TrueClass, FalseClass]
|
29
|
+
|
30
|
+
def self.name_and_class(type)
|
31
|
+
type = type.to_sym
|
32
|
+
return {:name => type, :type => type} if TYPES[type]
|
33
|
+
|
34
|
+
if type =~ /\A(.*?)_?[0-9]+\z/
|
35
|
+
ctype = $1.to_sym
|
36
|
+
return {:name => type, :type => ctype} if TYPES[ctype]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.get_class(type)
|
41
|
+
klass = eval("Eye::Checker::#{TYPES[type]}") rescue nil
|
42
|
+
raise "Unknown checker #{type}" unless klass
|
43
|
+
if deps = klass.requires
|
44
|
+
Array(deps).each { |d| require d }
|
45
|
+
end
|
46
|
+
klass
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.create(pid, options = {}, process = nil)
|
50
|
+
get_class(options[:type]).new(pid, options, process)
|
51
|
+
|
52
|
+
rescue Exception, Timeout::Error => ex
|
53
|
+
log_ex(ex)
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.validate!(options)
|
58
|
+
get_class(options[:type]).validate(options)
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize(pid, options = {}, process = nil)
|
62
|
+
@process = process
|
63
|
+
@pid = pid
|
64
|
+
@options = options.dup
|
65
|
+
@type = options[:type]
|
66
|
+
@full_name = @process.full_name if @process
|
67
|
+
@initialized_at = Time.now
|
68
|
+
|
69
|
+
debug "create checker, with #{options}"
|
70
|
+
|
71
|
+
@value = nil
|
72
|
+
@values = Eye::Utils::Tail.new(max_tries)
|
73
|
+
@check_count = 0
|
74
|
+
end
|
75
|
+
|
76
|
+
def inspect
|
77
|
+
"<#{self.class} @process='#{@full_name}' @options=#{@options} @pid=#{@pid}>"
|
78
|
+
end
|
79
|
+
|
80
|
+
def logger_tag
|
81
|
+
@process.logger.prefix
|
82
|
+
end
|
83
|
+
|
84
|
+
def logger_sub_tag
|
85
|
+
"check:#{check_name}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def last_human_values
|
89
|
+
h_values = @values.map do |v|
|
90
|
+
sign = v[:good] ? '' : '*'
|
91
|
+
sign + human_value(v[:value]).to_s
|
92
|
+
end
|
93
|
+
|
94
|
+
'[' + h_values * ', ' + ']'
|
95
|
+
end
|
96
|
+
|
97
|
+
def check
|
98
|
+
if initial_grace && (Time.now - @initialized_at < initial_grace)
|
99
|
+
debug 'skipped initial grace'
|
100
|
+
return true
|
101
|
+
else
|
102
|
+
@options[:initial_grace] = nil
|
103
|
+
end
|
104
|
+
|
105
|
+
@value = get_value_safe
|
106
|
+
@good_value = good?(value)
|
107
|
+
@values << {:value => @value, :good => @good_value}
|
108
|
+
|
109
|
+
result = true
|
110
|
+
@check_count += 1
|
111
|
+
|
112
|
+
if @values.size == max_tries
|
113
|
+
bad_count = @values.count{|v| !v[:good] }
|
114
|
+
result = false if bad_count >= min_tries
|
115
|
+
end
|
116
|
+
|
117
|
+
if skip_initial_fails
|
118
|
+
if @good_value
|
119
|
+
@options[:skip_initial_fails] = nil
|
120
|
+
else
|
121
|
+
result = true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
info "#{last_human_values} => #{result ? 'OK' : 'Fail'}"
|
126
|
+
result
|
127
|
+
|
128
|
+
rescue Exception, Timeout::Error => ex
|
129
|
+
log_ex(ex)
|
130
|
+
end
|
131
|
+
|
132
|
+
def get_value_safe
|
133
|
+
get_value
|
134
|
+
end
|
135
|
+
|
136
|
+
def get_value
|
137
|
+
raise NotImplementedError
|
138
|
+
end
|
139
|
+
|
140
|
+
def human_value(value)
|
141
|
+
value.to_s
|
142
|
+
end
|
143
|
+
|
144
|
+
# true if check ok
|
145
|
+
# false if check bad
|
146
|
+
def good?(value)
|
147
|
+
value
|
148
|
+
end
|
149
|
+
|
150
|
+
def check_name
|
151
|
+
@check_name ||= @type.to_s
|
152
|
+
end
|
153
|
+
|
154
|
+
def max_tries
|
155
|
+
@max_tries ||= if times
|
156
|
+
if times.is_a?(Array)
|
157
|
+
times[-1].to_i
|
158
|
+
else
|
159
|
+
times.to_i
|
160
|
+
end
|
161
|
+
else
|
162
|
+
1
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def min_tries
|
167
|
+
@min_tries ||= if times
|
168
|
+
if times.is_a?(Array)
|
169
|
+
times[0].to_i
|
170
|
+
else
|
171
|
+
max_tries
|
172
|
+
end
|
173
|
+
else
|
174
|
+
max_tries
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def previous_value
|
179
|
+
@values[-1][:value] if @values.present?
|
180
|
+
end
|
181
|
+
|
182
|
+
def run_in_process_context(p)
|
183
|
+
process.instance_exec(&p) if process.alive?
|
184
|
+
end
|
185
|
+
|
186
|
+
def fire
|
187
|
+
actions = fires ? Array(fires) : [:restart]
|
188
|
+
process.notify :warn, "Bounded #{check_name}: #{last_human_values} send to #{actions}"
|
189
|
+
|
190
|
+
actions.each do |action|
|
191
|
+
process.schedule action, Eye::Reason.new("bounded #{check_name}")
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def defer(&block)
|
196
|
+
Celluloid::Future.new(&block).value
|
197
|
+
end
|
198
|
+
|
199
|
+
class Defer < Eye::Checker
|
200
|
+
def get_value_safe
|
201
|
+
Celluloid::Future.new{ get_value }.value
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.register(base)
|
206
|
+
name = base.to_s.gsub('Eye::Checker::', '')
|
207
|
+
type = name.underscore.to_sym
|
208
|
+
Eye::Checker::TYPES[type] = name
|
209
|
+
Eye::Checker.const_set(name, base)
|
210
|
+
end
|
211
|
+
|
212
|
+
def self.requires
|
213
|
+
end
|
214
|
+
|
215
|
+
class CustomCell < Eye::Checker
|
216
|
+
def self.inherited(base)
|
217
|
+
super
|
218
|
+
register(base)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class Custom < Defer
|
223
|
+
def self.inherited(base)
|
224
|
+
super
|
225
|
+
register(base)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
class CustomDefer < Defer
|
230
|
+
def self.inherited(base)
|
231
|
+
super
|
232
|
+
register(base)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
class Measure < Eye::Checker
|
237
|
+
param :below, [Fixnum, Float]
|
238
|
+
param :above, [Fixnum, Float]
|
239
|
+
|
240
|
+
def good?(value)
|
241
|
+
return false if below && (value > below)
|
242
|
+
return false if above && (value < above)
|
243
|
+
true
|
244
|
+
end
|
245
|
+
|
246
|
+
def measure_str
|
247
|
+
if below && above
|
248
|
+
">#{human_value(above)}<#{human_value(below)}"
|
249
|
+
elsif below
|
250
|
+
"<#{human_value(below)}"
|
251
|
+
elsif above
|
252
|
+
">#{human_value(above)}"
|
253
|
+
else
|
254
|
+
'-'
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|