public
Fork of mojombo/god
Description: Ruby process monitor
Homepage: http://god.rubyforge.org
Clone URL: git://github.com/kevinclark/god.git
Search Repo:
Merge branch 'master' into proc_fs

Conflicts:

  lib/god/system/process.rb
kevinclark (author)
Tue May 13 20:37:51 -0700 2008
commit  ba98fdce987aa50fe840a2cafd5f1f4e6e0815aa
tree    88e2e664d0bade761a513c0cf05e0ca2cd099927
parent  466dbc3416c15c7f968f3012aa23904e157ad50f parent  582fca91f5dde41339522299835639d82d10a576
...
2
3
4
 
...
2
3
4
5
0
@@ -2,4 +2,5 @@
0
 pkg
0
 *.log
0
 logs
0
+*.rbc
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
0
@@ -1,3 +1,19 @@
0
+== 0.7.5 / 2008-02-21
0
+ * Bug Fixes
0
+ * Remove Ruby's Logger and replace with custom SimpleLogger to stop threaded leak
0
+
0
+== 0.7.4 / 2008-02-18
0
+ * Bug Fixes
0
+ * Introduce local scope to prevent faulty optimization that causes memory to leak
0
+
0
+== 0.7.3 / 2008-02-14
0
+ * Minor Enhancements
0
+ * Add --bleakhouse to make running diagnostics easier
0
+ * Bug Fixes
0
+ * Use ::Process.kill(0, ...) instead of `kill -0` [queso]
0
+ * Fix pid_file behavior in process-centric conditions so they work with tasks [matias]
0
+ * Redirect output of daemonized god to log file or /dev/null earlier [_eric]
0
+
0
 == 0.7.2 / 2008-02-04
0
   * Bug Fixes
0
     * Start event system for CLI commands
...
45
46
47
 
48
49
50
...
67
68
69
 
70
71
72
...
45
46
47
48
49
50
51
...
68
69
70
71
72
73
74
0
@@ -45,6 +45,7 @@
0
 lib/god/metric.rb
0
 lib/god/process.rb
0
 lib/god/registry.rb
0
+lib/god/simple_logger.rb
0
 lib/god/socket.rb
0
 lib/god/sugar.rb
0
 lib/god/system/process.rb
0
@@ -67,6 +68,7 @@
0
 test/configs/daemon_polls/simple_server.rb
0
 test/configs/degrading_lambda/degrading_lambda.god
0
 test/configs/degrading_lambda/tcp_server.rb
0
+test/configs/matias/matias.god
0
 test/configs/real.rb
0
 test/configs/running_load/running_load.god
0
 test/configs/stress/simple_server.rb
...
24
25
26
 
 
27
 
 
 
 
 
 
28
29
30
31
32
 
33
34
35
...
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 
40
41
42
43
0
@@ -24,12 +24,20 @@
0
 == INSTALL:
0
 
0
   $ sudo gem install god
0
+
0
+== CONTRIBUTE:
0
 
0
+Latest code is available at http://github.com/mojombo/god
0
+
0
+The 'master' branch can be cloned with:
0
+
0
+ $ git clone git://github.com/mojombo/god.git
0
+
0
 == LICENSE:
0
 
0
 (The MIT License)
0
 
0
-Copyright (c) 2007 FIX
0
+Copyright (c) 2007 Tom Preston-Werner
0
 
0
 Permission is hereby granted, free of charge, to any person obtaining
