god 0.7.5 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,11 @@
1
+ == 0.7.6 / 2008-05-13
2
+ * Major Enhancements
3
+ * Implement System::Process methods for Linux based on /proc [Kevin Clark]
4
+ * Minor Enhancements
5
+ * Allowing directories to be loaded at start [Bert Goethals]
6
+ * Bug Fixes
7
+ * Don't leak events on error in the kqueue handler [Kevin Clark]
8
+
1
9
  == 0.7.5 / 2008-02-21
2
10
  * Bug Fixes
3
11
  * Remove Ruby's Logger and replace with custom SimpleLogger to stop threaded leak
@@ -48,7 +48,9 @@ lib/god/registry.rb
48
48
  lib/god/simple_logger.rb
49
49
  lib/god/socket.rb
50
50
  lib/god/sugar.rb
51
+ lib/god/system/portable_poller.rb
51
52
  lib/god/system/process.rb
53
+ lib/god/system/slash_proc_poller.rb
52
54
  lib/god/task.rb
53
55
  lib/god/timeline.rb
54
56
  lib/god/trigger.rb
@@ -96,6 +98,7 @@ test/test_process.rb
96
98
  test/test_registry.rb
97
99
  test/test_socket.rb
98
100
  test/test_sugar.rb
101
+ test/test_system_portable_poller.rb
99
102
  test/test_system_process.rb
100
103
  test/test_task.rb
101
104
  test/test_timeline.rb
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'hoe'
3
3
 
4
- Hoe.new('god', '0.7.5') do |p|
4
+ Hoe.new('god', '0.7.6') do |p|
5
5
  p.rubyforge_name = 'god'
6
6
  p.author = 'Tom Preston-Werner'
7
7
  p.email = 'tom@rubyisawesome.com'
@@ -80,6 +80,7 @@ kqh_handle_events()
80
80
  nevents = kevent(kq, NULL, 0, events, num_to_fetch, NULL);
81
81
 
