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' of git://github.com/mojombo/god
kevinclark (author)
Tue May 13 20:36:42 -0700 2008
commit  582fca91f5dde41339522299835639d82d10a576
tree    4ef00119ce534cf7593ad522d07031442aa7765e
parent  87c194332a77399b96efec701a242a0df16778d8 parent  15f0ceeef36e49eb51d6efcc7ed4df7b6a03d14f
...
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
...
70
71
72
73
74
75
76
...
130
131
132
133
 
134
135
136
...
5
6
7
 
8
9
10
...
18
19
20
21
22
23
24
...
70
71
72
 
73
74
75
...
129
130
131
 
132
133
134
135
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
 require 'god/system/process'
0
 require 'god/dependency_graph'
0
@@ -70,7 +70,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
@@ -130,7 +129,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
...
8
9
10
11
 
12
13
14
...
8
9
10
 
11
12
13
14
0
@@ -8,7 +8,7 @@
0
       
0
       # Return true if this process is running, false otherwise
0
       def exists?
0
- system("kill -0 #{@pid} &> /dev/null")
0
+ !!::Process.kill(0, @pid) rescue false
0
       end
0
       
0
       # Memory usage in kilobytes (resident set size)