serverengine 1.5.5 → 1.5.6

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/.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