82
82
  if (-1 == nevents) {
83
+ free(events);
83
84
  rb_raise(rb_eStandardError, strerror(errno));
84
85
  } else {
85
86
  for (i = 0; i < nevents; i++) {
data/lib/god.rb CHANGED
@@ -20,7 +20,11 @@ end
20
20
  require 'god/errors'
21
21
  require 'god/simple_logger'
22
22
  require 'god/logger'
23
+
23
24
  require 'god/system/process'
25
+ require 'god/system/portable_poller'
26
+ require 'god/system/slash_proc_poller'
27
+
24
28
  require 'god/dependency_graph'
25
29
  require 'god/timeline'
26
30
  require 'god/configurable'
@@ -129,7 +133,7 @@ class Module
129
133
  end
130
134
 
131
135
  module God
132
- VERSION = '0.7.5'
136
+ VERSION = '0.7.6'
133
137
 
134
138
  LOG_BUFFER_SIZE_DEFAULT = 100
135
139
  PID_FILE_DIRECTORY_DEFAULTS = ['/var/run/god', '~/.god/pids']
@@ -33,6 +33,60 @@ module God
33
33
  end
34
34
  end
35
35
 
36
+ def default_run
37
+ # start attached pid watcher if necessary
38
+ if @options[:attach]
39
+ self.attach
40
+ end
41
+
42
+ if @options[:port]
43
+ God.port = @options[:port]
44
+ end
45
+
46
+ if @options[:events]
47
+ God::EventHandler.load
48
+ end
49
+
50
+ # set log level, defaults to WARN
51
+ if @options[:log_level]
52
+ God.log_level = @options[:log_level]
53
+ else
54
+ God.log_level = @options[:daemonize] ? :warn : :info
55
+ end
56
+
57
+ if @options[:config]
58
+ unless File.exist?(@options[:config])
59
+ abort "File not found: #{@options[:config]}"
60
+ end
61
+
62
+ # start the event handler
63
+ God::EventHandler.start if God::EventHandler.loaded?
64
+
65
+ load_config @options[:config]
66
+ end
67
+ end
68
+
69
+ def run_in_front
70
+ require 'god'
71
+
72
+ if @options[:bleakhouse]
73
+ BleakHouseDiagnostic.install
74
+ end
75
+
76
+ default_run
77
+
78
+ if @options[:log]
79
+ log_file = File.expand_path(@options[:log])
80
+ puts "Sending output to log file: #{log_file}"
81
+
82
+ # reset file descriptors
83
+ STDIN.reopen "/dev/null"
84
+ STDOUT.reopen(log_file, "a")
85
+ STDERR.reopen STDOUT
86
+ STDOUT.sync = true
87
+ end
88
+ end
89
+
36
90
  def run_daemonized
37
91
  # trap and ignore SIGHUP
38
92
  Signal.trap('HUP') {}
@@ -49,18 +103,8 @@ module God
49
103
  STDERR.reopen STDOUT
50
104
  STDOUT.sync = true
51
105
 
52
- # start attached pid watcher if necessary
53
- if @options[:attach]
54
- self.attach
55
- end
56
-
57
- # set port if requested
58
- if @options[:port]
59
- God.port = @options[:port]
60
- end
61
-
62
106
  # set pid if requested
63
- if @options[:pid]
107
+ if @options[:pid] # and as deamon
64
108
  God.pid = @options[:pid]
65
109
  end
66
110
 
@@ -68,9 +112,7 @@ module God
68
112
  Logger.syslog = false
69
113
  end
70
114
 
71
- if @options[:events]
72
- God::EventHandler.load
73
- end
115
+ default_run
74
116
 
75
117
  unless God::EventHandler.loaded?
76
118
  puts
@@ -83,34 +125,6 @@ module God
83
125
  puts
84
126
  end
85
127
 
86
- # load config
87
- if @options[:config]
88
- # set log level, defaults to WARN
89
- if @options[:log_level]
90
- God.log_level = @options[:log_level]
91
- else
92
- God.log_level = :warn
93
- end
94
-
95
- unless File.exist?(@options[:config])
96
- abort "File not found: #{@options[:config]}"
97
- end
98
-
99
- # start the event handler
100
- God::EventHandler.start if God::EventHandler.loaded?
101
-
102
- begin
103
- load File.expand_path(@options[:config])
104
- rescue Exception => e
105
- if e.instance_of?(SystemExit)
106
- raise
107
- else
108
- puts e.message
109
- puts e.backtrace.join("\n")
110
- abort "There was an error in your configuration file (see above)"
111
- end
112
- end
113
- end
114
128
  rescue => e
115
129
  puts e.message
116
130
  puts e.backtrace.join("\n")
@@ -127,63 +141,35 @@ module God
127
141
  exit
128
142
  end
129
143
 
130
- def run_in_front
131
- require 'god'
132
-
133
- if @options[:bleakhouse]
134
- BleakHouseDiagnostic.install
135
- end
136
-
137
- # start attached pid watcher if necessary
138
- if @options[:attach]
139
- self.attach
140
- end
141
-
142
- if @options[:port]
143
- God.port = @options[:port]
144
- end
145
-
146
- if @options[:events]
147
- God::EventHandler.load
148
- end
149
-
150
- # set log level if requested
151
- if @options[:log_level]
152
- God.log_level = @options[:log_level]
153
- end
154
-
155
- if @options[:config]
156
- unless File.exist?(@options[:config])
157
- abort "File not found: #{@options[:config]}"
144
+ def load_config(config)
145
+ if File.directory? config
146
+ files_loaded = false
147
+ Dir[File.expand_path('**/*.god', config)].each do |god_file|
148
+ files_loaded ||= load_god_file(File.expand_path(god_file))
158
149
  end
159
-
160
- # start the event handler
161
- God::EventHandler.start if God::EventHandler.loaded?
162
-
163
- begin
164
- load File.expand_path(@options[:config])
165
- rescue Exception => e
166
- if e.instance_of?(SystemExit)
167
- raise
168
- else
169
- puts e.message
170
- puts e.backtrace.join("\n")
171
- abort "There was an error in your configuration file (see above)"
172
- end
150
+ unless files_loaded
151
+ abort "No files could be loaded"
173
152
  end
174
-
175
- if @options[:log]
176
- log_file = File.expand_path(@options[:log])
177
- puts "Sending output to log file: #{log_file}"
178
-
179
- # reset file descriptors
180
- STDIN.reopen "/dev/null"
181
- STDOUT.reopen(log_file, "a")
182
- STDERR.reopen STDOUT
183
- STDOUT.sync = true
153
+ else
154
+ unless load_god_file(File.expand_path(config))
155
+ abort "File could not be loaded"
184
156
  end
185
157
  end
186
158
  end
159
+
160
+ def load_god_file(god_file)
161
+ load File.expand_path(god_file)
162
+ rescue Exception => e
163
+ if e.instance_of?(SystemExit)
164
+ raise
165
+ else
166
+ puts "There was an error in #{god_file}"
167
+ puts "\t" + e.message
168
+ puts "\t" + e.backtrace.join("\n\t")
169
+ return false
170
+ end
171
+ end
172
+
187
173
  end # Run
188
174
 
189
175
  end
@@ -0,0 +1,42 @@
1
+ module God
2
+ module System
3
+ class PortablePoller
4
+ def initialize(pid)
5
+ @pid = pid
6
+ end
7
+ # Memory usage in kilobytes (resident set size)
8
+ def memory
9
+ ps_int('rss')
10
+ end
11
+
12
+ # Percentage memory usage
13
+ def percent_memory
14
+ ps_float('%mem')
15
+ end
16
+
17
+ # Percentage CPU usage
18
+ def percent_cpu
19
+ ps_float('%cpu')
20
+ end
21
+
22
+ private
23
+
24
+ def ps_int(keyword)
25
+ `ps -o #{keyword}= -p #{@pid}`.to_i
26
+ end
27
+
28
+ def ps_float(keyword)
29
+ `ps -o #{keyword}= -p #{@pid}`.to_f
30
+ end
31
+
32
+ def ps_string(keyword)
33
+ `ps -o #{keyword}= -p #{@pid}`.strip
34
+ end
35
+
36
+ def time_string_to_seconds(text)
37
+ _, minutes, seconds, useconds = *text.match(/(\d+):(\d{2}).(\d{2})/)
38
+ (minutes.to_i * 60) + seconds.to_i
39
+ end
40
+ end
41
+ end
42
+ end
@@ -4,6 +4,7 @@ module God
4
4
  class Process
5
5
  def initialize(pid)
6
6
  @pid = pid.to_i
7
+ @poller = fetch_system_poller.new(@pid)
7
8
  end
8
9
 
9
10
  # Return true if this process is running, false otherwise
@@ -13,41 +14,27 @@ module God
13
14
 
14
15
  # Memory usage in kilobytes (resident set size)
15
16
  def memory
16
- ps_int('rss')
17
+ @poller.memory
17
18
  end
18
19
 
19
20
  # Percentage memory usage
20
21
  def percent_memory
21
- ps_float('%mem')
22
+ @poller.percent_memory
22
23
  end
23
24
 
24
25
  # Percentage CPU usage
25
26
  def percent_cpu
26
- ps_float('%cpu')
27
- end
28
-
29
- # Seconds of CPU time (accumulated cpu time, user + system)
30
- def cpu_time
31
- time_string_to_seconds(ps_string('time'))
27
+ @poller.percent_cpu
32
28
  end
33
29
 
34
30
  private
35
31
 
36
- def ps_int(keyword)
37
- `ps -o #{keyword}= -p #{@pid}`.to_i
38
- end
39
-
40
- def ps_float(keyword)
41
- `ps -o #{keyword}= -p #{@pid}`.to_f
42
- end
43
-
44
- def ps_string(keyword)
45
- `ps -o #{keyword}= -p #{@pid}`.strip
46
- end
47
-
48
- def time_string_to_seconds(text)
49
- _, minutes, seconds, useconds = *text.match(/(\d+):(\d{2}).(\d{2})/)
50
- (minutes.to_i * 60) + seconds.to_i
32
+ def fetch_system_poller
33
+ if test(?d, '/proc')
34
+ SlashProcPoller
35
+ else
36
+ PortablePoller
37
+ end
51
38
  end
52
39
  end
53
40
 
@@ -0,0 +1,62 @@
1
+ module God
2
+ module System
3
+ class SlashProcPoller < PortablePoller
4
+ @@kb_per_page = 4 # TODO: Need to make this portable
5
+ @@hertz = 100
6
+ @@total_mem = nil
7
+
8
+ def initialize(pid)
9
+ super(pid)
10
+
11
+ unless @@total_mem # in K
12
+ File.open("/proc/meminfo") do |f|
13
+ @@total_mem = f.gets.split[1]
14
+ end
15
+ end
16
+ end
17
+
18
+ def memory
19
+ stat[:rss].to_i * @@kb_per_page
20
+ end
21
+
22
+ def percent_memory
23
+ (memory / @@total_mem.to_f) * 100
24
+ end
25
+
26
+ # TODO: Change this to calculate the wma instead
27
+ def percent_cpu
28
+ stats = stat
29
+ total_time = stats[:utime].to_i + stats[:stime].to_i # in jiffies
30
+ seconds = uptime - stats[:starttime].to_i / @@hertz
31
+ if seconds == 0
32
+ 0
33
+ else
34
+ ((total_time * 1000 / @@hertz) / seconds) / 10
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ # in seconds
41
+ def uptime
42
+ File.read('/proc/uptime').split[0].to_f
43
+ end
44
+
45
+ def stat
46
+ stats = {}
47
+ stats[:pid], stats[:comm], stats[:state], stats[:ppid], stats[:pgrp],
48
+ stats[:session], stats[:tty_nr], stats[:tpgid], stats[:flags],
49
+ stats[:minflt], stats[:cminflt], stats[:majflt], stats[:cmajflt],
50
+ stats[:utime], stats[:stime], stats[:cutime], stats[:cstime],
51
+ stats[:priority], stats[:nice], _, stats[:itrealvalue],
52
+ stats[:starttime], stats[:vsize], stats[:rss], stats[:rlim],
53
+ stats[:startcode], stats[:endcode], stats[:startstack], stats[:kstkesp],
54
+ stats[:kstkeip], stats[:signal], stats[:blocked], stats[:sigignore],
55
+ stats[:sigcatch], stats[:wchan], stats[:nswap], stats[:cnswap],
56
+ stats[:exit_signal], stats[:processor], stats[:rt_priority],
57
+ stats[:policy] = File.read("/proc/#{@pid}/stat").split
58
+ stats
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSystemPortablePoller < Test::Unit::TestCase
4
+ def setup
5
+ pid = Process.pid
6
+ @process = System::PortablePoller.new(pid)
7
+ end
8
+
9
+ def test_time_string_to_seconds
10
+ assert_equal 0, @process.bypass.time_string_to_seconds('0:00:00')
11
+ assert_equal 0, @process.bypass.time_string_to_seconds('0:00:55')
12
+ assert_equal 27, @process.bypass.time_string_to_seconds('0:27:32')
13
+ assert_equal 75, @process.bypass.time_string_to_seconds('1:15:13')
14
+ assert_equal 735, @process.bypass.time_string_to_seconds('12:15:13')
15
+ end
16
+ end
17
+
@@ -26,17 +26,5 @@ class TestSystemProcess < Test::Unit::TestCase
26
26
  def test_percent_cpu
27
27
  assert_kind_of Float, @process.percent_cpu
28
28
  end
29
-
30
- def test_cpu_time
31
- assert_kind_of Integer, @process.cpu_time
32
- end
33
-
34
- def test_time_string_to_seconds
35
- assert_equal 0, @process.bypass.time_string_to_seconds('0:00:00')
36
- assert_equal 0, @process.bypass.time_string_to_seconds('0:00:55')
37
- assert_equal 27, @process.bypass.time_string_to_seconds('0:27:32')
38
- assert_equal 75, @process.bypass.time_string_to_seconds('1:15:13')
39
- assert_equal 735, @process.bypass.time_string_to_seconds('12:15:13')
40
- end
41
29
  end
42
30
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: god
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.7.5
7
- date: 2008-05-13 00:00:00 -07:00
6
+ version: 0.7.6
7
+ date: 2008-05-21 00:00:00 -07:00
8
8
  summary: Like monit, only awesome
9
9
  require_paths:
10
10
  - lib
@@ -80,7 +80,9 @@ files:
80
80
  - lib/god/simple_logger.rb
81
81
  - lib/god/socket.rb
82
82
  - lib/god/sugar.rb
83
+ - lib/god/system/portable_poller.rb
83
84
  - lib/god/system/process.rb
85
+ - lib/god/system/slash_proc_poller.rb
84
86
  - lib/god/task.rb
85
87
  - lib/god/timeline.rb
86
88
  - lib/god/trigger.rb
@@ -128,6 +130,7 @@ files:
128
130
  - test/test_registry.rb
129
131
  - test/test_socket.rb
130
132
  - test/test_sugar.rb
133
+ - test/test_system_portable_poller.rb
131
134
  - test/test_system_process.rb
132
135
  - test/test_task.rb
133
136
  - test/test_timeline.rb
@@ -152,6 +155,7 @@ test_files:
152
155
  - test/test_registry.rb
153
156
  - test/test_socket.rb
154
157
  - test/test_sugar.rb
158
+ - test/test_system_portable_poller.rb
155
159
  - test/test_system_process.rb
156
160
  - test/test_task.rb
157
161
  - test/test_timeline.rb