eye 0.3.2 → 0.4

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +1 -1
  4. data/.travis.yml +3 -1
  5. data/CHANGES.md +11 -2
  6. data/Gemfile +1 -0
  7. data/README.md +18 -14
  8. data/Rakefile +10 -3
  9. data/bin/eye +41 -27
  10. data/examples/process_thin.rb +1 -1
  11. data/examples/processes/em.rb +2 -2
  12. data/examples/processes/forking.rb +2 -2
  13. data/examples/processes/sample.rb +5 -5
  14. data/examples/rbenv.eye +1 -1
  15. data/examples/sidekiq.eye +2 -2
  16. data/examples/test.eye +10 -6
  17. data/examples/thin-farm.eye +1 -1
  18. data/examples/unicorn.eye +1 -1
  19. data/eye.gemspec +13 -7
  20. data/lib/eye.rb +6 -6
  21. data/lib/eye/application.rb +9 -6
  22. data/lib/eye/checker.rb +51 -21
  23. data/lib/eye/checker/file_size.rb +1 -1
  24. data/lib/eye/checker/http.rb +3 -3
  25. data/lib/eye/checker/memory.rb +1 -1
  26. data/lib/eye/checker/socket.rb +6 -6
  27. data/lib/eye/child_process.rb +7 -11
  28. data/lib/eye/client.rb +6 -6
  29. data/lib/eye/config.rb +2 -2
  30. data/lib/eye/controller.rb +11 -8
  31. data/lib/eye/controller/commands.rb +8 -9
  32. data/lib/eye/controller/helpers.rb +1 -0
  33. data/lib/eye/controller/load.rb +11 -7
  34. data/lib/eye/controller/send_command.rb +44 -19
  35. data/lib/eye/controller/show_history.rb +8 -7
  36. data/lib/eye/controller/status.rb +39 -26
  37. data/lib/eye/dsl.rb +3 -3
  38. data/lib/eye/dsl/application_opts.rb +4 -4
  39. data/lib/eye/dsl/config_opts.rb +4 -4
  40. data/lib/eye/dsl/helpers.rb +2 -2
  41. data/lib/eye/dsl/main.rb +2 -2
  42. data/lib/eye/dsl/opts.rb +19 -14
  43. data/lib/eye/dsl/process_opts.rb +1 -1
  44. data/lib/eye/dsl/pure_opts.rb +2 -2
  45. data/lib/eye/dsl/validation.rb +7 -5
  46. data/lib/eye/group.rb +17 -11
  47. data/lib/eye/group/chain.rb +3 -3
  48. data/lib/eye/loader.rb +8 -6
  49. data/lib/eye/logger.rb +14 -5
  50. data/lib/eye/notify.rb +13 -7
  51. data/lib/eye/notify/jabber.rb +2 -2
  52. data/lib/eye/notify/mail.rb +2 -2
  53. data/lib/eye/process.rb +10 -13
  54. data/lib/eye/process/child.rb +1 -1
  55. data/lib/eye/process/commands.rb +34 -32
  56. data/lib/eye/process/config.rb +17 -12
  57. data/lib/eye/process/controller.rb +3 -6
  58. data/lib/eye/process/data.rb +16 -5
  59. data/lib/eye/process/monitor.rb +12 -5
  60. data/lib/eye/process/notify.rb +1 -1
  61. data/lib/eye/process/scheduler.rb +3 -3
  62. data/lib/eye/process/states.rb +10 -13
  63. data/lib/eye/process/states_history.rb +3 -3
  64. data/lib/eye/process/system.rb +17 -21
  65. data/lib/eye/process/trigger.rb +11 -30
  66. data/lib/eye/process/watchers.rb +9 -9
  67. data/lib/eye/server.rb +14 -6
  68. data/lib/eye/settings.rb +4 -4
  69. data/lib/eye/system.rb +10 -7
  70. data/lib/eye/system_resources.rb +4 -4
  71. data/lib/eye/trigger.rb +58 -21
  72. data/lib/eye/trigger/flapping.rb +24 -4
  73. data/lib/eye/trigger/state.rb +28 -0
  74. data/lib/eye/utils/alive_array.rb +1 -1
  75. data/lib/eye/utils/celluloid_klass.rb +5 -0
  76. data/lib/eye/utils/pmap.rb +7 -0
  77. data/lib/eye/utils/tail.rb +1 -1
  78. metadata +39 -23
  79. data/lib/eye/utils/leak_19.rb +0 -7
