logrotate 1.0.0 → 1.1.0
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/History.txt +9 -0
- data/Manifest.txt +3 -0
- data/README.txt +148 -26
- data/Rakefile +1 -1
- data/lib/logrotate.rb +2 -245
- data/lib/logrotate/impl.rb +416 -0
- data/lib/logrotate/logrotate.rb +86 -0
- data/lib/logrotate/rotateinfo.rb +166 -0
- data/test/test_logrotate.rb +78 -37
- metadata +6 -3
@@ -0,0 +1,416 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'zlib'
|
4
|
+
require 'date'
|
5
|
+
require 'logrotate/rotateinfo'
|
6
|
+
|
7
|
+
module LogRotate
|
8
|
+
|
9
|
+
#
|
10
|
+
# This module contains implementation methods for the LogRotate class.
|
11
|
+
# In most cases, the client of this library will only use the
|
12
|
+
# LogRotate class directly to rotate files. However, I have exposed a
|
13
|
+
# number of methods in this class, as they may be useful.
|
14
|
+
#
|
15
|
+
module Impl
|
16
|
+
|
17
|
+
DEFAULT_COUNT = 5
|
18
|
+
DEFAULT_GZIP = false
|
19
|
+
DEFAULT_DATE_TIME_EXTENSION = false
|
20
|
+
DEFAULT_DATE_TIME_FORMAT = '%F'
|
21
|
+
DEFAULT_DATE_TIME_EXT = false
|
22
|
+
|
23
|
+
# This method validates the rotated file name with date extension.
|
24
|
+
# Specifically, it checks that the file name specified has a date
|
25
|
+
# that is formatted according to the date/time format specified.
|
26
|
+
#
|
27
|
+
# ====Parameters:
|
28
|
+
# [file_name]
|
29
|
+
# The rotated file name to be validated.
|
30
|
+
# [rotation_base_name]
|
31
|
+
# The base name of the rotated file (without the date/time extension).
|
32
|
+
# [date_time_format]
|
33
|
+
# The format of the date/time extension of the rotated file.
|
34
|
+
#
|
35
|
+
def self.is_valid_date_time_file_name(file_name, rotation_base_name, date_time_format)
|
36
|
+
base_name = File.basename(file_name)
|
37
|
+
|
38
|
+
if (match_data = base_name.match("^#{rotation_base_name}\.([^.]+)(\.gz)?$"))
|
39
|
+
# Since the caller is allowed to specify the date/time format,
|
40
|
+
# here we do not have a regular expression to match the
|
41
|
+
# date/time portion of the rotated file's extension. As a
|
42
|
+
# result, some foreign files can sneak into our list of rotated
|
43
|
+
# files (eg. base_file.GARBAGE.gz). We will attempt to weed
|
44
|
+
# them out below.
|
45
|
+
|
46
|
+
# Validation #1: convert the date/time portion of the
|
47
|
+
# extension to a DateTime object and skip the file if an
|
48
|
+
# exception is thrown.
|
49
|
+
begin
|
50
|
+
date_time = DateTime.strptime(match_data[1], date_time_format)
|
51
|
+
gz = match_data[2] ? match_data[2] : ""
|
52
|
+
|
53
|
+
# Validation #2: convert the date/time portion of the
|
54
|
+
# extension to a DateTime object and back to a string again.
|
55
|
+
# Then reconstruct the rotated file name with this string,
|
56
|
+
# and ensure the reconstructed rotated file matches the
|
57
|
+
# original. This will take into account cases where
|
58
|
+
# strptime succeeded but had additional garbage characters
|
59
|
+
# present.
|
60
|
+
# eg: date_time_format = '%F', file = 'mysql_backup.2008-08-04.BACK.gz'
|
61
|
+
reconstructed_file = "#{rotation_base_name}.#{date_time.strftime(date_time_format)}#{gz}"
|
62
|
+
if (base_name == reconstructed_file)
|
63
|
+
return true
|
64
|
+
end
|
65
|
+
rescue => e
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# ====Description:
|
74
|
+
# This method is passed a list of rotated file names. This method
|
75
|
+
# validates that list, and returns a list of rotated file names and
|
76
|
+
# corresponding dates/times. Specifically, an array is returned,
|
77
|
+
# where each element is a hash containing the following fields:
|
78
|
+
# +file+:: A rotated file name.
|
79
|
+
# +date_time+:: A DateTime object extracted from the rotated file name's extension.
|
80
|
+
#
|
81
|
+
# Validating the list refers to ensuring that each file name has a
|
82
|
+
# properly formatted date/time. See is_valid_date_time_file_name
|
83
|
+
# for further information.
|
84
|
+
#
|
85
|
+
# ====Parameters:
|
86
|
+
# [file_names]
|
87
|
+
# The rotated file names.
|
88
|
+
# [rotation_base_name]
|
89
|
+
# The base name of the rotated file (without the date/time extension).
|
90
|
+
# [date_time_format]
|
91
|
+
# The format of the date/time extension of the rotated file.
|
92
|
+
#
|
93
|
+
# ===Returns:
|
94
|
+
# A list of rotated file names and corresponding date/times.
|
95
|
+
#
|
96
|
+
def self.organize_rotated_files_date_extension(file_names, rotation_base_name, date_time_format)
|
97
|
+
rotated_files_and_dates = file_names.map do |file|
|
98
|
+
if (is_valid_date_time_file_name(file, rotation_base_name, date_time_format))
|
99
|
+
next get_rotated_file_and_date(file, date_time_format)
|
100
|
+
end
|
101
|
+
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
# Remove the null entries.
|
106
|
+
rotated_files_and_dates = rotated_files_and_dates.select { |entry| entry }
|
107
|
+
|
108
|
+
# Sort files by date (newer to older).
|
109
|
+
rotated_files_and_dates.sort! { |left, right| right[:date_time] <=> left[:date_time] }
|
110
|
+
|
111
|
+
return rotated_files_and_dates
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# ====Description:
|
116
|
+
# This method returns a list of rotated files that are due to be deleted.
|
117
|
+
#
|
118
|
+
# The new_rotated_file field should only be set if the caller is
|
119
|
+
# currently in the process of rotating a file (eg. inside
|
120
|
+
# LogRotate::rotate_file), and the caller wants to retain 1 less
|
121
|
+
# file than count. This would be because the caller will shortly
|
122
|
+
# create a new rotated file, and would rather remove the expired
|
123
|
+
# rotated file BEFORE creating the new rotated file to minimize
|
124
|
+
# storage space usage. Thus, there would only be count rotated
|
125
|
+
# files at any time (as opposed to count + 1 if the caller first
|
126
|
+
# generated the new rotated file, and then deleted the expired
|
127
|
+
# file).
|
128
|
+
# ====Parameters:
|
129
|
+
# [rotated_files_and_dates]
|
130
|
+
# The rotated file names and dates.
|
131
|
+
# [count]
|
132
|
+
# The number of rotated files to keep.
|
133
|
+
# [new_rotated_file]
|
134
|
+
# If the caller is in the midst of rotating a file, this value
|
135
|
+
# is set to the new rotated file (yet to be created, not listed
|
136
|
+
# in rotated_files_and_dates).
|
137
|
+
# ===Returns:
|
138
|
+
# A list of rotated file names and corresponding date/times to delete.
|
139
|
+
#
|
140
|
+
def self.get_expired_rotated_files_date_extension(rotated_files_and_dates,
|
141
|
+
count,
|
142
|
+
new_rotated_file = nil)
|
143
|
+
if (new_rotated_file)
|
144
|
+
# Add 1 to account for the additional rotated file that will be
|
145
|
+
# created after the original file passed in is rotated.
|
146
|
+
num_old_files = rotated_files_and_dates.length() - count + 1
|
147
|
+
|
148
|
+
# There is a chance that the newly rotated file will have the same
|
149
|
+
# name as one of the already existing rotated files (eg. if the
|
150
|
+
# extension is simply the date, and the job is ran a second time
|
151
|
+
# in the same day). In this case, there will be 1 less rotated
|
152
|
+
# file (and thus 1 less to delete).
|
153
|
+
if (rotated_files_and_dates.find {|entry| entry[:file] == new_rotated_file})
|
154
|
+
num_old_files -= 1
|
155
|
+
end
|
156
|
+
else
|
157
|
+
num_old_files = rotated_files_and_dates.length() - count
|
158
|
+
end
|
159
|
+
|
160
|
+
files_to_delete = []
|
161
|
+
if (num_old_files > 0)
|
162
|
+
files_to_delete = rotated_files_and_dates.slice!(-num_old_files, num_old_files)
|
163
|
+
end
|
164
|
+
|
165
|
+
return files_to_delete
|
166
|
+
end
|
167
|
+
|
168
|
+
#
|
169
|
+
# ====Description:
|
170
|
+
# This method returns a list of rotated files that are due to be deleted.
|
171
|
+
#
|
172
|
+
# The new_rotated_file field should only be set if the caller is
|
173
|
+
# currently in the process of rotating a file (eg. inside
|
174
|
+
# LogRotate::rotate_file), and the caller wants to retain 1 less
|
175
|
+
# file than count. This would be because the caller will shortly
|
176
|
+
# create a new rotated file, and would rather remove the expired
|
177
|
+
# rotated file BEFORE creating the new rotated file to minimize
|
178
|
+
# storage space usage. Thus, there would only be count rotated
|
179
|
+
# files at any time (as opposed to count + 1 if the caller first
|
180
|
+
# generated the new rotated file, and then deleted the expired
|
181
|
+
# file).
|
182
|
+
# ====Parameters:
|
183
|
+
# [rotated_files_and_counts]
|
184
|
+
# The rotated file names and associated indexes.
|
185
|
+
# [count]
|
186
|
+
# The number of rotated files to keep.
|
187
|
+
# [new_rotated_file]
|
188
|
+
# If the caller is in the midst of rotating a file, this value
|
189
|
+
# is set to the new rotated file (yet to be created, not listed
|
190
|
+
# in rotated_files_and_counts).
|
191
|
+
# ===Returns:
|
192
|
+
# A list of rotated file names and corresponding indexes to delete.
|
193
|
+
#
|
194
|
+
def self.get_expired_rotated_files_integer_extension(rotated_files_and_counts,
|
195
|
+
count,
|
196
|
+
new_rotated_file = nil)
|
197
|
+
if (new_rotated_file)
|
198
|
+
count -= 1
|
199
|
+
end
|
200
|
+
deleted_files_and_counts = rotated_files_and_counts.select { |entry| entry[:index] > count }
|
201
|
+
|
202
|
+
return deleted_files_and_counts
|
203
|
+
end
|
204
|
+
|
205
|
+
#
|
206
|
+
# ====Description:
|
207
|
+
# This method is passed a list of rotated file names. This method
|
208
|
+
# validates that list, and returns a list of rotated file names and
|
209
|
+
# corresponding indexes (derived from the file names' integer
|
210
|
+
# extensions). Specifically, an array is returned, where each element
|
211
|
+
# is a hash containing the following fields:
|
212
|
+
# +file+:: A rotated file name.
|
213
|
+
# +index+:: The index extracted from the rotated file name's extension.
|
214
|
+
#
|
215
|
+
# Validating the list of file names specified consists of ensuring
|
216
|
+
# that each file name has a properly formatted integer extension.
|
217
|
+
#
|
218
|
+
# ====Parameters:
|
219
|
+
# [file_names]
|
220
|
+
# The rotated file names.
|
221
|
+
# [rotation_base_name]
|
222
|
+
# The base name of the rotated file (without the date/time extension).
|
223
|
+
#
|
224
|
+
# ===Returns:
|
225
|
+
# A list of rotated file names and corresponding indexes.
|
226
|
+
#
|
227
|
+
def self.organize_rotated_files_integer_extension(file_names, rotation_base_name)
|
228
|
+
rotated_files_and_counts = file_names.map do |file_name|
|
229
|
+
|
230
|
+
if (file_name.match("#{rotation_base_name}\.[0-9]+(\.gz)?$"))
|
231
|
+
index = file_name.match("\.([0-9]+)(\.gz)?$")[1].to_i()
|
232
|
+
next { :index => index, :file => file_name }
|
233
|
+
end
|
234
|
+
|
235
|
+
nil
|
236
|
+
end
|
237
|
+
|
238
|
+
rotated_files_and_counts = rotated_files_and_counts.select { |entry| entry }
|
239
|
+
|
240
|
+
# Sort files by count (newer to older).
|
241
|
+
rotated_files_and_counts.sort! { |left, right| left[:index] <=> right[:index] }
|
242
|
+
|
243
|
+
return rotated_files_and_counts
|
244
|
+
end
|
245
|
+
|
246
|
+
def self.rotate_single_file(file, options = {})
|
247
|
+
gzip = options[:gzip] ? options[:gzip] : DEFAULT_GZIP
|
248
|
+
|
249
|
+
# error checking
|
250
|
+
if (!File.exist?(file)) then raise "File does not exist: #{file}." end
|
251
|
+
if (!File.readable?(file)) then raise "File is not readable: #{file}." end
|
252
|
+
|
253
|
+
if (options[:pre_rotate])
|
254
|
+
options[:pre_rotate].call()
|
255
|
+
end
|
256
|
+
|
257
|
+
is_date_time_ext = options[:date_time_ext] ? options[:date_time_ext] : DEFAULT_DATE_TIME_EXT
|
258
|
+
if (is_date_time_ext)
|
259
|
+
result = rotate_file_date_extension(file, options)
|
260
|
+
else
|
261
|
+
result = rotate_file_integer_extension(file, options)
|
262
|
+
end
|
263
|
+
|
264
|
+
if (options[:post_rotate])
|
265
|
+
options[:post_rotate].call()
|
266
|
+
end
|
267
|
+
|
268
|
+
if (gzip)
|
269
|
+
result.new_rotated_file = gzip_file(result.new_rotated_file)
|
270
|
+
result.rotated_files[0] = result.new_rotated_file
|
271
|
+
|
272
|
+
if (options[:date_time_ext])
|
273
|
+
result.rotated_files_and_dates[0][:file] = result.new_rotated_file
|
274
|
+
else
|
275
|
+
result.rotated_files_and_counts[0][:file] = result.new_rotated_file
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
return result
|
280
|
+
end
|
281
|
+
private_class_method(:rotate_single_file)
|
282
|
+
|
283
|
+
def self.rotate_file_date_extension(file, options)
|
284
|
+
count = options[:count] ? options[:count] : DEFAULT_COUNT
|
285
|
+
|
286
|
+
now = options[:date_time] ? options[:date_time] : DateTime.now()
|
287
|
+
|
288
|
+
date_time_format = options[:date_time_format] ? options[:date_time_format] : DEFAULT_DATE_TIME_FORMAT
|
289
|
+
|
290
|
+
# Get a list of the rotated files.
|
291
|
+
(source_directory, rotation_base_name) = File.split(file)
|
292
|
+
directory = options[:directory] ? options[:directory] : source_directory
|
293
|
+
|
294
|
+
rotated_files = Dir.glob("#{directory}/*").select do |entry|
|
295
|
+
File.basename(entry).match("^#{rotation_base_name}\..+(\.gz)?$")
|
296
|
+
end
|
297
|
+
rotated_files_and_dates = organize_rotated_files_date_extension(rotated_files,
|
298
|
+
rotation_base_name,
|
299
|
+
date_time_format)
|
300
|
+
|
301
|
+
#
|
302
|
+
# Remove the oldest files so that there are count rotated files remaining.
|
303
|
+
#
|
304
|
+
new_rotated_file = File.join(directory, rotation_base_name + "." + now.strftime(date_time_format))
|
305
|
+
deleted_files_and_dates = get_expired_rotated_files_date_extension(rotated_files_and_dates,
|
306
|
+
count,
|
307
|
+
new_rotated_file)
|
308
|
+
|
309
|
+
deleted_files = deleted_files_and_dates.map { |entry| entry[:file] }
|
310
|
+
if (deleted_files_and_dates.length() > 0)
|
311
|
+
File.unlink(*deleted_files)
|
312
|
+
end
|
313
|
+
|
314
|
+
# Rename the original file.
|
315
|
+
File.rename(file, new_rotated_file)
|
316
|
+
|
317
|
+
# Push the newly rotated file name and associated date/time onto
|
318
|
+
# the front of the list of rotated files and date/times to be
|
319
|
+
# returned to the caller. Note: Do not specify the newly rotated
|
320
|
+
# file's date/time as the original date/time used when formatting
|
321
|
+
# the extension. Instead, specify the date/time with the
|
322
|
+
# granularity allowed by the date_time_format field.
|
323
|
+
rotated_files_and_dates.unshift(get_rotated_file_and_date(new_rotated_file, date_time_format))
|
324
|
+
|
325
|
+
return RotateInfoDateExtension.new(rotated_files_and_dates,
|
326
|
+
rotated_files_and_dates.map { |entry| entry[:file] },
|
327
|
+
deleted_files,
|
328
|
+
new_rotated_file)
|
329
|
+
end
|
330
|
+
private_class_method(:rotate_file_date_extension)
|
331
|
+
|
332
|
+
def self.rotate_file_integer_extension(file, options)
|
333
|
+
count = options[:count] ? options[:count] : DEFAULT_COUNT
|
334
|
+
|
335
|
+
# Get a list of the backed up files.
|
336
|
+
(source_directory, rotation_base_name) = File.split(file)
|
337
|
+
directory = options[:directory] ? options[:directory] : source_directory
|
338
|
+
|
339
|
+
rotated_files = Dir.glob("#{directory}/*").select do |entry|
|
340
|
+
File.basename(entry).match("^#{rotation_base_name}\..+(\.gz)?$")
|
341
|
+
end
|
342
|
+
rotated_files_and_counts = organize_rotated_files_integer_extension(rotated_files, rotation_base_name)
|
343
|
+
|
344
|
+
# Delete old files.
|
345
|
+
new_rotated_file = File.join(directory, rotation_base_name + ".1")
|
346
|
+
deleted_files_and_counts = get_expired_rotated_files_integer_extension(rotated_files_and_counts,
|
347
|
+
count,
|
348
|
+
new_rotated_file)
|
349
|
+
deleted_files = deleted_files_and_counts.map {|entry| entry[:file] }
|
350
|
+
File.unlink(*deleted_files)
|
351
|
+
|
352
|
+
rotated_files_and_counts = rotated_files_and_counts - deleted_files_and_counts
|
353
|
+
|
354
|
+
# Sort files such that the index is descending (eg. [hello.txt.5, hello.txt.4, ..]).
|
355
|
+
rotated_files_and_counts.sort! { |left, right| right[:index] <=> left[:index] }
|
356
|
+
|
357
|
+
|
358
|
+
# Rotate the files- Increment the index on each file.
|
359
|
+
rotated_files_and_counts = rotated_files_and_counts.map do |rotated_file|
|
360
|
+
|
361
|
+
new_index = rotated_file[:index] + 1
|
362
|
+
new_file = rotated_file[:file].sub(/(^.*\.)(#{rotated_file[:index]})(\.gz)?$/,
|
363
|
+
"\\1#{new_index}\\3")
|
364
|
+
File.rename(rotated_file[:file], new_file)
|
365
|
+
|
366
|
+
{ :index => new_index, :file => new_file }
|
367
|
+
end
|
368
|
+
|
369
|
+
# Rename the original file.
|
370
|
+
File.rename(file, new_rotated_file)
|
371
|
+
|
372
|
+
# Reverse the list of files so that the index is ascending (newer
|
373
|
+
# to older), and add the newly backed up file.
|
374
|
+
rotated_files_and_counts.reverse!()
|
375
|
+
rotated_files_and_counts.unshift({ :index => 1, :file => new_rotated_file })
|
376
|
+
|
377
|
+
return RotateInfoIntegerExtension.new(rotated_files_and_counts,
|
378
|
+
rotated_files_and_counts.map { |entry| entry[:file] },
|
379
|
+
deleted_files,
|
380
|
+
new_rotated_file)
|
381
|
+
end
|
382
|
+
private_class_method(:rotate_file_integer_extension)
|
383
|
+
|
384
|
+
def self.gzip_file(file)
|
385
|
+
# Zip the original file if necessary.
|
386
|
+
gzip_file = file + ".gz"
|
387
|
+
|
388
|
+
begin
|
389
|
+
File.open(file) do |file_stream|
|
390
|
+
Zlib::GzipWriter.open(gzip_file) do |gz|
|
391
|
+
while (buffer = file_stream.read(1024))
|
392
|
+
gz.write(buffer)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
rescue => error
|
397
|
+
raise "Unable to zip file: #{file}. Error: #{error}\n"
|
398
|
+
end
|
399
|
+
File.unlink(file)
|
400
|
+
|
401
|
+
return gzip_file
|
402
|
+
end
|
403
|
+
private_class_method(:gzip_file)
|
404
|
+
|
405
|
+
def self.get_rotated_file_and_date(file_name, date_time_format)
|
406
|
+
base_name = File.basename(file_name)
|
407
|
+
|
408
|
+
match_data = base_name.match("\.([^.]+)(\.gz)?$")
|
409
|
+
date_time = DateTime.strptime(match_data[1], date_time_format)
|
410
|
+
|
411
|
+
return { :date_time => date_time, :file => file_name }
|
412
|
+
end
|
413
|
+
private_class_method(:get_rotated_file_and_date)
|
414
|
+
|
415
|
+
end
|
416
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'logrotate/impl'
|
4
|
+
require 'logrotate/rotateinfo'
|
5
|
+
|
6
|
+
#
|
7
|
+
# This module provides a few methods for log rotation.
|
8
|
+
#
|
9
|
+
|
10
|
+
module LogRotate
|
11
|
+
|
12
|
+
#
|
13
|
+
# ====Description: This method rotates the given files.
|
14
|
+
#
|
15
|
+
# ====Parameters:
|
16
|
+
# [file(s)]
|
17
|
+
# The files to be rotated.
|
18
|
+
# [options = {}]
|
19
|
+
# A list of optional arguments.
|
20
|
+
#
|
21
|
+
# See rotate_file for details.
|
22
|
+
#
|
23
|
+
# ====Returns:
|
24
|
+
# A list of RotateInfoDateExtension or
|
25
|
+
# RotateInfoIntegerExtension instances depending whether the
|
26
|
+
# option +date_time_ext+ was specified.
|
27
|
+
#
|
28
|
+
def self.rotate_files(files, options)
|
29
|
+
return files.map do |file|
|
30
|
+
Impl.send(:rotate_single_file, file, options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# ====Description:
|
36
|
+
# This method rotates the given file(s).
|
37
|
+
#
|
38
|
+
# There are 2 types of extensions that can be used: the default
|
39
|
+
# extension which is an integer value (".1", ".2", ..), and a
|
40
|
+
# date/time extension (".2008-08-01", ..). If a date/time extension
|
41
|
+
# is chosen, the caller can specify a date/time format for this
|
42
|
+
# extension.
|
43
|
+
#
|
44
|
+
# If this method fails, an exception will be thrown. If this method
|
45
|
+
# succeeds, the method will return normally, and an instance will be
|
46
|
+
# returned with information about the rotated files.
|
47
|
+
#
|
48
|
+
# ====Parameters:
|
49
|
+
# [file]
|
50
|
+
# The file to be rotated.
|
51
|
+
# [options = {}]
|
52
|
+
# A list of optional arguments.
|
53
|
+
#
|
54
|
+
# The optional arguments are listed below:
|
55
|
+
# +count+:: The number of rotated files to be kept.
|
56
|
+
# +directory+:: The directory to store the newly rotated file. If this
|
57
|
+
# option is unspecified, the original file's directory
|
58
|
+
# will be used.
|
59
|
+
# +date_time_ext+:: Whether the extension on the rotated files should be
|
60
|
+
# a date. Possible values are: +true+, +false+.
|
61
|
+
# +date_time_format+:: For use with +date_time_ext+. This specifies the
|
62
|
+
# format of the date / time extension.
|
63
|
+
# +date_time+:: For use with +date_time_ext+. The +DateTime+ that will be
|
64
|
+
# used to construct the extension of the newly rotated file.
|
65
|
+
# If this option is not specified, the current date and time
|
66
|
+
# will be used.
|
67
|
+
# +pre_rotate+:: A +Proc+ to be executed before the rotation is started.
|
68
|
+
# +post_rotate+:: A +Proc+ to be executed after the rotation is finished,
|
69
|
+
# and before the newly rotated file is zipped.
|
70
|
+
# +gzip+:: Whether the newly rotated file should be gzipped. Possible
|
71
|
+
# values are: +true+, +false+.
|
72
|
+
#
|
73
|
+
# To see the default values of these options, see the DEFAULT_
|
74
|
+
# constants in LogRotate::Impl.
|
75
|
+
#
|
76
|
+
# ====Returns:
|
77
|
+
# A RotateInfoDateExtension or RotateInfoIntegerExtension instance
|
78
|
+
# depending whether the option +date_time_ext+ was specified. This instance
|
79
|
+
# contains information about the rotated files.
|
80
|
+
#
|
81
|
+
def self.rotate_file(file, options)
|
82
|
+
return Impl.send(:rotate_single_file, file, options)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|