daemonizer 0.4.18 → 0.5.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +4 -0
- data/Rakefile +9 -29
- data/daemonizer.gemspec +20 -69
- data/lib/daemonizer.rb +10 -4
- data/lib/daemonizer/cli.rb +14 -1
- data/lib/daemonizer/config.rb +2 -2
- data/lib/daemonizer/dsl.rb +16 -4
- data/lib/daemonizer/engine.rb +41 -36
- data/lib/daemonizer/option.rb +11 -5
- data/lib/daemonizer/process_manager.rb +7 -3
- data/lib/daemonizer/version.rb +3 -0
- data/lib/daemonizer/worker.rb +16 -2
- data/lib/daemonizer/worker_pool.rb +10 -4
- data/spec/cli/debugging_spec.rb +45 -0
- data/spec/cli/not_started_spec.rb +127 -0
- data/spec/cli/started_spec.rb +121 -0
- data/spec/common/forking_spec.rb +35 -0
- data/spec/settings/callbacks_spec.rb +103 -0
- data/spec/settings/options_spec.rb +146 -0
- data/spec/settings/pools_spec.rb +43 -0
- data/spec/spec_helper.rb +56 -8
- data/spec/support/daemonfile_factory.rb +33 -0
- data/spec/support/helpers.rb +70 -0
- data/spec/support/path.rb +18 -0
- data/spec/support/processes.rb +23 -0
- data/spec/support/ruby_ext.rb +19 -0
- metadata +107 -50
- data/VERSION +0 -1
- data/spec/spec.opts +0 -1
- data/spec/test_spec.rb +0 -5
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
CHANGED
@@ -1,35 +1,15 @@
|
|
1
|
-
require
|
2
|
-
require 'rubygems/specification'
|
1
|
+
require "bundler/gem_tasks"
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
gemspec.summary = "Daemonizer allows you to easily create custom daemons on ruby. Supporting prefork model"
|
9
|
-
gemspec.description = "Inspired by bundler and rack. Mostly built on top of Alexey Kovyrin's loops code. http://github.com/kovyrin/loops"
|
10
|
-
gemspec.email = "glebpom@gmail.com"
|
11
|
-
gemspec.homepage = "http://github.com/glebpom/daemonizer"
|
12
|
-
gemspec.authors = ["Gleb Pomykalov"]
|
13
|
-
gemspec.add_dependency('thor', '>= 0.13.7')
|
14
|
-
gemspec.add_dependency('simple-statistics', '>= 0')
|
15
|
-
gemspec.add_development_dependency "rspec", ">= 1.2.9"
|
16
|
-
gemspec.add_development_dependency "yard", ">= 0"
|
17
|
-
end
|
18
|
-
Jeweler::GemcutterTasks.new
|
19
|
-
rescue LoadError
|
20
|
-
puts "Jeweler not available. Install it with: gem install jeweler"
|
21
|
-
end
|
22
|
-
|
23
|
-
require 'spec/rake/spectask'
|
24
|
-
Spec::Rake::SpecTask.new(:spec) do |spec|
|
25
|
-
spec.libs << 'lib' << 'spec'
|
26
|
-
spec.spec_files = FileList['spec/**/*_spec.rb']
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
RSpec::Core::RakeTask.new do |t|
|
5
|
+
t.rspec_opts = %w(-fs --color)
|
6
|
+
t.ruby_opts = %w(-w)
|
27
7
|
end
|
28
8
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
9
|
+
desc "Run all specs with rcov"
|
10
|
+
RSpec::Core::RakeTask.new(:rcov => :spec) do |t|
|
11
|
+
t.rcov = true
|
12
|
+
t.rcov_opts = %w{--exclude gems\/,spec\/}
|
33
13
|
end
|
34
14
|
|
35
15
|
task :spec => :check_dependencies
|
data/daemonizer.gemspec
CHANGED
@@ -1,77 +1,28 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "daemonizer/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |s|
|
7
|
-
s.name
|
8
|
-
s.version
|
6
|
+
s.name = "daemonizer"
|
7
|
+
s.version = Daemonizer::VERSION
|
8
|
+
s.authors = ["Gleb Pomykalov"]
|
9
|
+
s.email = ["glebpom@gmail.com"]
|
10
|
+
s.homepage = "http://github.com/glebpom/daemonizer"
|
11
|
+
s.summary = "Daemonizer allows you to easily create custom daemons on ruby. Supporting prefork model"
|
12
|
+
s.description = "Inspired by bundler and rack. Mostly built on top of Alexey Kovyrin's loops code. http://github.com/kovyrin/loops"
|
9
13
|
|
10
|
-
s.
|
11
|
-
|
12
|
-
s.
|
13
|
-
s.
|
14
|
-
s.
|
15
|
-
s.email = %q{glebpom@gmail.com}
|
16
|
-
s.executables = ["daemonizer"]
|
17
|
-
s.extra_rdoc_files = [
|
18
|
-
"LICENSE",
|
19
|
-
"README.md"
|
20
|
-
]
|
21
|
-
s.files = [
|
22
|
-
"LICENSE",
|
23
|
-
"README.md",
|
24
|
-
"ROADMAP",
|
25
|
-
"Rakefile",
|
26
|
-
"VERSION",
|
27
|
-
"bin/daemonizer",
|
28
|
-
"daemonizer.gemspec",
|
29
|
-
"lib/daemonizer.rb",
|
30
|
-
"lib/daemonizer/autoload.rb",
|
31
|
-
"lib/daemonizer/cli.rb",
|
32
|
-
"lib/daemonizer/config.rb",
|
33
|
-
"lib/daemonizer/daemonize.rb",
|
34
|
-
"lib/daemonizer/dsl.rb",
|
35
|
-
"lib/daemonizer/engine.rb",
|
36
|
-
"lib/daemonizer/errors.rb",
|
37
|
-
"lib/daemonizer/handler.rb",
|
38
|
-
"lib/daemonizer/option.rb",
|
39
|
-
"lib/daemonizer/process_manager.rb",
|
40
|
-
"lib/daemonizer/stats.rb",
|
41
|
-
"lib/daemonizer/worker.rb",
|
42
|
-
"lib/daemonizer/worker_pool.rb",
|
43
|
-
"spec/spec.opts",
|
44
|
-
"spec/spec_helper.rb",
|
45
|
-
"spec/test_spec.rb"
|
46
|
-
]
|
47
|
-
s.homepage = %q{http://github.com/glebpom/daemonizer}
|
14
|
+
s.rubyforge_project = "daemonizer"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
48
19
|
s.require_paths = ["lib"]
|
49
|
-
s.rubygems_version = %q{1.5.3}
|
50
|
-
s.summary = %q{Daemonizer allows you to easily create custom daemons on ruby. Supporting prefork model}
|
51
|
-
s.test_files = [
|
52
|
-
"spec/spec_helper.rb",
|
53
|
-
"spec/test_spec.rb"
|
54
|
-
]
|
55
20
|
|
56
|
-
|
57
|
-
|
21
|
+
s.add_development_dependency "rspec", ">=2.1.0"
|
22
|
+
s.add_development_dependency "mocha", ">=0.9.9"
|
23
|
+
s.add_development_dependency "yard", ">= 0"
|
24
|
+
s.add_development_dependency "rake"
|
58
25
|
|
59
|
-
|
60
|
-
|
61
|
-
s.add_runtime_dependency(%q<simple-statistics>, [">= 0"])
|
62
|
-
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
63
|
-
s.add_development_dependency(%q<yard>, [">= 0"])
|
64
|
-
else
|
65
|
-
s.add_dependency(%q<thor>, [">= 0.13.7"])
|
66
|
-
s.add_dependency(%q<simple-statistics>, [">= 0"])
|
67
|
-
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
68
|
-
s.add_dependency(%q<yard>, [">= 0"])
|
69
|
-
end
|
70
|
-
else
|
71
|
-
s.add_dependency(%q<thor>, [">= 0.13.7"])
|
72
|
-
s.add_dependency(%q<simple-statistics>, [">= 0"])
|
73
|
-
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
74
|
-
s.add_dependency(%q<yard>, [">= 0"])
|
75
|
-
end
|
26
|
+
s.add_runtime_dependency "thor", ">= 0.13.7"
|
27
|
+
s.add_runtime_dependency "simple-statistics", ">=0.0.3"
|
76
28
|
end
|
77
|
-
|
data/lib/daemonizer.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'daemonizer/version'
|
1
2
|
require 'rubygems'
|
2
3
|
require 'yaml'
|
3
4
|
require 'erb'
|
@@ -5,7 +6,6 @@ require 'pathname'
|
|
5
6
|
require 'logger'
|
6
7
|
require 'simple-statistics'
|
7
8
|
|
8
|
-
|
9
9
|
module Daemonizer
|
10
10
|
|
11
11
|
def self.root=(value)
|
@@ -59,7 +59,7 @@ module Daemonizer
|
|
59
59
|
@@log_level ||= :info
|
60
60
|
end
|
61
61
|
|
62
|
-
def self.init_logger(
|
62
|
+
def self.init_logger(log_file)
|
63
63
|
@@logger_file = File.open(log_file, File::WRONLY | File::APPEND)
|
64
64
|
@@logger_file.sync = true
|
65
65
|
@@logger = Logger.new(@@logger_file)
|
@@ -74,7 +74,13 @@ module Daemonizer
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def self.reopen_log_file
|
77
|
-
|
77
|
+
if @@logger_file.respond_to?(:path)
|
78
|
+
log_file = @@logger_file.path
|
79
|
+
f = File.open(log_file, File::WRONLY | File::APPEND | File::CREAT)
|
80
|
+
f.sync = true
|
81
|
+
@@logger_file.reopen(f)
|
82
|
+
end
|
83
|
+
true
|
78
84
|
end
|
79
85
|
|
80
86
|
def self.flush_logger
|
@@ -100,7 +106,7 @@ module Daemonizer
|
|
100
106
|
end
|
101
107
|
|
102
108
|
def self.find_pools(pool_name = nil)
|
103
|
-
pools = Dsl.evaluate(daemonfile)
|
109
|
+
pools = Dsl.evaluate(File.read(daemonfile.to_s), daemonfile.to_s).process
|
104
110
|
|
105
111
|
if pool_name
|
106
112
|
if pool = pools[pool_name.to_sym]
|
data/lib/daemonizer/cli.rb
CHANGED
@@ -109,6 +109,19 @@ module Daemonizer
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
+
desc "logrotate", "Reopen all log files"
|
113
|
+
def logrotate(pool_name = nil)
|
114
|
+
control_pools_loop(pool_name, "log file reopened") do |pool|
|
115
|
+
# Pid file check
|
116
|
+
unless Daemonize.check_pid(pool.pid_file)
|
117
|
+
print_pool pool.name, "not started!"
|
118
|
+
exit(1)
|
119
|
+
end
|
120
|
+
|
121
|
+
Process.kill('HUP', Daemonize.read_pid(pool.pid_file))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
112
125
|
private
|
113
126
|
def control_pools_loop(pool_name, message = nil, debug = false, &block)
|
114
127
|
if debug
|
@@ -122,7 +135,7 @@ module Daemonizer
|
|
122
135
|
else
|
123
136
|
Daemonizer.find_pools(pool_name).each do |pool|
|
124
137
|
Process.fork do
|
125
|
-
Daemonizer.init_logger(pool.
|
138
|
+
Daemonizer.init_logger(pool.log_file)
|
126
139
|
yield(pool)
|
127
140
|
end
|
128
141
|
Process.wait
|
data/lib/daemonizer/config.rb
CHANGED
@@ -62,7 +62,7 @@ module Daemonizer
|
|
62
62
|
raise ConfigError, "Log level should be one of [#{VALID_LOG_LEVELS.map(&:to_s).join(',')}]" unless VALID_LOG_LEVELS.include?(@options[:log_level].to_sym)
|
63
63
|
if @options[:handler]
|
64
64
|
raise ConfigError, "Handler should be a class" unless @options[:handler].is_a?(Class)
|
65
|
-
raise ConfigError, "Handler should respond to :start" unless @options[:handler].public_instance_methods.include?(
|
65
|
+
raise ConfigError, "Handler should respond to :start" unless @options[:handler].public_instance_methods.map(&:to_sym).include?(:start)
|
66
66
|
raise ConfigError, "Handler set. Don't use :start and :before init in Demfile" if @options[:prepare] || @options[:start]
|
67
67
|
else
|
68
68
|
if @options[:prepare]
|
@@ -84,7 +84,7 @@ module Daemonizer
|
|
84
84
|
|
85
85
|
[:log_file, :pid_file].each do |method|
|
86
86
|
define_method method do
|
87
|
-
File.
|
87
|
+
File.expand_path(@options[method.to_sym], Daemonizer.root)
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
data/lib/daemonizer/dsl.rb
CHANGED
@@ -1,10 +1,22 @@
|
|
1
1
|
class Daemonizer::Dsl
|
2
2
|
class DslError < StandardError; end
|
3
3
|
|
4
|
-
def self.evaluate(
|
4
|
+
def self.evaluate(daemonfile, daemonfile_name = 'Daemonfile')
|
5
5
|
builder = new
|
6
|
-
builder.instance_eval(
|
7
|
-
builder
|
6
|
+
builder.instance_eval(daemonfile, daemonfile_name.to_s, 1)
|
7
|
+
builder
|
8
|
+
end
|
9
|
+
|
10
|
+
def configs
|
11
|
+
@configs
|
12
|
+
end
|
13
|
+
|
14
|
+
def process
|
15
|
+
result = {}
|
16
|
+
@configs.each do |k,v|
|
17
|
+
result[k] = Daemonizer::Config.new(k, v)
|
18
|
+
end
|
19
|
+
result
|
8
20
|
end
|
9
21
|
|
10
22
|
def initialize
|
@@ -92,7 +104,7 @@ class Daemonizer::Dsl
|
|
92
104
|
@pool = name.to_sym
|
93
105
|
options = config_copy
|
94
106
|
yield
|
95
|
-
@configs[@pool] =
|
107
|
+
@configs[@pool] = @options.clone
|
96
108
|
rescue Daemonizer::Config::ConfigError => e
|
97
109
|
puts "* Error in pool \"#{@pool}\": #{e.to_s}. Skipping..."
|
98
110
|
ensure
|
data/lib/daemonizer/engine.rb
CHANGED
@@ -2,11 +2,11 @@ module Daemonizer
|
|
2
2
|
class Engine
|
3
3
|
attr_reader :config
|
4
4
|
|
5
|
-
def initialize(config
|
5
|
+
def initialize(config)
|
6
6
|
@config = config
|
7
7
|
Daemonizer.logger_context = "#{Process.pid}/monitor"
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def run_callback(callback, *args)
|
11
11
|
if callbacks = @config.callbacks[callback.to_sym] and callbacks.any?
|
12
12
|
Daemonizer.logger.info "Running :#{callback} callbacks"
|
@@ -15,22 +15,22 @@ module Daemonizer
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
19
|
-
def start!
|
20
|
-
@pm = ProcessManager.new(@config)
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
run_callback :before_start, @config.pool, process_id, @config.workers
|
28
|
-
@config.handler.start
|
29
|
-
end
|
30
|
-
rescue Exception => e
|
31
|
-
log_error(e)
|
32
|
-
end
|
19
|
+
def run_prepare_with_callbacks(&block)
|
20
|
+
run_callback :before_prepare, @config.pool
|
21
|
+
Daemonizer.logger.info "Workers count is #{@config.workers}"
|
22
|
+
@config.handler.prepare(block) do
|
23
|
+
run_callback :after_prepare, @config.pool
|
33
24
|
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def run_start_with_callbacks
|
28
|
+
run_callback :before_start, @config.pool, @config.handler.worker_id, @config.handler.workers_count
|
29
|
+
@config.handler.start
|
30
|
+
end
|
31
|
+
|
32
|
+
def start!
|
33
|
+
@pm = ProcessManager.new(@config)
|
34
34
|
|
35
35
|
begin
|
36
36
|
if @config.cow_friendly
|
@@ -42,16 +42,22 @@ module Daemonizer
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
@config.handler.logger = Daemonizer.logger
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
run_prepare_with_callbacks do
|
46
|
+
begin
|
47
|
+
@pm.start_workers do |process_id|
|
48
|
+
@config.handler.worker_id = process_id
|
49
|
+
@config.handler.workers_count = @config.workers
|
50
|
+
run_start_with_callbacks
|
51
|
+
end
|
52
|
+
rescue Exception => e
|
53
|
+
log_error(e)
|
54
|
+
end
|
49
55
|
end
|
50
56
|
rescue Exception => e
|
51
57
|
log_error(e)
|
52
58
|
end
|
53
59
|
# Start monitoring loop
|
54
|
-
|
60
|
+
|
55
61
|
setup_signals
|
56
62
|
@pm.monitor_workers
|
57
63
|
end
|
@@ -59,28 +65,22 @@ module Daemonizer
|
|
59
65
|
def debug!
|
60
66
|
Daemonizer.init_console_logger('console')
|
61
67
|
@config.handler.logger = Daemonizer.logger
|
62
|
-
|
63
|
-
init_block = Proc.new do
|
64
|
-
begin
|
65
|
-
@config.handler.worker_id = 1
|
66
|
-
@config.handler.workers_count = 1
|
67
|
-
run_callback :before_start, @config.pool, 1, 1
|
68
|
-
@config.handler.start
|
69
|
-
rescue Exception => e
|
70
|
-
log_error(e)
|
71
|
-
end
|
72
|
-
end
|
73
68
|
|
74
69
|
begin
|
75
|
-
|
76
|
-
|
77
|
-
|
70
|
+
run_prepare_with_callbacks do
|
71
|
+
begin
|
72
|
+
@config.handler.worker_id = 1
|
73
|
+
@config.handler.workers_count = 1
|
74
|
+
run_start_with_callbacks
|
75
|
+
rescue Exception => e
|
76
|
+
log_error(e)
|
77
|
+
end
|
78
78
|
end
|
79
79
|
rescue Exception => e
|
80
80
|
log_error(e)
|
81
81
|
end
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
84
|
def log_error(e)
|
85
85
|
Daemonizer.logger.fatal e.to_s
|
86
86
|
Daemonizer.logger.fatal "#{e.class}: #{e}\n" + e.backtrace.join("\n")
|
@@ -96,6 +96,11 @@ module Daemonizer
|
|
96
96
|
trap('TERM', stop)
|
97
97
|
trap('INT', stop)
|
98
98
|
trap('EXIT', stop)
|
99
|
+
|
100
|
+
trap('SIGHUP') do
|
101
|
+
Daemonizer.reopen_log_file
|
102
|
+
@pm.send_signal_to_workers('SIGHUP')
|
103
|
+
end
|
99
104
|
end
|
100
105
|
|
101
106
|
end
|
data/lib/daemonizer/option.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Daemonizer
|
2
2
|
class Option
|
3
3
|
class OptionError < StandardError; end
|
4
|
-
|
4
|
+
|
5
5
|
def initialize(option, value, auto_eval = false)
|
6
6
|
@option = option
|
7
7
|
@value = value
|
@@ -10,11 +10,17 @@ module Daemonizer
|
|
10
10
|
raise OptionError, "auto_apply can be used only with callable option"
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def value(handler = nil)
|
15
|
-
if @auto_eval && @value.is_a?(Proc)
|
15
|
+
if @auto_eval && @value.is_a?(Proc)
|
16
16
|
if handler && handler.worker_id && handler.workers_count
|
17
|
-
|
17
|
+
if @value.arity == 0 || @value.arity == -1
|
18
|
+
return @value.call
|
19
|
+
elsif @value.arity == 2
|
20
|
+
return @value.call(handler.worker_id, handler.workers_count)
|
21
|
+
else
|
22
|
+
raise OptionError, "option lambda should accept 0 or 2 parameters"
|
23
|
+
end
|
18
24
|
else
|
19
25
|
raise OptionError, "value called before handler initialized"
|
20
26
|
end
|
@@ -22,6 +28,6 @@ module Daemonizer
|
|
22
28
|
@value
|
23
29
|
end
|
24
30
|
end
|
25
|
-
|
31
|
+
|
26
32
|
end
|
27
33
|
end
|