0
 a copy of this software and associated documentation files (the
...
1
2
3
4
 
5
6
7
...
1
2
3
 
4
5
6
7
0
@@ -1,7 +1,7 @@
0
 require 'rubygems'
0
 require 'hoe'
0
 
0
-Hoe.new('god', '0.7.2') do |p|
0
+Hoe.new('god', '0.7.5') do |p|
0
   p.rubyforge_name = 'god'
0
   p.author = 'Tom Preston-Werner'
0
   p.email = 'tom@rubyisawesome.com'
...
87
88
89
 
 
 
 
90
91
92
...
87
88
89
90
91
92
93
94
95
96
0
@@ -87,6 +87,10 @@
0
     opts.on("--no-events", "Disable the event system") do
0
       options[:events] = false
0
     end
0
+
0
+ opts.on("--bleakhouse", "Enable bleakhouse profiling") do
0
+ options[:bleakhouse] = true
0
+ end
0
   end
0
   
0
   opts.parse!
...
5
6
7
8
9
10
11
...
19
20
21
 
22
23
24
...
74
75
76
77
78
79
80
...
134
135
136
137
 
138
139
140
...
5
6
7
 
8
9
10
...
18
19
20
21
22
23
24
...
74
75
76
 
77
78
79
...
133
134
135
 
136
137
138
139
0
@@ -5,7 +5,6 @@
0
 
0
 # core
0
 require 'stringio'
0
-require 'logger'
0
 require 'fileutils'
0
 
0
 begin
0
@@ -19,6 +18,7 @@
0
 
0
 # internal requires
0
 require 'god/errors'
0
+require 'god/simple_logger'
0
 require 'god/logger'
0
 
0
 require 'god/system/process'
0
@@ -74,7 +74,6 @@
0
 
0
 # App wide logging system
0
 LOG = God::Logger.new
0
-LOG.datetime_format = "%Y-%m-%d %H:%M:%S "
0
 
0
 def applog(watch, level, text)
0
   LOG.log(watch, level, text)
0
@@ -134,7 +133,7 @@
0
 end
0
 
0
 module God
0
- VERSION = '0.7.2'
0
+ VERSION = '0.7.5'
0
   
0
   LOG_BUFFER_SIZE_DEFAULT = 100
0
   PID_FILE_DIRECTORY_DEFAULTS = ['/var/run/god', '~/.god/pids']
...
33
34
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
37
38
39
40
...
43
44
45
46
47
48
49
50
51
52
53
54
55
 
 
 
 
 
56
57
58
59
60
61
62
63
64
65
66
67
68
 
69
70
71
72
73
...
73
74
75
76
77
78
 
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
 
 
 
 
 
 
 
 
 
107
108
109
110
111
112
113
114
115
116
117
118
119
...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
 
 
 
 
 
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
 
 
169
170
171
172
173
174
175
176
177
178
179
 
 
 
180
181
182
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
184
185
...
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
...
97
98
99
 
 
 
 
 
 
 
 
 
 
100
101
102
103
104
105
 
 
 
 
 
 
 
 
 
 
106
 
107
108
109
110
111
112
...
112
113
114
 
 
 
115
116
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
118
119
120
121
122
123
124
125
126
127
 
 
 
 
 
128
129
130
131
132
133
...
141
142
143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
145
146
147
148
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
151
152
 
 
 
 
 
 
 
 
 
 
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
0
@@ -33,6 +33,60 @@
0
         end
0
       end
0
       
0
+ def default_run
0
+ # start attached pid watcher if necessary
0
+ if @options[:attach]
0
+ self.attach
0
+ end
0
+
0
+ if @options[:port]
0
+ God.port = @options[:port]
0
+ end
0
+
0
+ if @options[:events]
0
+ God::EventHandler.load
0
+ end
0
+
0
+ # set log level, defaults to WARN
0
+ if @options[:log_level]
0
+ God.log_level = @options[:log_level]
0
+ else
0
+ God.log_level = @options[:daemonize] ? :warn : :info
0
+ end
0
+
0
+ if @options[:config]
0
+ unless File.exist?(@options[:config])
0
+ abort "File not found: #{@options[:config]}"
0
+ end
0
+
0
+ # start the event handler
0
+ God::EventHandler.start if God::EventHandler.loaded?
0
+
0
+ load_config @options[:config]
0
+ end
0
+ end
0
+
0
+ def run_in_front
0
+ require 'god'
0
+
0
+ if @options[:bleakhouse]
0
+ BleakHouseDiagnostic.install
0
+ end
0
+
0
+ default_run
0
+
0
+ if @options[:log]
0
+ log_file = File.expand_path(@options[:log])
0
+ puts "Sending output to log file: #{log_file}"
0
+
0
+ # reset file descriptors
0
+ STDIN.reopen "/dev/null"
0
+ STDOUT.reopen(log_file, "a")
0
+ STDERR.reopen STDOUT
0
+ STDOUT.sync = true
0
+ end
0
+ end
0
+
0
       def run_daemonized
0
         # trap and ignore SIGHUP
0
         Signal.trap('HUP') {}
0
0
0
@@ -43,29 +97,14 @@
0
             
0
             log_file = @options[:log] || "/dev/null"
0
             
0
- unless God::EventHandler.loaded?
0
- puts
0
- puts "***********************************************************************"
0
- puts "*"
0
- puts "* Event conditions are not available for your installation of god."
0
- puts "* You may still use and write custom conditions using the poll system"
0
- puts "*"
0
- puts "***********************************************************************"
0
- puts
0
- end
0
+ # reset file descriptors
0
+ STDIN.reopen "/dev/null"
0
+ STDOUT.reopen(log_file, "a")
0
+ STDERR.reopen STDOUT
0
+ STDOUT.sync = true
0
             
0
- # start attached pid watcher if necessary
0
- if @options[:attach]
0
- self.attach
0
- end
0
-
0
- # set port if requested
0
- if @options[:port]
0
- God.port = @options[:port]
0
- end
0
-
0
             # set pid if requested
0
- if @options[:pid]
0
+ if @options[:pid] # and as deamon
0
               God.pid = @options[:pid]
0
             end
0
             
0
0
0
@@ -73,44 +112,19 @@
0
               Logger.syslog = false
0
             end
0
             
0
- if @options[:events]
0
- God::EventHandler.load
0
- end
0
+ default_run
0
             
0
- # load config
0
- if @options[:config]
0
- # set log level, defaults to WARN
0
- if @options[:log_level]
0
- God.log_level = @options[:log_level]
0
- else
0
- God.log_level = :warn
0
- end
0
-
0
- unless File.exist?(@options[:config])
0
- abort "File not found: #{@options[:config]}"
0
- end
0
-
0
- # start the event handler
0
- God::EventHandler.start if God::EventHandler.loaded?
0
-
0
- begin
0
- load File.expand_path(@options[:config])
0
- rescue Exception => e
0
- if e.instance_of?(SystemExit)
0
- raise
0
- else
0
- puts e.message
0
- puts e.backtrace.join("\n")
0
- abort "There was an error in your configuration file (see above)"
0
- end
0
- end
0
+ unless God::EventHandler.loaded?
0
+ puts
0
+ puts "***********************************************************************"
0
+ puts "*"
0
+ puts "* Event conditions are not available for your installation of god."
0
+ puts "* You may still use and write custom conditions using the poll system"
0
+ puts "*"
0
+ puts "***********************************************************************"
0
+ puts
0
             end
0
             
0
- # reset file descriptors
0
- STDIN.reopen "/dev/null"
0
- STDOUT.reopen(log_file, "a")
0
- STDERR.reopen STDOUT
0
- STDOUT.sync = true
0
           rescue => e
0
             puts e.message
0
             puts e.backtrace.join("\n")
0
0
0
0
@@ -127,59 +141,35 @@
0
         exit
0
       end
0
       
0
- def run_in_front
0
- require 'god'
0
-
0
- # start attached pid watcher if necessary
0
- if @options[:attach]
0
- self.attach
0
- end
0
-
0
- if @options[:port]
0
- God.port = @options[:port]
0
- end
0
-
0
- if @options[:events]
0
- God::EventHandler.load
0
- end
0
-
0
- # set log level if requested
0
- if @options[:log_level]
0
- God.log_level = @options[:log_level]
0
- end
0
-
0
- if @options[:config]
0
- unless File.exist?(@options[:config])
0
- abort "File not found: #{@options[:config]}"
0
+ def load_config(config)
0
+ if File.directory? config
0
+ files_loaded = false
0
+ Dir[File.expand_path('**/*.god', config)].each do |god_file|
0
+ files_loaded ||= load_god_file(File.expand_path(god_file))
0
           end
0
-
0
- # start the event handler
0
- God::EventHandler.start if God::EventHandler.loaded?
0
-
0
- begin
0
- load File.expand_path(@options[:config])
0
- rescue Exception => e
0
- if e.instance_of?(SystemExit)
0
- raise
0
- else
0
- puts e.message
0
- puts e.backtrace.join("\n")
0
- abort "There was an error in your configuration file (see above)"
0
- end
0
+ unless files_loaded
0
+ abort "No files could be loaded"
0
           end
0
-
0
- if @options[:log]
0
- log_file = File.expand_path(@options[:log])
0
- puts "Sending output to log file: #{log_file}"
0
-
0
- # reset file descriptors
0
- STDIN.reopen "/dev/null"
0
- STDOUT.reopen(log_file, "a")
0
- STDERR.reopen STDOUT
0
- STDOUT.sync = true
0
+ else
0
+ unless load_god_file(File.expand_path(config))
0
+ abort "File could not be loaded"
0
           end
0
         end
0
       end
0
+
0
+ def load_god_file(god_file)
0
+ load File.expand_path(god_file)
0
+ rescue Exception => e
0
+ if e.instance_of?(SystemExit)
0
+ raise
0
+ else
0
+ puts "There was an error in #{god_file}"
0
+ puts "\t" + e.message
0
+ puts "\t" + e.backtrace.join("\n\t")
0
+ return false
0
+ end
0
+ end
0
+
0
     end # Run
0
     
0
   end
...
50
51
52
53
 
54
55
56
57
58
 
59
60
61
...
50
51
52
 
53
54
55
56
57
 
58
59
60
61
0
@@ -50,12 +50,12 @@
0
       end
0
       
0
       def pid
0
- self.watch.pid || File.read(self.pid_file).strip.to_i
0
+ self.pid_file ? File.read(self.pid_file).strip.to_i : self.watch.pid
0
       end
0
       
0
       def valid?
0
         valid = true
0
- valid &= complain("Attribute 'pid_file' must be specified", self) if self.watch.pid_file.nil? && self.pid_file.nil?
0
+ valid &= complain("Attribute 'pid_file' must be specified", self) if self.pid_file.nil? && self.watch.pid_file.nil?
0
         valid &= complain("Attribute 'above' must be specified", self) if self.above.nil?
0
         valid
0
       end
...
52
53
54
55
 
56
57
58
59
60
 
61
62
63
...
52
53
54
 
55
56
57
58
59
 
60
61
62
63
0
@@ -52,12 +52,12 @@
0
       end
0
       
0
       def pid
0
- self.watch.pid || File.read(self.pid_file).strip.to_i
0
+ self.pid_file ? File.read(self.pid_file).strip.to_i : self.watch.pid
0
       end
0
       
0
       def valid?
0
         valid = true
0
- valid &= complain("Attribute 'pid_file' must be specified", self) if self.watch.pid_file.nil? && self.pid_file.nil?
0
+ valid &= complain("Attribute 'pid_file' must be specified", self) if self.pid_file.nil? && self.watch.pid_file.nil?
0
         valid &= complain("Attribute 'above' must be specified", self) if self.above.nil?
0
         valid
0
       end
...
23
24
25
 
 
26
27
28
29
...
31
32
33
 
 
 
 
34
35
 
36
37
38
39
...
49
50
51
52
 
53
54
55
56
57
58
59
 
 
60
61
62
...
23
24
25
26
27
28
29
30
31
...
33
34
35
36
37
38
39
40
 
41
42
43
44
45
...
55
56
57
 
58
59
60
61
62
63
64
 
65
66
67
68
69
0
@@ -23,6 +23,8 @@
0
     # c.pid_file = "/var/run/mongrel.3000.pid"
0
     # end
0
     class ProcessExits < EventCondition
0
+ attr_accessor :pid_file
0
+
0
       def initialize
0
         self.info = "process exited"
0
       end
0
0
@@ -31,8 +33,12 @@
0
         true
0
       end
0
       
0
+ def pid
0
+ self.pid_file ? File.read(self.pid_file).strip.to_i : self.watch.pid
0
+ end
0
+
0
       def register
0
- pid = self.watch.pid
0
+ pid = self..pid
0
         
0
         begin
0
           EventHandler.register(pid, :proc_exit) do |extra|
0
0
@@ -49,14 +55,15 @@
0
       end
0
       
0
       def deregister
0
- pid = self.watch.pid
0
+ pid = self..pid
0
         if pid
0
           EventHandler.deregister(pid, :proc_exit)
0
           
0
           msg = "#{self.watch.name} deregistered 'proc_exit' event for pid #{pid}"
0
           applog(self.watch, :info, msg)
0
         else
0
- applog(self.watch, :error, "#{self.watch.name} could not deregister: no cached PID or PID file #{self.watch.pid_file} (#{self.base_name})")
0
+ pid_file_location = self.pid_file || self.watch.pid_file
0
+ applog(self.watch, :error, "#{self.watch.name} could not deregister: no cached PID or PID file #{pid_file_location} (#{self.base_name})")
0
         end
0
       end
0
     end
...
37
38
39
40
 
41
42
43
44
45
 
46
47
48
...
50
51
52
53
54
55
56
57
58
 
59
60
61
...
37
38
39
 
40
41
42
43
44
 
45
46
47
48
...
50
51
52
 
 
 
 
 
 
53
54
55
56
0
@@ -37,12 +37,12 @@
0
       attr_accessor :running, :pid_file
0
       
0
       def pid
0
- self.watch.pid || File.read(self.pid_file).strip.to_i
0
+ self.pid_file ? File.read(self.pid_file).strip.to_i : self.watch.pid
0
       end
0
       
0
       def valid?
0
         valid = true
0
- valid &= complain("Attribute 'pid_file' must be specified", self) if self.watch.pid_file.nil? && self.pid_file.nil?
0
+ valid &= complain("Attribute 'pid_file' must be specified", self) if self.pid_file.nil? && self.watch.pid_file.nil?
0
         valid &= complain("Attribute 'running' must be specified", self) if self.running.nil?
0
         valid
0
       end
0
@@ -50,12 +50,7 @@
0
       def test
0
         self.info = []
0
         
0
- # unless File.exist?(self.watch.pid_file)
0
- # self.info << "#{self.watch.name} #{self.class.name}: no such pid file: #{self.watch.pid_file}"
0
- # return !self.running
0
- # end
0
-
0
- pid = self.watch.pid
0
+ pid = self.pid
0
         active = pid && System::Process.new(pid).exists?
0
         
0
         if (self.running && active)
...
31
32
33
 
34
35
36
...
31
32
33
34
35
36
37
0
@@ -31,6 +31,7 @@
0
     end
0
     
0
     def base_name
0
+ x = 1 # fix for MRI's local scope optimization bug DO NOT REMOVE!
0
       self.class.name.split('::').last
0
     end
0
     
...
22
23
24
25
26
 
 
27
28
29
...
22
23
24
 
 
25
26
27
28
29
0
@@ -22,8 +22,8 @@
0
     File.delete(LOG_FILE) rescue nil
0
   end
0
   
0
- def self.snapshot
0
- self.logger.snapshot(LOG_FILE, "timer", false) if self.logger
0
+ def self.snapshot(name)
0
+ self.logger.snapshot(LOG_FILE, name, false) if self.logger
0
   end
0
   
0
   def self.spin(delay = 1)
...
40
41
42
 
43
44
45
 
46
47
48
 
49
50
51
...
40
41
42
43
44
45
 
46
47
48
 
49
50
51
52
0
@@ -40,12 +40,13 @@
0
     
0
     def self.deregister(pid, event=nil)
0
       if watching_pid? pid
0
+ running = ::Process.kill(0, pid.to_i) rescue false
0
         if event.nil?
0
           @@actions.delete(pid)
0
- @@handler.register_process(pid, []) if system("kill -0 #{pid} &> /dev/null")
0
+ @@handler.register_process(pid, []) if running
0
         else
0
           @@actions[pid].delete(event)
0
- @@handler.register_process(pid, @@actions[pid].keys) if system("kill -0 #{pid} &> /dev/null")
0
+ @@handler.register_process(pid, @@actions[pid].keys) if running
0
         end
0
       end
0
     end
...
1
2
3
 
4
5
6
...
22
23
24
25
 
26
27
28
...
1
2
 
3
4
5
6
...
22
23
24
 
25
26
27
28
0
@@ -1,6 +1,6 @@
0
 module God
0
   
0
- class Logger < ::Logger
0
+ class Logger < SimpleLogger
0
     SYSLOG_EQUIVALENTS = {:fatal => :crit,
0
                           :error => :err,
0
                           :warn => :debug,
0
@@ -22,7 +22,7 @@
0
       @mutex = Mutex.new
0
       @capture = nil
0
       @templogio = StringIO.new
0
- @templog = ::Logger.new(@templogio)
0
+ @templog = SimpleLogger.new(@templogio)
0
       @templog.level = Logger::INFO
0
       load_syslog
0
     end
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
0
@@ -1 +1,54 @@
0
+module God
0
+
0
+ class SimpleLogger
0
+ DEBUG = 2
0
+ INFO = 4
0
+ WARN = 8
0
+ ERROR = 16
0
+ FATAL = 32
0
+
0
+ SEV_LABEL = {DEBUG => 'DEBUG',
0
+ INFO => 'INFO',
0
+ WARN => 'WARN',
0
+ ERROR => 'ERROR',
0
+ FATAL => 'FATAL'}
0
+
0
+ attr_accessor :datetime_format, :level
0
+
0
+ def initialize(io)
0
+ @io = io
0
+ @level = INFO
0
+ @datetime_format = "%Y-%m-%d %H:%M:%S"
0
+ end
0
+
0
+ def output(level, msg)
0
+ return if level < self.level
0
+
0
+ time = Time.now.strftime(self.datetime_format)
0
+ label = SEV_LABEL[level]
0
+ @io.print("#{label[0..0]} [#{time}] #{label.rjust(5)}: #{msg}\n")
0
+ end
0
+
0
+ def fatal(msg)
0
+ self.output(FATAL, msg)
0
+ end
0
+
0
+ def error(msg)
0
+ self.output(ERROR, msg)
0
+ end
0
+
0
+ def warn(msg)
0
+ self.output(WARN, msg)
0
+ end
0
+
0
+ def info(msg)
0
+ self.output(INFO, msg)
0
+ end
0
+
0
+ def debug(msg)
0
+ self.output(DEBUG, msg)
0
+ end
0
+ end
0
+
0
+end
...
9
10
11
12
 
13
14
15
...
9
10
11
 
12
13
14
15
0
@@ -9,7 +9,7 @@
0
       
0
       # Return true if this process is running, false otherwise
0
       def exists?
0
- !!Process.kill(0, @pid) rescue false
0
+ !!::Process.kill(0, @pid) rescue false
0
       end
0
       
0
       # Memory usage in kilobytes (resident set size)