@@ -1,5 +1,5 @@
1
1
  # load submodules, here just for example
2
- Eye.load("./eye/*.rb")
2
+ Eye.load("./eye/*.rb")
3
3
 
4
4
  # Eye self-configuration section
5
5
  Eye.config do
@@ -24,6 +24,10 @@ Eye.application "test" do
24
24
  process :sample1 do
25
25
  pid_file "1.pid" # pid_path will be expanded with the working_dir
26
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
+
27
31
  daemonize true
28
32
  stdall "sample1.log"
29
33
 
@@ -49,13 +53,13 @@ Eye.application "test" do
49
53
 
50
54
  start_timeout 5.seconds
51
55
  stop_grace 5.seconds
52
-
56
+
53
57
  monitor_children do
54
58
  restart_command "kill -2 {PID}" # for this child process
55
59
  checks :memory, :below => 300.megabytes, :times => 3
56
60
  end
57
61
  end
58
-
62
+
59
63
  # eventmachine process, daemonized with eye
60
64
  process :event_machine do |p|
61
65
  pid_file 'em.pid'
@@ -63,8 +67,8 @@ Eye.application "test" do
63
67
  stdout 'em.log'
64
68
  daemonize true
65
69
  stop_signals [:QUIT, 2.seconds, :KILL]
66
-
67
- checks :socket, :addr => "tcp://127.0.0.1:33221", :every => 10.seconds, :times => 2,
70
+
71
+ checks :socket, :addr => "tcp://127.0.0.1:33221", :every => 10.seconds, :times => 2,
68
72
  :timeout => 1.second, :send_data => "ping", :expect_data => /pong/
69
73
  end
70
74
 
@@ -74,7 +78,7 @@ Eye.application "test" do
74
78
  start_command "bundle exec thin start -R thin.ru -p 33233 -d -l thin.log -P thin.pid"
75
79
  stop_signals [:QUIT, 2.seconds, :TERM, 1.seconds, :KILL]
76
80
 
77
- checks :http, :url => "http://127.0.0.1:33233/hello", :pattern => /World/, :every => 5.seconds,
81
+ checks :http, :url => "http://127.0.0.1:33233/hello", :pattern => /World/, :every => 5.seconds,
78
82
  :times => [2, 3], :timeout => 1.second
79
83
  end
80
84
 
@@ -11,7 +11,7 @@ Eye.app 'thin-farm' do
11
11
  working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
12
12
  env "RAILS_ENV" => "production"
13
13
 
14
- stop_on_delete true # this option means, when we change pids and load config,
14
+ stop_on_delete true # this option means, when we change pids and load config,
15
15
  # deleted processes will be stops
16
16
 
17
17
  triggers :flapping, :times => 10, :within => 1.minute
@@ -6,7 +6,7 @@ RAILS_ENV = 'production'
6
6
  Eye.application "rails_unicorn" do
7
7
  env "RAILS_ENV" => RAILS_ENV, "PATH" => "#{File.dirname(RUBY)}:#{ENV['PATH']}"
8
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"
12
12
  start_command "#{RUBY} ./bin/unicorn -Dc ./config/unicorn.rb -E #{RAILS_ENV}"
@@ -17,16 +17,21 @@ Gem::Specification.new do |gem|
17
17
  gem.license = "MIT"
