logrotate 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +2 -0
- data/LICENSE +20 -0
- data/Manifest.txt +7 -0
- data/README.txt +162 -0
- data/Rakefile +10 -0
- data/lib/logrotate.rb +246 -0
- data/test/test_logrotate.rb +196 -0
- metadata +72 -0
data/History.txt
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Designing Patterns
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
= logrotate
|
2
|
+
* Project Page: http://rubyforge.org/projects/logrotate/
|
3
|
+
* Documentation: http://logrotate.rubyforge.org/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
This package is a library of methods that perform log rotation. The
|
8
|
+
log rotate methods allow the caller to specify options (via
|
9
|
+
parameters) such as how many rotated files to keep, what type of
|
10
|
+
extension to place on the rotated file (date or a simple count), and
|
11
|
+
whether to zip the rotated files. Live log files (currently being
|
12
|
+
written to by a live process) can be rotated as well. The post_rotate
|
13
|
+
option is useful in that context, as it can be used to send a HUP
|
14
|
+
signal to notify the live process to reopen its log file.
|
15
|
+
|
16
|
+
This package was inspired by the need to have a library version of the
|
17
|
+
unix logrotate tool. The unix logrotate tool requires the user to
|
18
|
+
specify options in a config file, and is usually invoked through cron.
|
19
|
+
|
20
|
+
== PROBLEMS:
|
21
|
+
|
22
|
+
None (known).
|
23
|
+
|
24
|
+
== SYNOPSIS:
|
25
|
+
Here are some sample invocations of the library.
|
26
|
+
|
27
|
+
Rotate a log file with simple extensions: .1, .2, etc, keep 10 rotated
|
28
|
+
files around (.1 .. .10), and gzip the rotated files (.1.gz, .2.gz, .. .10.gz):
|
29
|
+
======+examples/rotate_count.rb+:
|
30
|
+
#!/usr/bin/env ruby
|
31
|
+
|
32
|
+
require 'rubygems'
|
33
|
+
require 'logrotate'
|
34
|
+
|
35
|
+
options = {
|
36
|
+
:count => 10,
|
37
|
+
:gzip => true
|
38
|
+
}
|
39
|
+
|
40
|
+
LogRotate.rotate_file("/tmp/erwin.dat", options)
|
41
|
+
|
42
|
+
|
43
|
+
Rotate a set of log files with date extensions, and move the rotated
|
44
|
+
files to a specified directory:
|
45
|
+
======+examples/rotate_date.rb+:
|
46
|
+
#!/usr/bin/env ruby
|
47
|
+
|
48
|
+
require 'rubygems'
|
49
|
+
require 'logrotate'
|
50
|
+
|
51
|
+
output_directory = "/tmp/archives"
|
52
|
+
if (!File.directory?(output_directory)) then Dir.mkdir(output_directory) end
|
53
|
+
|
54
|
+
files = (1..64).map {|i| "/tmp/erwin_#{i}.dat" }
|
55
|
+
files.each do |file|
|
56
|
+
File.open(file, "w") do |fs|
|
57
|
+
fs.write(file)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
options = {
|
62
|
+
:date_time_ext => true,
|
63
|
+
:directory => output_directory
|
64
|
+
}
|
65
|
+
|
66
|
+
LogRotate.rotate_files(files, options)
|
67
|
+
|
68
|
+
|
69
|
+
Rotate a log file with date/time extensions.
|
70
|
+
======+examples/rotate_date_time.rb+:
|
71
|
+
#!/usr/bin/env/ruby
|
72
|
+
|
73
|
+
require 'rubygems'
|
74
|
+
require 'logrotate'
|
75
|
+
|
76
|
+
options = {
|
77
|
+
:date_time_ext => true,
|
78
|
+
:date_time_format => '%F_%T'
|
79
|
+
}
|
80
|
+
|
81
|
+
LogRotate.rotate_file("/tmp/erwin.dat", options)
|
82
|
+
|
83
|
+
|
84
|
+
Rotate a log file that is currently being written to by a live
|
85
|
+
process. After the file is rotated, notify the live process to reopen
|
86
|
+
its log file.
|
87
|
+
======+examples/rotate_live.rb+:
|
88
|
+
#!/usr/bin/env ruby
|
89
|
+
|
90
|
+
require 'rubygems'
|
91
|
+
require 'logrotate'
|
92
|
+
|
93
|
+
block = Proc.new() do
|
94
|
+
File.open("/tmp/hupper.pid") do |pid_stream|
|
95
|
+
pid = pid_stream.read().to_i()
|
96
|
+
Process.kill("HUP", pid)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
options = {
|
101
|
+
:gzip => true,
|
102
|
+
:count => 3,
|
103
|
+
:post_rotate => block
|
104
|
+
}
|
105
|
+
|
106
|
+
LogRotate.rotate_file("/tmp/hupper.dat", options)
|
107
|
+
|
108
|
+
|
109
|
+
== REQUIREMENTS:
|
110
|
+
|
111
|
+
Hoe is required but only for running the tests.
|
112
|
+
|
113
|
+
== INSTALL:
|
114
|
+
|
115
|
+
sudo gem install logrotate
|
116
|
+
|
117
|
+
== AUTHORS:
|
118
|
+
=== Designing Patterns
|
119
|
+
* Homepage: http://www.designingpatterns.com
|
120
|
+
* Blogs: http://blogs.designingpatterns.com
|
121
|
+
|
122
|
+
== SUPPORT
|
123
|
+
Please post questions, concerns, or requests for enhancement to the forums on
|
124
|
+
the project page. Alternatively, direct contact information for
|
125
|
+
Designing Patterns can be found on the project page for this gem.
|
126
|
+
|
127
|
+
== ENHANCEMENTS
|
128
|
+
Please feel free to contact us with any ideas; we will try our best to
|
129
|
+
enhance the software and respond to user requests. Of course, we are more
|
130
|
+
likely to work on a particular enhancement if we know that there are users
|
131
|
+
who want it. Designing Patterns provides contracting and consulting services,
|
132
|
+
so if there is an enhancement that *must* get done (and in a specified time
|
133
|
+
frame), please inquire about retaining our services!
|
134
|
+
|
135
|
+
== LICENSE:
|
136
|
+
The license text can be found in the +LICENSE+ file at the root of the
|
137
|
+
distribution.
|
138
|
+
|
139
|
+
This package is licensed with an MIT license:
|
140
|
+
|
141
|
+
Copyright (c) 2008 Designing Patterns
|
142
|
+
|
143
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
144
|
+
a copy of this software and associated documentation files (the
|
145
|
+
'Software'), to deal in the Software without restriction, including
|
146
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
147
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
148
|
+
permit persons to whom the Software is furnished to do so, subject to
|
149
|
+
the following conditions:
|
150
|
+
|
151
|
+
The above copyright notice and this permission notice shall be
|
152
|
+
included in all copies or substantial portions of the Software.
|
153
|
+
|
154
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
155
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
156
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
157
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
158
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
159
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
160
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
161
|
+
|
162
|
+
== SHARE AND ENJOY!
|
data/Rakefile
ADDED
data/lib/logrotate.rb
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
require 'zlib'
|
5
|
+
require 'date'
|
6
|
+
|
7
|
+
#
|
8
|
+
# This module provides a few methods for log rotation.
|
9
|
+
#
|
10
|
+
|
11
|
+
module LogRotate
|
12
|
+
|
13
|
+
DEFAULT_COUNT = 5
|
14
|
+
DEFAULT_GZIP = false
|
15
|
+
DEFAULT_DATE_TIME_EXTENSION = false
|
16
|
+
|
17
|
+
DEFAULT_DATE_TIME_FORMAT = '%F'
|
18
|
+
|
19
|
+
#
|
20
|
+
# ====Description: This method rotates the given files.
|
21
|
+
#
|
22
|
+
# ====Parameters:
|
23
|
+
# [file(s)]
|
24
|
+
# The files to be rotated.
|
25
|
+
# [options = {}]
|
26
|
+
# A list of optional arguments.
|
27
|
+
#
|
28
|
+
# See rotate_file for details.
|
29
|
+
#
|
30
|
+
def self.rotate_files(files, options)
|
31
|
+
files.each do |file|
|
32
|
+
rotate_single_file(file, options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# ====Description: This method rotates the given file(s).
|
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
|
+
# ====Parameters:
|
45
|
+
# [file]
|
46
|
+
# The file to be rotated.
|
47
|
+
# [options = {}]
|
48
|
+
# A list of optional arguments.
|
49
|
+
#
|
50
|
+
# The optional arguments are listed below:
|
51
|
+
# +count+:: The number of rotated files to be kept.
|
52
|
+
# +directory+:: The directory to store the newly rotated file. If this
|
53
|
+
# option is unspecified, the original file's directory
|
54
|
+
# will be used.
|
55
|
+
# +date_time_ext+:: Whether the extension on the rotated files should be
|
56
|
+
# a date. Possible values are: +true+, +false+.
|
57
|
+
# +date_time_format+:: For use with +date_time_ext+. This specifies the
|
58
|
+
# format of the date / time extension.
|
59
|
+
# +date_time+:: For use with +date_time_ext+. The +DateTime+ that will be
|
60
|
+
# used to construct the extension of the newly rotated file.
|
61
|
+
# If this option is not specified, the current date and time
|
62
|
+
# will be used.
|
63
|
+
# +pre_rotate+:: A +Proc+ to be executed before the rotation is started.
|
64
|
+
# +post_rotate+:: A +Proc+ to be executed after the rotation is finished,
|
65
|
+
# and before the newly rotated file is zipped.
|
66
|
+
# +gzip+:: Whether the newly rotated file should be gzipped. Possible
|
67
|
+
# values are: +true+, +false+.
|
68
|
+
#
|
69
|
+
def self.rotate_file(file, options)
|
70
|
+
rotate_single_file(file, options)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.rotate_single_file(file, options = {})
|
74
|
+
gzip = options[:gzip] ? options[:gzip] : DEFAULT_GZIP
|
75
|
+
|
76
|
+
# error checking
|
77
|
+
if (!File.exist?(file)) then raise "File does not exist: #{file}." end
|
78
|
+
if (!File.readable?(file)) then raise "File is not readable: #{file}." end
|
79
|
+
|
80
|
+
if (options[:pre_rotate]) then options[:pre_rotate].call() end
|
81
|
+
|
82
|
+
new_backup = ""
|
83
|
+
if (options[:date_time_ext])
|
84
|
+
new_backup = rotate_file_date_extension(file, options)
|
85
|
+
else
|
86
|
+
new_backup = rotate_file_integer_extension(file, options)
|
87
|
+
end
|
88
|
+
|
89
|
+
if (options[:post_rotate]) then options[:post_rotate].call() end
|
90
|
+
|
91
|
+
if (gzip) then gzip_file(new_backup) end
|
92
|
+
end
|
93
|
+
private_class_method(:rotate_single_file)
|
94
|
+
|
95
|
+
def self.rotate_file_date_extension(file, options)
|
96
|
+
count = options[:count] ? options[:count] : DEFAULT_COUNT
|
97
|
+
|
98
|
+
now = options[:date_time] ? options[:date_time] : DateTime.now()
|
99
|
+
|
100
|
+
date_time_format = options[:date_time_format] ? options[:date_time_format] : DEFAULT_DATE_TIME_FORMAT
|
101
|
+
|
102
|
+
# Get a list of the rotated files.
|
103
|
+
(source_directory, base_name) = File.split(file)
|
104
|
+
directory = options[:directory] ? options[:directory] : source_directory
|
105
|
+
|
106
|
+
rotated_files = Dir.entries(directory).map do |entry|
|
107
|
+
rslt = nil
|
108
|
+
|
109
|
+
# Validate the rotated file. Since the caller is allowed to
|
110
|
+
# specify the date/time format, here we do not have a regular
|
111
|
+
# expression to match the date/time portion of the rotated
|
112
|
+
# file's extension. As a result, some foreign files can sneak
|
113
|
+
# into our list of rotated files (eg. base_file.GARBAGE.gz). Thus,
|
114
|
+
# the rotated file is validated below, and if invalid, it is
|
115
|
+
# skipped / left alone / not considered in the rotation.
|
116
|
+
|
117
|
+
if (entry.match("^#{base_name}\..+(\.gz)?$"))
|
118
|
+
|
119
|
+
# Validation #1: convert the date/time portion of the
|
120
|
+
# extension to a DateTime object and skip the file if an
|
121
|
+
# exception is thrown.
|
122
|
+
begin
|
123
|
+
match_data = entry.match("\.([^.]+)(\.gz)?$")
|
124
|
+
date_time = DateTime.strptime(match_data[1], date_time_format)
|
125
|
+
gz = match_data[2] ? match_data[2] : ""
|
126
|
+
|
127
|
+
# Validation #2: convert the date/time portion of the
|
128
|
+
# extension to a DateTime object and back to a string again.
|
129
|
+
# Then reconstruct the rotated file name with this string,
|
130
|
+
# and ensure the reconstructed rotated file matches the
|
131
|
+
# original. This will take into account cases where
|
132
|
+
# strptime succeeded but had additional garbage characters
|
133
|
+
# present.
|
134
|
+
# eg: date_time_format = '%F', file = 'mysql_backup.2008-08-04.BACK.gz'
|
135
|
+
reconstructed_file = "#{base_name}.#{date_time.strftime(date_time_format)}#{gz}"
|
136
|
+
if (entry == reconstructed_file)
|
137
|
+
rslt = { :date_time => date_time, :file => entry }
|
138
|
+
end
|
139
|
+
rescue => e
|
140
|
+
end
|
141
|
+
|
142
|
+
rslt
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
rotated_files = rotated_files.select { |rotated_file| rotated_file }
|
147
|
+
|
148
|
+
# Sort files by date (newer to older).
|
149
|
+
rotated_files.sort! { |left, right| right[:date_time] <=> left[:date_time] }
|
150
|
+
|
151
|
+
# Remove the oldest files so that there are count rotated files remaining.
|
152
|
+
|
153
|
+
# Determine how many old files to be removed.
|
154
|
+
new_backup_base_name = base_name + "." + now.strftime(date_time_format)
|
155
|
+
|
156
|
+
# Add 1 to account for the additional rotated file that will be
|
157
|
+
# created after the original file passed in is rotated.
|
158
|
+
num_old_files = rotated_files.length() - count + 1
|
159
|
+
|
160
|
+
# There is a chance that the newly rotated file will have the same
|
161
|
+
# name as one of the already existing rotated files (eg. if the
|
162
|
+
# extension is simply the date, and the job is ran a second time
|
163
|
+
# in the same day). In this case, there will be 1 less rotated
|
164
|
+
# file (and thus 1 less to delete).
|
165
|
+
if (rotated_files.find {|rotated_file| rotated_file[:file] == new_backup_base_name})
|
166
|
+
num_old_files -= 1
|
167
|
+
end
|
168
|
+
|
169
|
+
if (num_old_files > 0)
|
170
|
+
old_files = rotated_files.slice!(-num_old_files, num_old_files)
|
171
|
+
File.unlink(*old_files.map {|old_file| File.join(directory, old_file[:file]) })
|
172
|
+
end
|
173
|
+
|
174
|
+
# Rename the original file.
|
175
|
+
new_backup = File.join(directory, new_backup_base_name)
|
176
|
+
File.rename(file, new_backup)
|
177
|
+
|
178
|
+
return new_backup
|
179
|
+
end
|
180
|
+
private_class_method(:rotate_file_date_extension)
|
181
|
+
|
182
|
+
def self.rotate_file_integer_extension(file, options)
|
183
|
+
count = options[:count] ? options[:count] : DEFAULT_COUNT
|
184
|
+
|
185
|
+
# Get a list of the backed up files.
|
186
|
+
(source_directory, base_name) = File.split(file)
|
187
|
+
directory = options[:directory] ? options[:directory] : source_directory
|
188
|
+
|
189
|
+
rotated_files = Dir.entries(directory).select do |entry|
|
190
|
+
entry.match("#{base_name}\.[0-9]+(\.gz)?$")
|
191
|
+
end
|
192
|
+
|
193
|
+
rotated_files.map! do |rotated_file|
|
194
|
+
index = rotated_file.match("\.([0-9]+)(\.gz)?$")[1].to_i()
|
195
|
+
{ :index => index, :file => rotated_file }
|
196
|
+
end
|
197
|
+
|
198
|
+
# Delete old files.
|
199
|
+
old_files = rotated_files.select { |rotated_file| rotated_file[:index] >= count }
|
200
|
+
File.unlink(*old_files.map {|old_file| File.join(directory, old_file[:file]) })
|
201
|
+
|
202
|
+
rotated_files = rotated_files - old_files
|
203
|
+
|
204
|
+
# Sort files such that the back up count is descending (eg. [hello.txt.5, hello.txt.4, ..]).
|
205
|
+
rotated_files.sort! { |left, right| right[:index] <=> left[:index] }
|
206
|
+
|
207
|
+
# Rotate - Increment the back up index on each file.
|
208
|
+
rotated_files.each do |rotated_file|
|
209
|
+
|
210
|
+
new_index = rotated_file[:index] + 1
|
211
|
+
new_file = rotated_file[:file].sub(/(^.*\.)(#{rotated_file[:index]})(.gz)?$/,
|
212
|
+
"\\1#{new_index}\\3")
|
213
|
+
File.rename(File.join(directory, rotated_file[:file]),
|
214
|
+
File.join(directory, new_file))
|
215
|
+
end
|
216
|
+
|
217
|
+
# Rename the original file.
|
218
|
+
new_backup = File.join(directory, base_name + ".1")
|
219
|
+
File.rename(file, new_backup)
|
220
|
+
|
221
|
+
return new_backup
|
222
|
+
end
|
223
|
+
private_class_method(:rotate_file_integer_extension)
|
224
|
+
|
225
|
+
def self.gzip_file(file)
|
226
|
+
# Zip the original file if necessary.
|
227
|
+
gzip_file = file + ".gz"
|
228
|
+
|
229
|
+
begin
|
230
|
+
File.open(file) do |file_stream|
|
231
|
+
Zlib::GzipWriter.open(gzip_file) do |gz|
|
232
|
+
while (buffer = file_stream.read(1024))
|
233
|
+
gz.write(buffer)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
rescue => error
|
238
|
+
raise "Unable to zip file: #{file}. Error: #{error}\n"
|
239
|
+
end
|
240
|
+
|
241
|
+
File.unlink(file)
|
242
|
+
end
|
243
|
+
private_class_method(:gzip_file)
|
244
|
+
|
245
|
+
end
|
246
|
+
|
@@ -0,0 +1,196 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'logrotate'
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
|
7
|
+
class LogRotateTest < Test::Unit::TestCase
|
8
|
+
TEMP_DIR = "./temp"
|
9
|
+
|
10
|
+
def remove_directory(directory)
|
11
|
+
if (!File.directory?(directory)) then return end
|
12
|
+
|
13
|
+
entries = Dir.entries(directory).select {|entry| (entry != ".") && (entry != "..") }
|
14
|
+
entries.map! { |entry| File.join(directory, entry) }
|
15
|
+
|
16
|
+
entries.each do |entry|
|
17
|
+
if File.directory?(entry)
|
18
|
+
remove_directory(entry)
|
19
|
+
else
|
20
|
+
File.delete(entry)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Dir.delete(directory)
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove_temp_directory
|
28
|
+
remove_directory(TEMP_DIR)
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup
|
32
|
+
remove_temp_directory()
|
33
|
+
end
|
34
|
+
|
35
|
+
def teardown
|
36
|
+
remove_temp_directory()
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_rotate_integer_extension()
|
40
|
+
rotate_integer_extension(3, 10, gzip = false)
|
41
|
+
rotate_integer_extension(3, 10, gzip = true)
|
42
|
+
|
43
|
+
output_dir = "#{TEMP_DIR}/archives"
|
44
|
+
rotate_integer_extension(3, 10, gzip = false, output_dir)
|
45
|
+
rotate_integer_extension(3, 10, gzip = true, output_dir)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_rotate_date_extension()
|
49
|
+
date_time_format = "%F_%T"
|
50
|
+
|
51
|
+
output_dir = nil
|
52
|
+
rotate_date_extension(3, 10, gzip = false)
|
53
|
+
rotate_date_extension(3, 10, gzip = true)
|
54
|
+
rotate_date_extension(3, 10, gzip = true, output_dir, date_time_format)
|
55
|
+
|
56
|
+
output_dir = "#{TEMP_DIR}/archives"
|
57
|
+
rotate_date_extension(3, 10, gzip = false, output_dir)
|
58
|
+
rotate_date_extension(3, 10, gzip = true, output_dir)
|
59
|
+
rotate_date_extension(3, 10, gzip = true, output_dir, date_time_format)
|
60
|
+
end
|
61
|
+
|
62
|
+
def rotate_integer_extension(count, rotations, gzip, output_dir = nil)
|
63
|
+
source_dir = "#{TEMP_DIR}"
|
64
|
+
base_name = "file.dat"
|
65
|
+
file = File.join(source_dir, base_name)
|
66
|
+
Dir.mkdir(source_dir)
|
67
|
+
|
68
|
+
is_output_dir = false
|
69
|
+
if (output_dir) then
|
70
|
+
Dir.mkdir(output_dir)
|
71
|
+
is_output_dir = true
|
72
|
+
else
|
73
|
+
output_dir = source_dir
|
74
|
+
end
|
75
|
+
|
76
|
+
manipulate_garbage_files(output_dir, base_name, :insert)
|
77
|
+
|
78
|
+
#rotate the file
|
79
|
+
rotations.downto(1) do |i|
|
80
|
+
File.open(file, "w") do |file_stream|
|
81
|
+
file_stream.write("#{i}")
|
82
|
+
end
|
83
|
+
|
84
|
+
options = {:count => count}
|
85
|
+
if (gzip) then options[:gzip] = true end
|
86
|
+
if (is_output_dir) then options[:directory] = output_dir end
|
87
|
+
|
88
|
+
LogRotate.rotate_files(file, options)
|
89
|
+
end
|
90
|
+
|
91
|
+
manipulate_garbage_files(output_dir, base_name, :delete)
|
92
|
+
|
93
|
+
#verify there are count rotated files.
|
94
|
+
entries = Dir.glob("#{output_dir}/*").map {|entry| File.split(entry)[1]}
|
95
|
+
assert_equal(entries.length, count)
|
96
|
+
|
97
|
+
#verify the contents
|
98
|
+
expected = (1..count).map { |i| "#{base_name}.#{i}" + (gzip ? ".gz" : "") }
|
99
|
+
assert_equal(entries.sort, expected)
|
100
|
+
|
101
|
+
(1..count).each do |i|
|
102
|
+
rotated_file = "#{output_dir}/#{base_name}.#{i}" + (gzip ? ".gz" : "")
|
103
|
+
if (gzip)
|
104
|
+
Zlib::GzipReader.open("#{rotated_file}") do |gz|
|
105
|
+
assert_equal(gz.read().to_i(), i)
|
106
|
+
end
|
107
|
+
else
|
108
|
+
assert_equal(IO.read(rotated_file).to_i(), i)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
remove_temp_directory()
|
113
|
+
end
|
114
|
+
|
115
|
+
def manipulate_garbage_files(dir, base_name, operation)
|
116
|
+
bogus_extensions = [ ".back", ".back123", ".2008-10-04_00:00:00foo", ".123x", "." ]
|
117
|
+
|
118
|
+
bogus_extensions.each do |bogus_extension|
|
119
|
+
bogus_file_1 = File.join(dir, base_name + bogus_extension)
|
120
|
+
bogus_file_2 = bogus_file_1 + ".gz"
|
121
|
+
|
122
|
+
if (operation == :insert)
|
123
|
+
File.open(bogus_file_1, "w") { |file_stream| file_stream.print "garbage" }
|
124
|
+
File.open(bogus_file_2, "w") { |file_stream| file_stream.print "garbage" }
|
125
|
+
elsif (operation == :delete)
|
126
|
+
File.delete(bogus_file_1, bogus_file_2)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def rotate_date_extension(count, rotations, gzip,
|
132
|
+
output_dir = nil,
|
133
|
+
date_time_format = LogRotate::DEFAULT_DATE_TIME_FORMAT)
|
134
|
+
|
135
|
+
source_dir = "#{TEMP_DIR}"
|
136
|
+
base_name = "file.dat"
|
137
|
+
file = File.join(source_dir, base_name)
|
138
|
+
Dir.mkdir(source_dir)
|
139
|
+
|
140
|
+
is_output_dir = false
|
141
|
+
if (output_dir) then
|
142
|
+
Dir.mkdir(output_dir)
|
143
|
+
is_output_dir = true
|
144
|
+
else
|
145
|
+
output_dir = source_dir
|
146
|
+
end
|
147
|
+
|
148
|
+
manipulate_garbage_files(output_dir, base_name, :insert)
|
149
|
+
|
150
|
+
#rotate the file
|
151
|
+
now = DateTime.now
|
152
|
+
date_times = (0..(rotations - 1)).map { |i| now + i }
|
153
|
+
|
154
|
+
date_times.each do |date_time|
|
155
|
+
File.open(file, "w") do |file_stream|
|
156
|
+
file_stream.write(date_time.strftime(date_time_format))
|
157
|
+
end
|
158
|
+
|
159
|
+
options = { :date_time_ext => true, :date_time => date_time, :count => count }
|
160
|
+
if gzip then options[:gzip] = true end
|
161
|
+
if (is_output_dir) then options[:directory] = output_dir end
|
162
|
+
if date_time_format then options[:date_time_format] = date_time_format end
|
163
|
+
|
164
|
+
LogRotate.rotate_files(file, options)
|
165
|
+
end
|
166
|
+
|
167
|
+
#verify there are count rotated files.
|
168
|
+
manipulate_garbage_files(output_dir, base_name, :delete)
|
169
|
+
|
170
|
+
entries = Dir.glob("#{output_dir}/*").map {|entry| File.split(entry)[1]}
|
171
|
+
assert_equal(count, entries.length)
|
172
|
+
|
173
|
+
#verify the contents
|
174
|
+
date_times.slice!(0, date_times.length - count)
|
175
|
+
expected = date_times.map do |date_time|
|
176
|
+
"#{base_name}.#{date_time.strftime(date_time_format)}" + (gzip ? ".gz" : "")
|
177
|
+
end
|
178
|
+
|
179
|
+
assert_equal(expected, entries.sort)
|
180
|
+
|
181
|
+
date_times.each do |date_time|
|
182
|
+
rotated_file = "#{output_dir}/#{base_name}.#{date_time.strftime(date_time_format)}" + (gzip ? ".gz" : "")
|
183
|
+
|
184
|
+
if (gzip)
|
185
|
+
Zlib::GzipReader.open(rotated_file) do |gz|
|
186
|
+
assert_equal(gz.read(), "#{date_time.strftime(date_time_format)}")
|
187
|
+
end
|
188
|
+
else
|
189
|
+
assert_equal(IO.read("#{rotated_file}"), "#{date_time.strftime(date_time_format)}")
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
remove_temp_directory()
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: logrotate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- DesigningPatterns
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-07-17 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.7.0
|
24
|
+
version:
|
25
|
+
description: This package is a library of methods that perform log rotation. The log rotate methods allow the caller to specify options (via parameters) such as how many rotated files to keep, what type of extension to place on the rotated file (date or a simple count), and whether to zip the rotated files. Live log files (currently being written to by a live process) can be rotated as well. The post_rotate option is useful in that context, as it can be used to send a HUP signal to notify the live process to reopen its log file. This package was inspired by the need to have a library version of the unix logrotate tool. The unix logrotate tool requires the user to specify options in a config file, and is usually invoked through cron.
|
26
|
+
email:
|
27
|
+
- technical.inquiries@designingpatterns.com
|
28
|
+
executables: []
|
29
|
+
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files:
|
33
|
+
- Manifest.txt
|
34
|
+
- History.txt
|
35
|
+
- README.txt
|
36
|
+
files:
|
37
|
+
- Manifest.txt
|
38
|
+
- LICENSE
|
39
|
+
- History.txt
|
40
|
+
- README.txt
|
41
|
+
- Rakefile
|
42
|
+
- lib/logrotate.rb
|
43
|
+
- test/test_logrotate.rb
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: "Project Page: http://rubyforge.org/projects/logrotate/"
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options:
|
48
|
+
- --main
|
49
|
+
- README.txt
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project: logrotate
|
67
|
+
rubygems_version: 1.2.0
|
68
|
+
signing_key:
|
69
|
+
specification_version: 2
|
70
|
+
summary: This package is a library of methods that perform log rotation
|
71
|
+
test_files:
|
72
|
+
- test/test_logrotate.rb
|