Skip to content

Commit 7b6146f

Browse files
committed
Enable log file rotation on Windows
Since ruby 2.3, a file opened with `File::SHARE_DELETE` and `File::BINARY` can be renamed or removed.
1 parent f904ad2 commit 7b6146f

File tree

2 files changed

+69
-38
lines changed

2 files changed

+69
-38
lines changed

lib/logger/log_device.rb

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ def reopen(log = nil)
6767

6868
private
6969

70+
# :stopdoc:
71+
72+
MODE = File::WRONLY | File::APPEND
73+
MODE_TO_OPEN = MODE | File::SHARE_DELETE | File::BINARY
74+
MODE_TO_CREATE = MODE_TO_OPEN | File::CREAT | File::EXCL
75+
7076
def set_dev(log)
7177
if log.respond_to?(:write) and log.respond_to?(:close)
7278
@dev = log
@@ -77,34 +83,54 @@ def set_dev(log)
7783
end
7884
else
7985
@dev = open_logfile(log)
80-
@dev.sync = true
81-
@dev.binmode if @binmode
8286
@filename = log
8387
end
8488
end
8589

90+
if MODE_TO_OPEN == MODE
91+
def fixup_mode(dev, filename)
92+
dev
93+
end
94+
else
95+
def fixup_mode(dev, filename)
96+
return dev if @binmode
97+
dev.autoclose = false
98+
old_dev = dev
99+
dev = File.new(dev.fileno, mode: MODE, path: filename)
100+
old_dev.close
101+
PathAttr.set_path(dev, filename) if defined?(PathAttr)
102+
dev
103+
end
104+
end
105+
86106
def open_logfile(filename)
87107
begin
88-
File.open(filename, (File::WRONLY | File::APPEND))
108+
dev = File.open(filename, MODE_TO_OPEN)
89109
rescue Errno::ENOENT
90110
create_logfile(filename)
111+
else
112+
dev = fixup_mode(dev, filename)
113+
dev.sync = true
114+
dev.binmode if @binmode
115+
dev
91116
end
92117
end
93118

94119
def create_logfile(filename)
95120
begin
96-
logdev = File.open(filename, (File::WRONLY | File::APPEND | File::CREAT | File::EXCL))
121+
logdev = File.open(filename, MODE_TO_CREATE)
97122
logdev.flock(File::LOCK_EX)
123+
logdev = fixup_mode(logdev, filename)
98124
logdev.sync = true
99125
logdev.binmode if @binmode
100126
add_log_header(logdev)
101127
logdev.flock(File::LOCK_UN)
128+
logdev
102129
rescue Errno::EEXIST
103130
# file is created by another process
104-
logdev = open_logfile(filename)
105-
logdev.sync = true
131+
open_logfile(filename)
106132
end
107-
logdev
133+
end
108134

109135
def handle_write_errors(mesg)
110136
yield
@@ -135,40 +161,33 @@ def check_shift_log
135161
end
136162
end
137163

138-
if /mswin|mingw|cygwin/ =~ RbConfig::CONFIG['host_os']
139-
def lock_shift_log
140-
yield
141-
end
142-
else
143-
def lock_shift_log
144-
retry_limit = 8
145-
retry_sleep = 0.1
146-
begin
147-
File.open(@filename, File::WRONLY | File::APPEND) do |lock|
148-
lock.flock(File::LOCK_EX) # inter-process locking. will be unlocked at closing file
149-
if File.identical?(@filename, lock) and File.identical?(lock, @dev)
150-
yield # log shifting
151-
else
152-
# log shifted by another process (i-node before locking and i-node after locking are different)
153-
@dev.close rescue nil
154-
@dev = open_logfile(@filename)
155-
@dev.sync = true
156-
end
157-
end
158-
rescue Errno::ENOENT
159-
# @filename file would not exist right after #rename and before #create_logfile
160-
if retry_limit <= 0
161-
warn("log rotation inter-process lock failed. #{$!}")
164+
def lock_shift_log
165+
retry_limit = 8
166+
retry_sleep = 0.1
167+
begin
168+
File.open(@filename, MODE_TO_OPEN) do |lock|
169+
lock.flock(File::LOCK_EX) # inter-process locking. will be unlocked at closing file
170+
if File.identical?(@filename, lock) and File.identical?(lock, @dev)
171+
yield # log shifting
162172
else
163-
sleep retry_sleep
164-
retry_limit -= 1
165-
retry_sleep *= 2
166-
retry
173+
# log shifted by another process (i-node before locking and i-node after locking are different)
174+
@dev.close rescue nil
175+
@dev = open_logfile(@filename)
167176
end
168177
end
169-
rescue
170-
warn("log rotation inter-process lock failed. #{$!}")
178+
rescue Errno::ENOENT
179+
# @filename file would not exist right after #rename and before #create_logfile
180+
if retry_limit <= 0
181+
warn("log rotation inter-process lock failed. #{$!}")
182+
else
183+
sleep retry_sleep
184+
retry_limit -= 1
185+
retry_sleep *= 2
186+
retry
187+
end
171188
end
189+
rescue
190+
warn("log rotation inter-process lock failed. #{$!}")
172191
end
173192

174193
def shift_log_age
@@ -203,3 +222,15 @@ def shift_log_period(period_end)
203222
end
204223
end
205224
end
225+
226+
File.open(IO::NULL) do |f|
227+
File.new(f.fileno, autoclose: false, path: "").path
228+
rescue IOError
229+
module PathAttr # :nodoc:
230+
attr_reader :path
231+
232+
def self.set_path(file, path)
233+
file.extend(self).instance_variable_set(:@path, path)
234+
end
235+
end
236+
end

test/logger/test_logdevice.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ def test_shifting_size_not_rotate_too_much
452452
end
453453
ensure
454454
logdev0.close
455-
end unless /mswin|mingw|cygwin/ =~ RbConfig::CONFIG['host_os']
455+
end
456456

457457
def test_shifting_midnight
458458
Dir.mktmpdir do |tmpdir|

0 commit comments

Comments
 (0)