18
18
 
19
19
  gem.required_ruby_version = '>= 1.9.2' # because of celluloid
20
- gem.required_rubygems_version = '>= 1.3.6'
21
-
22
- gem.add_dependency 'celluloid', '~> 0.13.0'
23
- gem.add_dependency 'celluloid-io', '~> 0.13.0'
24
- gem.add_dependency 'state_machine', '< 1.2'
25
- gem.add_dependency 'activesupport', '~> 3.2.0'
20
+ gem.required_rubygems_version = '>= 1.3.6'
21
+
22
+ gem.add_dependency 'celluloid', '~> 0.14.0'
23
+ gem.add_dependency 'celluloid-io', '~> 0.14.0'
24
+ gem.add_dependency 'state_machine'
26
25
  gem.add_dependency 'thor'
27
26
 
27
+ if RUBY_VERSION == '1.9.2'
28
+ gem.add_dependency 'activesupport', '>= 3', '< 4.0'
29
+ else
30
+ gem.add_dependency 'activesupport', '>= 3'
31
+ end
32
+
28
33
  gem.add_development_dependency 'rake'
29
- gem.add_development_dependency 'rspec'
34
+ gem.add_development_dependency 'rspec', '< 2.14'
30
35
  gem.add_development_dependency 'rr'
31
36
  gem.add_development_dependency 'ruby-graphviz'
32
37
  gem.add_development_dependency 'forking'
@@ -36,4 +41,5 @@ Gem::Specification.new do |gem|
36
41
  gem.add_development_dependency 'thin'
37
42
  gem.add_development_dependency 'xmpp4r'
38
43
  gem.add_development_dependency 'coveralls'
44
+ gem.add_development_dependency 'parallel_tests'
39
45
  end
data/lib/eye.rb CHANGED
@@ -1,5 +1,5 @@
1
- module Eye
2
- VERSION = "0.3.2"
1
+ module Eye
2
+ VERSION = "0.4"
3
3
  ABOUT = "Eye v#{VERSION} (c) 2012-2013 @kostya"
4
4
  PROCLINE = "eye monitoring v#{VERSION}"
5
5
 
@@ -8,19 +8,19 @@ module Eye
8
8
  autoload :Server, 'eye/server'
9
9
  autoload :Logger, 'eye/logger'
10
10
  autoload :System, 'eye/system'
11
- autoload :SystemResources,'eye/system_resources'
11
+ autoload :SystemResources,'eye/system_resources'
12
12
  autoload :Checker, 'eye/checker'
13
13
  autoload :Trigger, 'eye/trigger'
14
14
  autoload :Group, 'eye/group'
15
- autoload :Dsl, 'eye/dsl'
16
- autoload :Application, 'eye/application'
15
+ autoload :Dsl, 'eye/dsl'
16
+ autoload :Application, 'eye/application'
17
17
  autoload :Settings, 'eye/settings'
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
-
23
+
24
24
  autoload :Controller, 'eye/controller'
25
25
  autoload :Control, 'eye/control'
26
26
  end
@@ -2,16 +2,17 @@ class Eye::Application
2
2
 
3
3
  attr_reader :groups, :name
4
4
 
5
- include Eye::Logger::Helpers
6
-
7
5
  def initialize(name, config = {})
8
6
  @groups = Eye::Utils::AliveArray.new
9
7
  @name = name
10
- @logger = Eye::Logger.new(full_name)
11
8
  @config = config
12
9
  debug 'created'
13
10
  end
14
11
 
12
+ def logger_tag
13
+ full_name
14
+ end
15
+
15
16
  def full_name
16
17
  @name
17
18
  end
@@ -37,7 +38,7 @@ class Eye::Application
37
38
 
38
39
  def status_data_short
39
40
  h = Hash.new 0
40
- @groups.each do |c|
41
+ @groups.each do |c|
41
42
  c.processes.each do |p|
