rb.rotate 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +32 -0
- data/LICENSE.txt +20 -0
- data/README.md +81 -0
- data/Rakefile +41 -0
- data/VERSION +1 -0
- data/bin/rb.rotate +12 -0
- data/lib/rb.rotate.rb +30 -0
- data/lib/rb.rotate/configuration.rb +312 -0
- data/lib/rb.rotate/directory.rb +201 -0
- data/lib/rb.rotate/dispatcher.rb +112 -0
- data/lib/rb.rotate/file.rb +174 -0
- data/lib/rb.rotate/hook.rb +135 -0
- data/lib/rb.rotate/install/defaults.yaml.initial +25 -0
- data/lib/rb.rotate/install/rotate.yaml.initial +349 -0
- data/lib/rb.rotate/log.rb +80 -0
- data/lib/rb.rotate/mail.rb +40 -0
- data/lib/rb.rotate/reader.rb +94 -0
- data/lib/rb.rotate/state.rb +211 -0
- data/lib/rb.rotate/state/archive.rb +109 -0
- data/lib/rb.rotate/state/file.rb +139 -0
- data/lib/rb.rotate/storage.rb +208 -0
- data/lib/rb.rotate/storage/entry.rb +120 -0
- data/lib/rb.rotate/storage/item.rb +415 -0
- data/rb.rotate.gemspec +85 -0
- metadata +153 -0
@@ -0,0 +1,201 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "rb.rotate/configuration"
|
4
|
+
require "rb.rotate/file"
|
5
|
+
require "rb.rotate/reader"
|
6
|
+
require "rb.rotate/storage"
|
7
|
+
|
8
|
+
module RbRotate
|
9
|
+
|
10
|
+
##
|
11
|
+
# Represents one log directory.
|
12
|
+
#
|
13
|
+
|
14
|
+
class Directory
|
15
|
+
|
16
|
+
##
|
17
|
+
# Internal cache of the configuration.
|
18
|
+
#
|
19
|
+
|
20
|
+
@configuration
|
21
|
+
|
22
|
+
##
|
23
|
+
# Parent configuration object.
|
24
|
+
#
|
25
|
+
|
26
|
+
@parent
|
27
|
+
|
28
|
+
##
|
29
|
+
# Indicates, it isn't child directory of configured directory,
|
30
|
+
# but directly the configured directory.
|
31
|
+
#
|
32
|
+
|
33
|
+
@configured
|
34
|
+
|
35
|
+
##
|
36
|
+
# Holds directory identifier.
|
37
|
+
#
|
38
|
+
|
39
|
+
@identifier
|
40
|
+
attr_reader :identifier
|
41
|
+
|
42
|
+
##
|
43
|
+
# Holds directory path.
|
44
|
+
#
|
45
|
+
|
46
|
+
@path
|
47
|
+
attr_reader :path
|
48
|
+
|
49
|
+
##
|
50
|
+
# Constructor.
|
51
|
+
#
|
52
|
+
# Identifier is symbol so identifier in configuration file or
|
53
|
+
# string, so directory path.
|
54
|
+
#
|
55
|
+
|
56
|
+
def initialize(identifier, parent = nil)
|
57
|
+
@parent = parent
|
58
|
+
if identifier.kind_of? Symbol
|
59
|
+
@identifier = identifier
|
60
|
+
else
|
61
|
+
@path = identifier
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Returns the configuration instance.
|
67
|
+
#
|
68
|
+
|
69
|
+
def configuration
|
70
|
+
if @configuration.nil?
|
71
|
+
# If no identifier set, looks for the dir
|
72
|
+
# in configuration.
|
73
|
+
if @identifier.nil?
|
74
|
+
directory = Configuration::find_path(@path)
|
75
|
+
|
76
|
+
if not directory.nil?
|
77
|
+
@identifier = directory.identifier
|
78
|
+
@configured = true
|
79
|
+
elsif not @parent.nil?
|
80
|
+
@identifier = @parent.identifier
|
81
|
+
@configured = false
|
82
|
+
else
|
83
|
+
@identifier = :default
|
84
|
+
@configured = false
|
85
|
+
end
|
86
|
+
else
|
87
|
+
@configured = true
|
88
|
+
end
|
89
|
+
|
90
|
+
@configuration = DirectoryConfiguration::new(@identifier)
|
91
|
+
end
|
92
|
+
|
93
|
+
return @configuration
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Indicates, it isn't child directory of configured directory,
|
98
|
+
# but directly the configured directory.
|
99
|
+
#
|
100
|
+
|
101
|
+
def configured?
|
102
|
+
self.configuration
|
103
|
+
@configured
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Returns path to directory.
|
108
|
+
#
|
109
|
+
# So it get @path or directory from configuration if hasn't
|
110
|
+
# been sete.
|
111
|
+
#
|
112
|
+
|
113
|
+
def path
|
114
|
+
if not @path.nil?
|
115
|
+
@path
|
116
|
+
else
|
117
|
+
self.configuration.directory
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Returns relative path to parent (configured) directory.
|
123
|
+
#
|
124
|
+
|
125
|
+
def relative_path
|
126
|
+
if self.configured?
|
127
|
+
"."
|
128
|
+
else
|
129
|
+
self.path[(self.configured_ancestor.path.length + 1)..-1]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# Returns the "nearest" configured ancestor.
|
135
|
+
#
|
136
|
+
|
137
|
+
def configured_ancestor
|
138
|
+
if self.configured?
|
139
|
+
self
|
140
|
+
elsif not @parent.nil?
|
141
|
+
@parent.configured_ancestor
|
142
|
+
else
|
143
|
+
nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Traverses through all files in directory.
|
149
|
+
#
|
150
|
+
|
151
|
+
def each_file(&block)
|
152
|
+
Reader::read(self, :filter => :files, &block)
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# Traverses through all directories in directory.
|
157
|
+
#
|
158
|
+
|
159
|
+
def each_directory(&block)
|
160
|
+
Reader::read(self, :filter => :dirs, &block)
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Rotates.
|
165
|
+
#
|
166
|
+
|
167
|
+
def rotate!
|
168
|
+
# Cleans old or expired items
|
169
|
+
# self.storage.cleanup! (cleaned up globally by dispatcher call)
|
170
|
+
|
171
|
+
# Rotates
|
172
|
+
if self.configuration[:recursive]
|
173
|
+
self.each_directory do |directory|
|
174
|
+
directory.rotate!
|
175
|
+
end
|
176
|
+
end
|
177
|
+
self.each_file do |file|
|
178
|
+
file.rotate!
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# Indicates, directory entries should be compressed
|
184
|
+
# in archive.
|
185
|
+
#
|
186
|
+
|
187
|
+
def compressable?
|
188
|
+
not self.configuration[:compress].nil?
|
189
|
+
end
|
190
|
+
|
191
|
+
##
|
192
|
+
# Returns storage appropriate to directory.
|
193
|
+
#
|
194
|
+
|
195
|
+
def storage
|
196
|
+
Storage::get(self)
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module RbRotate
|
4
|
+
|
5
|
+
##
|
6
|
+
# Dispatches all operations.
|
7
|
+
#
|
8
|
+
|
9
|
+
class Dispatcher
|
10
|
+
|
11
|
+
##
|
12
|
+
# Runs the rotate session.
|
13
|
+
#
|
14
|
+
|
15
|
+
def run!
|
16
|
+
require "rb.rotate/configuration"
|
17
|
+
require "rb.rotate/state"
|
18
|
+
require "rb.rotate/storage"
|
19
|
+
require "rb.rotate/log"
|
20
|
+
|
21
|
+
# Reads configuration file
|
22
|
+
locator = ::File.dirname(::File.dirname(__FILE__)).dup << "/paths.conf"
|
23
|
+
if not ::File.exists? locator
|
24
|
+
STDERR.write("FATAL: rb.rotate unconfigured. Please, run 'rb.rotate install' or eventually create the " << locator << " file with path to configuration file. Aborted.\n")
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
path = ::File.read(locator)
|
29
|
+
path.strip!
|
30
|
+
|
31
|
+
Configuration::read(path)
|
32
|
+
log "Configuration file loaded."
|
33
|
+
|
34
|
+
# Process
|
35
|
+
log "Start of processing."
|
36
|
+
Configuration::each_directory do |directory|
|
37
|
+
begin
|
38
|
+
directory.rotate!
|
39
|
+
rescue Exception => e
|
40
|
+
log "Exception: " << e.to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Removes orhpans
|
45
|
+
log "Start of orphans removing."
|
46
|
+
Storage::remove_orphans!
|
47
|
+
|
48
|
+
# Saves state file
|
49
|
+
State::save!
|
50
|
+
log "New state saved."
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Installs the application configuration files.
|
55
|
+
#
|
56
|
+
|
57
|
+
def install!
|
58
|
+
require "sys/uname"
|
59
|
+
require "fileutils"
|
60
|
+
|
61
|
+
basedir = ::File.dirname(__FILE__)
|
62
|
+
|
63
|
+
# Loads and creates the configuration dir
|
64
|
+
case Sys::Uname.sysname.downcase.to_sym
|
65
|
+
when :freebsd
|
66
|
+
etc = "/usr/local/etc"
|
67
|
+
when :linux
|
68
|
+
etc = "/etc"
|
69
|
+
else
|
70
|
+
raise Exception::new("You are running on an unknown platform. It cannot be problem, but it's necessary define path to configuration file and define paths in configuration file.")
|
71
|
+
end
|
72
|
+
|
73
|
+
etc << "/rb.rotate"
|
74
|
+
FileUtils.mkdir_p(etc)
|
75
|
+
|
76
|
+
# Creates other important directories
|
77
|
+
FileUtils.mkdir_p("/var/log")
|
78
|
+
FileUtils.mkdir_p("/var/lib")
|
79
|
+
|
80
|
+
# Puts configuration files to configuration directory
|
81
|
+
source = basedir.dup << "/install"
|
82
|
+
replacements = { "%%configuration" => etc }
|
83
|
+
files = ["rotate.yaml", "defaults.yaml"]
|
84
|
+
|
85
|
+
files.each do |file|
|
86
|
+
body = ::File.read(source.dup << "/" << file << ".initial")
|
87
|
+
replacements.each_pair do |key, value|
|
88
|
+
body.gsub! key, value
|
89
|
+
end
|
90
|
+
::File.open(etc.dup << "/" << file, "w") do |io|
|
91
|
+
io.write(body)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Puts to library root path path to configuration directory
|
96
|
+
::File.open(basedir.dup << "/../paths.conf", "w") do |io|
|
97
|
+
io.write(etc.dup << "/rotate.yaml")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Prints out system name.
|
103
|
+
#
|
104
|
+
|
105
|
+
def sysname!
|
106
|
+
require "sys/uname"
|
107
|
+
puts Sys::Uname.sysname.downcase
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "rb.rotate/storage"
|
5
|
+
require "rb.rotate/directory"
|
6
|
+
require "rb.rotate/configuration"
|
7
|
+
|
8
|
+
module RbRotate
|
9
|
+
|
10
|
+
##
|
11
|
+
# Represents one log file.
|
12
|
+
#
|
13
|
+
|
14
|
+
class File
|
15
|
+
|
16
|
+
##
|
17
|
+
# Holds parent file directory.
|
18
|
+
#
|
19
|
+
|
20
|
+
@directory
|
21
|
+
|
22
|
+
##
|
23
|
+
# Indicates file path.
|
24
|
+
#
|
25
|
+
|
26
|
+
@path
|
27
|
+
attr_reader :path
|
28
|
+
|
29
|
+
##
|
30
|
+
# Holds state for the file.
|
31
|
+
#
|
32
|
+
|
33
|
+
@state
|
34
|
+
|
35
|
+
##
|
36
|
+
# Holds stat informations about the (original) file.
|
37
|
+
#
|
38
|
+
|
39
|
+
@stat
|
40
|
+
|
41
|
+
##
|
42
|
+
# Constructor.
|
43
|
+
#
|
44
|
+
|
45
|
+
def initialize(path, directory = nil)
|
46
|
+
@directory = directory
|
47
|
+
@path = path
|
48
|
+
@stat = ::File.stat(@path.to_s)
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Returns the file parent directory object.
|
53
|
+
#
|
54
|
+
|
55
|
+
def directory
|
56
|
+
if @directory.nil?
|
57
|
+
if not self.state.directory.nil?
|
58
|
+
@directory = Directory::new(self.state.directory)
|
59
|
+
else
|
60
|
+
@directory = Configuration::find_path(::File.dirname(@path.to_s))
|
61
|
+
end
|
62
|
+
|
63
|
+
if @directory.nil?
|
64
|
+
raise Exception::new("File from directory which isn't covered by rb.rotate found: " << @path.to_s << ".")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
return @directory
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Removes the file from medium.
|
73
|
+
#
|
74
|
+
|
75
|
+
def remove!
|
76
|
+
FileUtils.remove_file(@path)
|
77
|
+
return @path
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Creates new file.
|
82
|
+
#
|
83
|
+
|
84
|
+
def create!
|
85
|
+
::File.open(@path, "w").close()
|
86
|
+
|
87
|
+
# Sets access rights and ownership according to
|
88
|
+
# stat information
|
89
|
+
if not @stat.nil?
|
90
|
+
::FileUtils.chmod(@stat.mode, @path)
|
91
|
+
::FileUtils.chown(@stat.uid, @stat.gid, @path)
|
92
|
+
end
|
93
|
+
|
94
|
+
return @path
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Truncates file.
|
99
|
+
#
|
100
|
+
|
101
|
+
alias :"truncate!" :"create!"
|
102
|
+
|
103
|
+
##
|
104
|
+
# Rotates the file.
|
105
|
+
#
|
106
|
+
|
107
|
+
def rotate!
|
108
|
+
if self.archivable?
|
109
|
+
self.archive!
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Archives file.
|
115
|
+
#
|
116
|
+
|
117
|
+
def archive!
|
118
|
+
Storage::put(self)
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Indicates, file is suitable for immediate archiving.
|
123
|
+
#
|
124
|
+
|
125
|
+
def archivable?
|
126
|
+
self.too_big? or self.too_old?
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Indicates, file is bigger than is allowed.
|
131
|
+
#
|
132
|
+
|
133
|
+
def too_big?
|
134
|
+
::File.size(@path) > @directory.configuration[:"max size"].to_bytes
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# Indicates, file is too old.
|
139
|
+
#
|
140
|
+
|
141
|
+
def too_old?
|
142
|
+
if self.state.exists?
|
143
|
+
period = @directory.configuration[:period].to_seconds
|
144
|
+
result = Time::at(self.state.date + period) < Time::now
|
145
|
+
else
|
146
|
+
result = false
|
147
|
+
end
|
148
|
+
|
149
|
+
return result
|
150
|
+
end
|
151
|
+
|
152
|
+
##
|
153
|
+
# Returns state.
|
154
|
+
#
|
155
|
+
|
156
|
+
def state
|
157
|
+
if @state.nil?
|
158
|
+
@state = State::get.file(@path)
|
159
|
+
end
|
160
|
+
|
161
|
+
return @state
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Indicates file exists.
|
166
|
+
#
|
167
|
+
|
168
|
+
def exists?
|
169
|
+
::File.exists? @path.to_s
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|