serverengine 1.5.5 → 1.5.6

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --require spec_helper
2
+
data/.travis.yml CHANGED
@@ -3,7 +3,6 @@ language: ruby
3
3
  rvm:
4
4
  - 1.9.3
5
5
  - 2.0.0
6
- - rbx-19mode
7
6
 
8
7
  branches:
9
8
  only:
data/Changelog CHANGED
@@ -1,4 +1,10 @@
1
1
 
2
+ 2013-10-20 version 1.5.6:
3
+
4
+ * Fixed log rotation in worker_type=process mode [#9]
5
+ * DaemonLogger supports 'trace' level
6
+
7
+
2
8
  2013-09-17 version 1.5.5:
3
9
 
4
10
  * worker_type=thread and embedded show uncaught errors caused in Worker#stop
data/README.md CHANGED
@@ -57,33 +57,35 @@ se = ServerEngine.create(nil, MyWorker, {
57
57
  se.run
58
58
  ```
59
59
 
60
- Send `TERM` signal to kill the daemon. See also **Signals** section bellow.
60
+ Send `TERM` signal to kill the daemon. See also **Signals** section bellow for details.
61
61
 
62
62
 
63
63
  ### Multiprocess server
64
64
 
65
- Simply set **process** or **thread** to `worker_type` parameter and number of workers to `workers` parameter.
65
+ Simply set **worker_type=process** or **worker_type=thread** parameter, and set number of workers to `workers` parameter.
66
66
 
67
67
  ```ruby
68
68
  se = ServerEngine.create(nil, MyWorker, {
69
69
  :daemonize => true,
70
70
  :log => 'myserver.log',
71
71
  :pid_path => 'myserver.pid',
72
- :workers => 4,
73
72
  :worker_type => 'process',
73
+ :workers => 4,
74
74
  })
75
75
  se.run
76
76
  ```
77
77
 
78
78
  See also **Worker types** section bellow.
79
79
 
80
- #### Multiprocess TCP server
81
80
 
82
- One of typical implementation styles of TCP servers is that a parent process listens socket and child processes accept connections from clients.
81
+ ### Multiprocess TCP server
82
+
83
+ One of the typical implementation styles of TCP servers is that a parent process listens socket and child processes accept connections from clients.
83
84
 
84
- You can optionally implement a server module to control the parent process.
85
+ ServerEngine allows you to optionally implement a server module to control the parent process:
85
86
 
86
87
  ```ruby
88
+ # Server module controls the parent process
87
89
  module MyServer
88
90
  def before_run
89
91
  @sock = TCPServer.new(config[:bind], config[:port])
@@ -92,6 +94,7 @@ module MyServer
92
94
  attr_reader :sock
93
95
  end
94
96
 
97
+ # Worker module controls child processes
95
98
  module MyWorker
96
99
  def run
97
100
  until @stop
@@ -111,8 +114,8 @@ se = ServerEngine.create(MyServer, MyWorker, {
111
114
  :daemonize => true,
112
115
  :log => 'myserver.log',
113
116
  :pid_path => 'myserver.pid',
114
- :workers => 4,
115
117
  :worker_type => 'process',
118
+ :workers => 4,
116
119
  :bind => '0.0.0.0',
117
120
  :port => 9071,
118
121
  })
@@ -141,7 +144,7 @@ See also **Configuration** section bellow.
141
144
 
142
145
  Server programs running 24x7 hours need to survive even if a process stalled because of unexpected memory swapping or network errors.
143
146
 
144
- Supervisor automatically reboots the server process if heartbeat breaks out.
147
+ Supervisor process runs as the parent process of the server process and monitor it to restart automatically.
145
148
 
146
149
  ```ruby
147
150
  se = ServerEngine.create(nil, MyWorker, {
@@ -152,10 +155,11 @@ se = ServerEngine.create(nil, MyWorker, {
152
155
  se.run
153
156
  ```
154
157
 
158
+
155
159
  ### Live restart
156
160
 
157
- You can restart a server process without waiting for completion of shutdown process (if `supervisor` and `enable_detach` parameters are enabled).
158
- This feature is useful to minimize downtime where workers take long time to complete tasks.
161
+ You can restart a server process without waiting for completion of all workers using `INT` signal (`supervisor=true` and `enable_detach=true` parameters must be enabled).
162
+ This feature allows you to minimize downtime where workers take long time to complete a task.
159
163
 
160
164
  ```
161
165
  # 1. starts server
@@ -187,7 +191,7 @@ This feature is useful to minimize downtime where workers take long time to comp
187
191
  +----------+ +-----------+
188
192
  ```
189
193
 
190
- Note that network servers (which listen sockets) shouldn't use live restart because it causes "Address already in use" error. Instead, simply use `worker_type=process` configuration and send `USR1` to restart only workers. USR1 signal doesn't restart server (by default. See also `restart_server_process` parameter). Restarting workers don't wait for completion of all running workers.
194
+ Note that network servers (which listen sockets) shouldn't use live restart because it causes "Address already in use" error at the server process. Instead, simply use `worker_type=process` configuration and send `USR1` to restart workers instead of the server. It restarts a worker without waiting for shutdown of the other workers. This way doesn't cause downtime because server process doesn't close listening sockets and keeps accepting new clients (See also `restart_server_process` parameter if necessary).
191
195
 
192
196
 
193
197
  ### Dynamic configuration reloading
@@ -322,43 +326,43 @@ Graceful shutdown and restart call `Worker#stop` method and wait for completion
322
326
  ## Configuration
323
327
 
324
328
  - Daemon
325
- - **daemonize** enables daemonize (default: false) (not dynamic reloadable)
326
- - **pid_path** sets the path to pid file (default: don't create pid file) (not dynamic reloadable)
327
- - **supervisor** enables supervisor if it's true (default: false) (not dynamic reloadable)
328
- - **daemon_process_name** changes process name ($0) of server or supervisor process (not dynamic reloadable)
329
- - **chuser** changes execution user (not dynamic reloadable)
330
- - **chgroup** changes execution group (not dynamic reloadable)
331
- - **chumask** changes umask (not dynamic reloadable)
332
- - **daemonize_error_exit_code** exit code when daemonize, changing user or changing group fails (default: 1) (not dynamic reloadable)
329
+ - **daemonize** enables daemonize (default: false)
330
+ - **pid_path** sets the path to pid file (default: don't create pid file)
331
+ - **supervisor** enables supervisor if it's true (default: false)
332
+ - **daemon_process_name** changes process name ($0) of server or supervisor process
333
+ - **chuser** changes execution user
334
+ - **chgroup** changes execution group
335
+ - **chumask** changes umask
336
+ - **daemonize_error_exit_code** exit code when daemonize, changing user or changing group fails (default: 1)
333
337
  - Supervisor: available only when `supervisor` parameters is true
334
- - **server_process_name** changes process name ($0) of server process (not dynamic reloadable)
335
- - **restart_server_process** restarts server process when it receives USR1 or HUP signal. (default: false) (not dynamic reloadable)
336
- - **enable_detach** enables INT signal (default: true) (not dynamic reloadable)
337
- - **exit_on_detach** exits supervisor after detaching server process instead of restarting it (default: false) (not dynamic reloadable)
338
- - **disable_reload** disables USR2 signal (default: false) (not dynamic reloadable)
339
- - **server_restart_wait** sets wait time before restarting server after last restarting (default: 1.0)
340
- - **server_detach_wait** sets wait time before starting live restart (default: 10.0)
338
+ - **server_process_name** changes process name ($0) of server process
339
+ - **restart_server_process** restarts server process when it receives USR1 or HUP signal. (default: false)
340
+ - **enable_detach** enables INT signal (default: true)
341
+ - **exit_on_detach** exits supervisor after detaching server process instead of restarting it (default: false)
342
+ - **disable_reload** disables USR2 signal (default: false)
343
+ - **server_restart_wait** sets wait time before restarting server after last restarting (default: 1.0) [dynamic reloadable]
344
+ - **server_detach_wait** sets wait time before starting live restart (default: 10.0) [dynamic reloadable]
341
345
  - Multithread server and multiprocess server: available only when `worker_type` is thread or process
342
- - **workers** sets number of workers (default: 1)
343
- - **start_worker_delay** sets wait time before starting a new worker (default: 0)
344
- - **start_worker_delay_rand** randomizes start_worker_delay at this ratio (default: 0.2)
345
- - Multiprocess server: available only when `worker_type` is "process"
346
- - **worker_process_name** changes process name ($0) of workers
347
- - **worker_heartbeat_interval** sets interval of heartbeats in seconds (default: 1.0)
348
- - **worker_heartbeat_timeout** sets timeout of heartbeat in seconds (default: 180)
349
- - **worker_graceful_kill_interval** sets the first interval of TERM signals in seconds (default: 15)
350
- - **worker_graceful_kill_interval_increment** sets increment of TERM signal interval in seconds (default: 10)
351
- - **worker_graceful_kill_timeout** sets promotion timeout from TERM to QUIT signal in seconds. -1 means no timeout (default: 600)
352
- - **worker_immediate_kill_interval** sets the first interval of QUIT signals in seconds (default: 10)
353
- - **worker_immediate_kill_interval_increment** sets increment of QUIT signal interval in seconds (default: 10)
354
- - **worker_immediate_kill_timeout** sets promotion timeout from QUIT to KILL signal in seconds. -1 means no timeout (default: 600)
346
+ - **workers** sets number of workers (default: 1) [dynamic reloadable]
347
+ - **start_worker_delay** sets wait time before starting a new worker (default: 0) [dynamic reloadable]
348
+ - **start_worker_delay_rand** randomizes start_worker_delay at this ratio (default: 0.2) [dynamic reloadable]
349
+ - Multiprocess server: available only when `worker_type` is "process" [dynamic reloadable]
350
+ - **worker_process_name** changes process name ($0) of workers [dynamic reloadable]
351
+ - **worker_heartbeat_interval** sets interval of heartbeats in seconds (default: 1.0) [dynamic reloadable]
352
+ - **worker_heartbeat_timeout** sets timeout of heartbeat in seconds (default: 180) [dynamic reloadable]
353
+ - **worker_graceful_kill_interval** sets the first interval of TERM signals in seconds (default: 15) [dynamic reloadable]
354
+ - **worker_graceful_kill_interval_increment** sets increment of TERM signal interval in seconds (default: 10) [dynamic reloadable]
355
+ - **worker_graceful_kill_timeout** sets promotion timeout from TERM to QUIT signal in seconds. -1 means no timeout (default: 600) [dynamic reloadable]
356
+ - **worker_immediate_kill_interval** sets the first interval of QUIT signals in seconds (default: 10) [dynamic reloadable]
357
+ - **worker_immediate_kill_interval_increment** sets increment of QUIT signal interval in seconds (default: 10) [dynamic reloadable]
358
+ - **worker_immediate_kill_timeout** sets promotion timeout from QUIT to KILL signal in seconds. -1 means no timeout (default: 600) [dynamic reloadable]
355
359
  - Logger
356
- - **log** sets path to log file. Set "-" for STDOUT (default: STDERR)
357
- - **log_level** log level: debug, info, warn, error or fatal. (default: debug)
358
- - **log_rotate_age** generations to keep rotated log files (default: 5) (not dynamic reloadable)
359
- - **log_rotate_size** sets the size to rotate log files (default: 1048576) (not dynamic reloadable)
360
- - **log_stdout** hooks STDOUT to log file (default: true) (not dynamic reloadable)
361
- - **log_stderr** hooks STDERR to log file (default: true) (not dynamic reloadable)
360
+ - **log** sets path to log file. Set "-" for STDOUT (default: STDERR) [dynamic reloadable]
361
+ - **log_level** log level: trace, debug, info, warn, error or fatal. (default: debug) [dynamic reloadable]
362
+ - **log_rotate_age** generations to keep rotated log files (default: 5)
363
+ - **log_rotate_size** sets the size to rotate log files (default: 1048576)
364
+ - **log_stdout** hooks STDOUT to log file (default: true)
365
+ - **log_stderr** hooks STDERR to log file (default: true)
362
366
  - **logger_class** class of the logger instance (default: ServerEngine::DaemonLogger)
363
367
 
364
368
  ---
data/Rakefile CHANGED
@@ -8,7 +8,6 @@ require 'rspec/core/rake_task'
8
8
 
9
9
  RSpec::Core::RakeTask.new(:spec) do |t|
10
10
  t.fail_on_error = false
11
- t.rspec_opts = %w[-rspec_helper]
12
11
  end
13
12
 
14
13
  task :default => [:spec, :build]
@@ -40,20 +40,13 @@ module ServerEngine
40
40
 
41
41
  @logger_class = @config[:logger_class] || DaemonLogger
42
42
 
43
- case c = @config[:log]
44
- when nil # default
45
- @log_dev = STDERR
46
- when "-"
47
- @log_dev = STDOUT
48
- else
49
- @log_dev = c
50
- end
51
-
52
43
  if @logger
53
- if @log_dev.is_a?(String)
54
- @logger.path = @log_dev
44
+ logdev = logdev_from_config(@config)
45
+ unless logdev.is_a?(IO)
46
+ # Here doesn't allow to change logdev to IO dynamically
47
+ # because Server#start_io_logging_thread can't follow it.
48
+ @logger.logdev = io
55
49
  end
56
-
57
50
  @logger.level = @config[:log_level] || 'debug'
58
51
  end
59
52
 
@@ -68,7 +61,18 @@ module ServerEngine
68
61
  return
69
62
  end
70
63
 
71
- @logger = @logger_class.new(@log_dev, @config)
64
+ @logger = @logger_class.new(logdev_from_config(@config), @config)
65
+ end
66
+
67
+ def logdev_from_config(config)
68
+ case c = @config[:log]
69
+ when nil # default
70
+ return STDERR
71
+ when "-"
72
+ return STDOUT
73
+ else
74
+ return c
75
+ end
72
76
  end
73
77
  end
74
78
 
@@ -20,42 +20,74 @@ module ServerEngine
20
20
  require 'logger'
21
21
 
22
22
  class DaemonLogger < Logger
23
- def initialize(dev, config={})
24
- @hook_stdout = config.fetch(:log_stdout, true)
25
- @hook_stderr = config.fetch(:log_stderr, true)
23
+ def initialize(logdev, config={})
26
24
  rotate_age = config[:log_rotate_age] || 5
27
25
  rotate_size = config[:log_rotate_size] || 1048576
28
26
 
29
- if dev.is_a?(String)
30
- @path = dev
31
- @io = File.open(@path, "a")
32
- @io.sync = true
27
+ @file_dev = MultiprocessFileLogDevice.new(nil,
28
+ shift_age: rotate_age, shift_size: rotate_size)
29
+
30
+ super(nil)
31
+
32
+ self.level = config[:log_level] || 'debug'
33
+ self.logdev = logdev
34
+ end
35
+
36
+ def logdev=(logdev)
37
+ # overwrites Logger's @logdev variable
38
+ if logdev.respond_to?(:write) and logdev.respond_to?(:close)
39
+ # IO
40
+ @logdev = logdev
41
+ @logdev.sync = true if @logdev.respond_to?(:sync=)
42
+ @file_dev.path = nil
33
43
  else
34
- @io = dev
44
+ # path string
45
+ @file_dev.path = logdev
46
+ @logdev = @file_dev
35
47
  end
48
+ logdev
49
+ end
36
50
 
37
- hook_stdout! if @hook_stdout
38
- hook_stderr! if @hook_stderr
39
-
40
- super(@io, rotate_age, rotate_size)
51
+ # override add method
52
+ def add(severity, message = nil, progname = nil, &block)
53
+ if severity < @level
54
+ return true
55
+ end
56
+ if message.nil?
57
+ if block_given?
58
+ message = yield
59
+ else
60
+ message = progname
61
+ progname = nil
62
+ end
63
+ end
64
+ progname ||= @progname
65
+ self << format_message(SEVERITY_FORMATS_[severity+1], Time.now, progname, message)
66
+ true
67
+ end
41
68
 
42
- self.level = config[:log_level] || 'debug'
69
+ module Severity
70
+ include Logger::Severity
71
+ TRACE = -1
43
72
  end
73
+ include Severity
44
74
 
45
- attr_accessor :path
75
+ SEVERITY_FORMATS_ = %w(TRACE DEBUG INFO WARN ERROR FATAL ANY)
46
76
 
47
77
  def level=(expr)
48
78
  case expr.to_s
49
- when 'fatal', Logger::FATAL.to_s
50
- e = Logger::FATAL
51
- when 'error', Logger::ERROR.to_s
52
- e = Logger::ERROR
53
- when 'warn', Logger::WARN.to_s
54
- e = Logger::WARN
55
- when 'info', Logger::INFO.to_s
56
- e = Logger::INFO
57
- when 'debug', Logger::DEBUG.to_s
58
- e = Logger::DEBUG
79
+ when 'fatal', FATAL.to_s
80
+ e = FATAL
81
+ when 'error', ERROR.to_s
82
+ e = ERROR
83
+ when 'warn', WARN.to_s
84
+ e = WARN
85
+ when 'info', INFO.to_s
86
+ e = INFO
87
+ when 'debug', DEBUG.to_s
88
+ e = DEBUG
89
+ when 'trace', TRACE.to_s
90
+ e = TRACE
59
91
  else
60
92
  raise ArgumentError, "invalid log level: #{expr}"
61
93
  end
@@ -63,29 +95,10 @@ module ServerEngine
63
95
  super(e)
64
96
  end
65
97
 
66
- def hook_stdout!
67
- STDOUT.sync = true
68
- @hook_stdout = true
69
-
70
- STDOUT.reopen(@io) if @io != STDOUT
71
- self
72
- end
73
-
74
- def hook_stderr!
75
- STDERR.sync = true
76
- @hook_stderr = true
77
-
78
- STDERR.reopen(@io) if @io != STDERR
79
- self
80
- end
98
+ def trace?; @level <= TRACE; end
81
99
 
82
100
  def reopen!
83
- if @path
84
- @io.reopen(@path, "a")
85
- @io.sync = true
86
- hook_stdout! if @hook_stdout
87
- hook_stderr! if @hook_stderr
88
- end
101
+ @file_dev.reopen!
89
102
  nil
90
103
  end
91
104
 
@@ -100,11 +113,139 @@ module ServerEngine
100
113
  end
101
114
 
102
115
  def close
103
- if @path
104
- @io.close unless @io.closed?
105
- end
116
+ @file_dev.close
106
117
  nil
107
118
  end
119
+
120
+ class MultiprocessFileLogDevice
121
+ def initialize(path, opts={})
122
+ @shift_age = opts[:shift_age] || 7
123
+ @shift_size = opts[:shift_size] || 1024*1024
124
+ @mutex = Mutex.new
125
+ self.path = path
126
+ end
127
+
128
+ def write(data)
129
+ # it's hard to remove this synchronize because IO#write raises
130
+ # Errno::ENOENT if IO#reopen is running concurrently.
131
+ @mutex.synchronize do
132
+ unless @file
133
+ return nil
134
+ end
135
+ log_rotate_or_reopen
136
+ @file.write(data)
137
+ end
138
+ rescue Exception => e
139
+ warn "log writing failed: #{e}"
140
+ end
141
+
142
+ def path=(path)
143
+ @mutex.synchronize do
144
+ old_file = @file
145
+ file = open_logfile(path)
146
+ begin
147
+ @file = file
148
+ @path = path
149
+ file = old_file
150
+ ensure
151
+ file.close if file
152
+ end
153
+ end
154
+ return path
155
+ end
156
+
157
+ def close
158
+ @mutex.synchronize do
159
+ @file.close
160
+ @file = nil
161
+ end
162
+ nil
163
+ end
164
+
165
+ attr_reader :path
166
+
167
+ def reopen!
168
+ @mutex.synchronize do
169
+ if @file
170
+ @file.reopen(@path, 'a')
171
+ @file.sync = true
172
+ end
173
+ end
174
+ true
175
+ end
176
+
177
+ # for compatibility with Logger::LogDevice
178
+ def dev
179
+ @file
180
+ end
181
+
182
+ # for compatibility with Logger::LogDevice
183
+ def filename
184
+ @path
185
+ end
186
+
187
+ private
188
+
189
+ def open_logfile(path)
190
+ return nil unless path
191
+ file = File.open(path, 'a')
192
+ file.sync = true
193
+ return file
194
+ end
195
+
196
+ def log_rotate_or_reopen
197
+ stat = @file.stat
198
+ if stat.size <= @shift_size
199
+ return
200
+ end
201
+
202
+ # inter-process locking
203
+ retry_limit = 8
204
+ retry_sleep = 0.1
205
+ begin
206
+ # 1) other process is log-rotating now
207
+ # 2) other process log rotated
208
+ # 3) no active processes
209
+ lock = File.open(@path, File::WRONLY | File::APPEND)
210
+ begin
211
+ lock.flock(File::LOCK_EX)
212
+ ino = lock.stat.ino
213
+ if ino == File.stat(@path).ino
214
+ # 3)
215
+ log_rotate
216
+ else
217
+ reopen!
218
+ end
219
+ rescue
220
+ lock.close
221
+ end
222
+ rescue Errno::ENOENT => e
223
+ raise e if retry_limit <= 0
224
+ sleep retry_sleep
225
+ retry_limit -= 1
226
+ retry_sleep *= 2
227
+ retry
228
+ end
229
+
230
+ rescue => e
231
+ warn "log rotation inter-process lock failed: #{e}"
232
+ end
233
+
234
+ def log_rotate
235
+ (@shift_age-2).downto(0) do |i|
236
+ old_path = "#{@path}.#{i}"
237
+ shift_path = "#{@path}.#{i+1}"
238
+ if FileTest.exist?(old_path)
239
+ File.rename(old_path, shift_path)
240
+ end
241
+ end
242
+ File.rename(@path, "#{@path}.0")
243
+ @file.reopen(@path, 'a')
244
+ @file.sync = true
245
+ rescue => e
246
+ warn "log rotation failed: #{e}"
247
+ end
248
+ end
108
249
  end
109
250
 
110
251
  end
@@ -26,6 +26,11 @@ module ServerEngine
26
26
  @stop = false
27
27
 
28
28
  super(load_config_proc, &block)
29
+
30
+ @log_stdout = !!@config.fetch(:log_stdout, true)
31
+ @log_stderr = !!@config.fetch(:log_stderr, true)
32
+ @log_stdout = false if logdev_from_config(@config) == STDOUT
33
+ @log_stderr = false if logdev_from_config(@config) == STDERR
29
34
  end
30
35
 
31
36
  def before_run
@@ -70,6 +75,10 @@ module ServerEngine
70
75
  def main
71
76
  create_logger unless @logger
72
77
 
78
+ # start threads to transfer logs from STDOUT/ERR to the logger
79
+ start_io_logging_thread(STDOUT) if @log_stdout
80
+ start_io_logging_thread(STDERR) if @log_stderr
81
+
73
82
  before_run
74
83
 
75
84
  begin
@@ -93,6 +102,22 @@ module ServerEngine
93
102
  w.instance_eval { initialize }
94
103
  w
95
104
  end
105
+
106
+ def start_io_logging_thread(io)
107
+ r, w = IO.pipe
108
+ io.reopen(w)
109
+ w.close
110
+
111
+ Thread.new do
112
+ begin
113
+ while line = r.gets
114
+ @logger << line
115
+ end
116
+ rescue => e
117
+ ServerEngine.dump_uncaught_error(e)
118
+ end
119
+ end
120
+ end
96
121
  end
97
122
 
98
123
  end
@@ -1,3 +1,3 @@
1
1
  module ServerEngine
2
- VERSION = "1.5.5"
2
+ VERSION = "1.5.6"
3
3
  end
@@ -1,40 +1,26 @@
1
+ require 'stringio'
1
2
 
2
3
  describe ServerEngine::DaemonLogger do
3
4
  before { FileUtils.mkdir_p("tmp") }
4
5
  before { FileUtils.rm_f("tmp/se1.log") }
6
+ before { FileUtils.rm_f Dir["tmp/se1.log.**"] }
5
7
  before { FileUtils.rm_f("tmp/se2.log") }
6
8
 
7
- subject { DaemonLogger.new("tmp/se1.log", log_stdout: false, log_stderr: false) }
9
+ subject { DaemonLogger.new("tmp/se1.log", level: 'trace') }
8
10
 
9
11
  it 'reopen' do
10
- subject.path = 'tmp/se2.log'
11
- subject.reopen!
12
- subject.warn "test"
13
-
14
- File.read('tmp/se2.log').should =~ /test$/
15
- end
16
-
17
- it 'stderr hook 1' do
18
- subject.hook_stderr!
19
- STDERR.puts "test"
20
-
21
- File.read('tmp/se1.log').should == "test\n"
22
- end
12
+ subject.warn "ABCDEF"
13
+ File.open('tmp/se1.log', "w") {|f| }
14
+ subject.warn "test2"
23
15
 
24
- it 'stderr hook 2' do
25
- log = DaemonLogger.new("tmp/se1.log", log_stdout: false, log_stderr: true)
26
- STDERR.puts "test"
27
-
28
- File.read('tmp/se1.log').should == "test\n"
16
+ File.read('tmp/se1.log').should_not =~ /ABCDEF/
29
17
  end
30
18
 
31
- it 'stderr hook and reopen' do
32
- subject.hook_stderr!
33
- subject.path = 'tmp/se2.log'
34
- subject.reopen!
35
- STDERR.puts "test"
19
+ it 'reset path' do
20
+ subject.logdev = 'tmp/se2.log'
21
+ subject.warn "test"
36
22
 
37
- File.read('tmp/se2.log').should == "test\n"
23
+ File.read('tmp/se2.log').should =~ /test$/
38
24
  end
39
25
 
40
26
  it 'default level is debug' do
@@ -45,18 +31,57 @@ describe ServerEngine::DaemonLogger do
45
31
  it 'level set by int' do
46
32
  subject.level = Logger::FATAL
47
33
  subject.level.should == Logger::FATAL
34
+ subject.trace?.should == false
35
+ subject.debug?.should == false
36
+ subject.info?.should == false
37
+ subject.warn?.should == false
38
+ subject.error?.should == false
39
+ subject.fatal?.should == true
48
40
 
49
41
  subject.level = Logger::ERROR
50
42
  subject.level.should == Logger::ERROR
43
+ subject.trace?.should == false
44
+ subject.debug?.should == false
45
+ subject.info?.should == false
46
+ subject.warn?.should == false
47
+ subject.error?.should == true
48
+ subject.fatal?.should == true
51
49
 
52
50
  subject.level = Logger::WARN
53
51
  subject.level.should == Logger::WARN
52
+ subject.trace?.should == false
53
+ subject.debug?.should == false
54
+ subject.info?.should == false
55
+ subject.warn?.should == true
56
+ subject.error?.should == true
57
+ subject.fatal?.should == true
54
58
 
55
59
  subject.level = Logger::INFO
56
60
  subject.level.should == Logger::INFO
61
+ subject.trace?.should == false
62
+ subject.debug?.should == false
63
+ subject.info?.should == true
64
+ subject.warn?.should == true
65
+ subject.error?.should == true
66
+ subject.fatal?.should == true
57
67
 
58
68
  subject.level = Logger::DEBUG
59
69
  subject.level.should == Logger::DEBUG
70
+ subject.trace?.should == false
71
+ subject.debug?.should == true
72
+ subject.info?.should == true
73
+ subject.warn?.should == true
74
+ subject.error?.should == true
75
+ subject.fatal?.should == true
76
+
77
+ subject.level = DaemonLogger::TRACE
78
+ subject.level.should == DaemonLogger::TRACE
79
+ subject.trace?.should == true
80
+ subject.debug?.should == true
81
+ subject.info?.should == true
82
+ subject.warn?.should == true
83
+ subject.error?.should == true
84
+ subject.fatal?.should == true
60
85
  end
61
86
 
62
87
  it 'level set by string' do
@@ -74,15 +99,43 @@ describe ServerEngine::DaemonLogger do
74
99
 
75
100
  subject.level = 'debug'
76
101
  subject.level.should == Logger::DEBUG
102
+
103
+ subject.level = 'trace'
104
+ subject.level.should == DaemonLogger::TRACE
77
105
  end
78
106
 
79
107
  it 'unknown level' do
80
108
  lambda { subject.level = 'unknown' }.should raise_error(ArgumentError)
81
109
  end
82
110
 
83
- it 'stdout logger' do
84
- STDOUT.should_not_receive(:reopen)
85
- log = DaemonLogger.new(STDOUT)
111
+ it 'rotation' do
112
+ log = DaemonLogger.new("tmp/se1.log", level: 'trace', log_rotate_age: 3, log_rotate_size: 10)
113
+ log.warn "test1"
114
+ File.exist?("tmp/se1.log").should == true
115
+ File.exist?("tmp/se1.log.0").should == false
116
+
117
+ log.warn "test2"
118
+ File.exist?("tmp/se1.log").should == true
119
+ File.exist?("tmp/se1.log.0").should == true
120
+ File.read("tmp/se1.log.0") =~ /test2$/
121
+
122
+ log.warn "test3"
123
+ log.warn "test4"
124
+ File.exist?("tmp/se1.log").should == true
125
+ File.exist?("tmp/se1.log.2").should == true
126
+ File.exist?("tmp/se1.log.3").should == false
127
+
128
+ log.warn "test5"
129
+ File.read("tmp/se1.log.0") =~ /test5$/
130
+ end
131
+
132
+ it 'IO logger' do
133
+ io = StringIO.new
134
+ io.should_receive(:write)
135
+ io.should_not_receive(:reopen)
136
+
137
+ log = DaemonLogger.new(io)
86
138
  log.debug "stdout logging test"
139
+ log.reopen!
87
140
  end
88
141
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serverengine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.5
4
+ version: 1.5.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-09-17 00:00:00.000000000 Z
12
+ date: 2013-10-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sigdump
@@ -67,6 +67,7 @@ extensions: []
67
67
  extra_rdoc_files: []
68
68
  files:
69
69
  - .gitignore
70
+ - .rspec
70
71
  - .travis.yml
71
72
  - Changelog
72
73
  - Gemfile
@@ -118,6 +119,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
119
  - - ! '>='
119
120
  - !ruby/object:Gem::Version
120
121
  version: '0'
122
+ segments:
123
+ - 0
124
+ hash: -2266789879425924886
121
125
  requirements: []
122
126
  rubyforge_project:
123
127
  rubygems_version: 1.8.23
@@ -133,4 +137,3 @@ test_files:
133
137
  - spec/signal_thread_spec.rb
134
138
  - spec/spec_helper.rb
135
139
  - spec/supervisor_spec.rb
136
- has_rdoc: false