42
43
  h[p.state] += 1
43
44
  end
@@ -51,7 +52,7 @@ class Eye::Application
51
52
 
52
53
  def send_command(command, *args)
53
54
  info "send_command #{command}"
54
-
55
+
55
56
  @groups.each do |group|
56
57
  group.send_command(command, *args)
57
58
  end
@@ -68,7 +69,9 @@ class Eye::Application
68
69
  end
69
70
 
70
71
  def processes
71
- Eye::Utils::AliveArray.new(@groups.map{|gr| gr.processes.to_a }.flatten)
72
+ out = []
73
+ @groups.each{|gr| out += gr.processes.to_a }
74
+ Eye::Utils::AliveArray.new(out)
72
75
  end
73
76
 
74
77
  end
@@ -1,5 +1,4 @@
1
1
  class Eye::Checker
2
- include Eye::Logger::Helpers
3
2
 
4
3
  autoload :Memory, 'eye/checker/memory'
5
4
  autoload :Cpu, 'eye/checker/cpu'
@@ -8,10 +7,15 @@ class Eye::Checker
8
7
  autoload :FileSize, 'eye/checker/file_size'
9
8
  autoload :Socket, 'eye/checker/socket'
10
9
 
11
- TYPES = {:memory => "Memory", :cpu => "Cpu", :http => "Http",
10
+ TYPES = {:memory => "Memory", :cpu => "Cpu", :http => "Http",
12
11
  :ctime => "FileCTime", :fsize => "FileSize", :socket => "Socket"}
13
12
 
14
- attr_accessor :value, :values, :options, :pid, :type, :check_count
13
+ attr_accessor :value, :values, :options, :pid, :type, :check_count, :process
14
+
15
+ extend Eye::Dsl::Validation
16
+ param :every, [Fixnum, Float], false, 5
17
+ param :times, [Fixnum, Array], nil, 1
18
+ param :fire, Symbol, nil, nil, [:stop, :restart, :unmonitor, :nothing]
15
19
 
16
20
  def self.name_and_class(type)
17
21
  type = type.to_sym
@@ -20,7 +24,7 @@ class Eye::Checker
20
24
  if type =~ /\A(.*?)_?[0-9]+\z/
21
25
  ctype = $1.to_sym
22
26
  return {:name => type, :type => ctype} if TYPES[ctype]
23
- end
27
+ end
24
28
  end
25
29
 
26
30
  def self.get_class(type)
@@ -29,29 +33,41 @@ class Eye::Checker
29
33
  klass
30
34
  end
31
35
 
32
- def self.create(pid, options = {}, logger_prefix = nil)
33
- get_class(options[:type]).new(pid, options, logger_prefix)
36
+ def self.create(pid, options = {}, process = nil)
37
+ get_class(options[:type]).new(pid, options, process)
34
38
  end
35
39
 
36
40
  def self.validate!(options)
37
41
  get_class(options[:type]).validate(options)
38
42
  end
39
43
 
40
- def initialize(pid, options = {}, logger_prefix = nil)
44
+ def initialize(pid, options = {}, process = nil)
45
+ @process = process
41
46
  @pid = pid
42
47
  @options = options
43
48
  @type = options[:type]
44
49
 
45
- @logger = Eye::Logger.new(logger_prefix, "check:#{check_name}")
46
50
  debug "create checker, with #{options}"
47
-
51
+
48
52
  @value = nil
49
53
  @values = Eye::Utils::Tail.new(max_tries)
50
54
  @check_count = 0
51
55
  end
52
56
 
57
+ def inspect
58
+ "<#{self.class} @process='#{@process.full_name}' @options=#{@options} @pid=#{@pid}>"
59
+ end
60
+
61
+ def logger_tag
62
+ @process.logger.prefix
63
+ end
64
+
65
+ def logger_sub_tag
66
+ "check:#{check_name}"
67
+ end
68
+
53
69
  def last_human_values
54
- h_values = @values.map do |v|
70
+ h_values = @values.map do |v|
55
71
  sign = v[:good] ? '' : '*'
56
72
  sign + human_value(v[:value]).to_s
57
73
  end
@@ -90,7 +106,7 @@ class Eye::Checker
90
106
  # true if check ok
91
107
  # false if check bad
92
108
  def good?(value)
93
- raise 'Realize me'
109
+ value
94
110
  end
95
111
 
96
112
  def check_name
@@ -106,7 +122,7 @@ class Eye::Checker
106
122
  end
107
123
  else
108
124
  1
109
- end
125
+ end
110
126
  end
111
127
 
112
128
  def min_tries
@@ -125,10 +141,13 @@ class Eye::Checker
125
141
  @values[-1][:value] if @values.present?
126
142
  end
127
143
 
128
- extend Eye::Dsl::Validation
129
- param :every, [Fixnum, Float], false, 5
130
- param :times, [Fixnum, Array]
131
- param :fire, Symbol, nil, nil, [:stop, :restart, :unmonitor, :nothing]
144
+ def run_in_process_context(p)
145
+ process.instance_exec(&p) if process.alive?
146
+ end
147
+
148
+ def defer(&block)
149
+ Celluloid::Future.new(&block).value
150
+ end
132
151
 
133
152
  class Defer < Eye::Checker
134
153
  def get_value_safe
@@ -136,13 +155,24 @@ class Eye::Checker
136
155
  end
137
156
  end
138
157
 
139
- class Custom < Defer
158
+ def self.register(base)
159
+ name = base.to_s.gsub("Eye::Checker::", '')
160
+ type = name.underscore.to_sym
161
+ Eye::Checker::TYPES[type] = name
162
+ Eye::Checker.const_set(name, base)
163
+ end
164
+
165
+ class Custom < Eye::Checker
166
+ def self.inherited(base)
167
+ super
168
+ register(base)
169
+ end
170
+ end
171
+
172
+ class CustomDefer < Defer
140
173
  def self.inherited(base)
141
174
  super
142
- name = base.to_s
143
- type = name.underscore.to_sym
144
- Eye::Checker::TYPES[type] = name
145
- Eye::Checker.const_set(name, base)
175
+ register(base)
146
176
  end
147
177
  end
148
178
  end
@@ -2,7 +2,7 @@ class Eye::Checker::FileSize < Eye::Checker
2
2
 
3
3
  # Check that file size changed (log for example)
4
4
 
5
- # checks :fsize, :every => 5.seconds, :file => "/tmp/1.log", :times => [3,5],
5
+ # checks :fsize, :every => 5.seconds, :file => "/tmp/1.log", :times => [3,5],
6
6
  # :below => 30.kilobytes, :above => 10.kilobytes
7
7
 
8
8
  param :file, [String], true
@@ -28,7 +28,7 @@ class Eye::Checker::Http < Eye::Checker::Defer
28
28
  @open_timeout = (open_timeout || 3).to_f
29
29
  @read_timeout = (read_timeout || timeout || 15).to_f
30
30
  end
31
-
31
+
32
32
  def get_value
33
33
  res = session.start{ |http| http.get(@uri.request_uri) }
34
34
  {:result => res}
@@ -52,7 +52,7 @@ class Eye::Checker::Http < Eye::Checker::Defer
52
52
  return false unless value[:result]
53
53
 
54
54
  unless value[:result].kind_of?(@kind)
55
- return false
55
+ return false
56
56
  end
57
57
 
58
58
  if @pattern
@@ -79,7 +79,7 @@ class Eye::Checker::Http < Eye::Checker::Defer
79
79
  msg += "<#{value[:notice]}>" if value[:notice]
80
80
  msg
81
81
  end
82
- end
82
+ end
83
83
 
84
84
  private
85
85
 
@@ -3,7 +3,7 @@ class Eye::Checker::Memory < Eye::Checker
3
3
  # checks :memory, :every => 3.seconds, :below => 80.megabytes, :times => [3,5]
4
4
 
5
5
  param :below, [Fixnum, Float], true
6
-
6
+
7
7
  def check_name
8
8
  @check_name ||= "memory(#{human_value(below)})"
9
9
  end
@@ -68,18 +68,18 @@ class Eye::Checker::Socket < Eye::Checker::Defer
68
68
  return false if !value[:result]
69
69
 
70
70
  if expect_data
71
- if expect_data.is_a?(Proc)
71
+ if expect_data.is_a?(Proc)
72
72
  match = begin
73
- !!expect_data[value[:result]]
73
+ !!expect_data[value[:result]]
74
74
  rescue Timeout::Error, Exception => ex
75
75
  mes = "proc match failed with '#{ex.message}'"
76
76
  error(mes)
77
77
  value[:notice] = mes
78
78
  return false
79
79
  end
80
-
80
+
81
81
  unless match
82
- warn "proc #{expect_data} not matched (#{value[:result].truncate(30)}) answer"
82
+ warn "proc #{expect_data} not matched (#{value[:result].truncate(30)}) answer"
83
83
  value[:notice] = "missing proc validation"
84
84
  end
85
85
 
@@ -108,7 +108,7 @@ class Eye::Checker::Socket < Eye::Checker::Defer
108
108
  else
109
109
  res = "#{value[:result].to_s.size}b"
110
110
  res += "<#{value[:notice]}>" if value[:notice]
111
- res
111
+ res
112
112
  end
113
113
  end
114
114
  end
@@ -116,7 +116,7 @@ class Eye::Checker::Socket < Eye::Checker::Defer
116
116
  private
117
117
 
118
118
  def open_socket
119
- if @socket_family == :tcp
119
+ if @socket_family == :tcp
120
120
  TCPSocket.open(@socket_addr, @socket_port)
121
121
  elsif @socket_family == :unix
122
122
  UNIXSocket.open(@socket_path)
@@ -15,9 +15,6 @@ class Eye::ChildProcess
15
15
  # system methods: send_signal
16
16
  include Eye::Process::System
17
17
 
18
- # logger methods: info, ...
19
- include Eye::Logger::Helpers
20
-
21
18
  # self_status_data
22
19
  include Eye::Process::Data
23
20
 
@@ -37,8 +34,6 @@ class Eye::ChildProcess
37
34
  @name = "child-#{pid}"
38
35
  @full_name = [logger_prefix, @name] * ':'
39
36
 
40
- @logger = Eye::Logger.new(@full_name)
41
-
42
37
  @watchers = {}
43
38
 
44
39
  debug "start monitoring CHILD config: #{@config.inspect}"
@@ -46,6 +41,10 @@ class Eye::ChildProcess
46
41
  start_checkers
47
42
  end
48
43
 
44
+ def logger_tag
45
+ full_name
46
+ end
47
+
49
48
  def state
50
49
  :up
51
50
  end
@@ -70,7 +69,7 @@ class Eye::ChildProcess
70
69
  execute_restart_command
71
70
  else
72
71
  stop
73
- end
72
+ end
74
73
  end
75
74
 
76
75
  def monitor
@@ -79,7 +78,7 @@ class Eye::ChildProcess
79
78
  def unmonitor
80
79
  end
81
80
 
82
- def delete
81
+ def delete
83
82
  end
84
83
 
85
84
  def destroy
@@ -88,10 +87,7 @@ class Eye::ChildProcess
88
87
  end
89
88
 
90
89
  def signal(sig)
91
- if self.pid
92
- res = send_signal(sig)
93
- info "send signal #{sig} to #{self.pid} = #{res}"
94
- end
90
+ send_signal(sig) if self.pid
95
91
  end
96
92
 
97
93
  def status_data(debug = false)