daemonizer 0.4.18 → 0.5.0.beta.1
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.
- 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
|