simple_rotate 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.
- checksums.yaml +4 -4
- data/lib/simple_rotate.rb +5 -1126
- data/lib/simple_rotate/accesser.rb +13 -0
- data/lib/simple_rotate/const.rb +7 -0
- data/lib/simple_rotate/core.rb +306 -0
- data/lib/simple_rotate/internal/error.rb +56 -0
- data/lib/simple_rotate/internal/log_level.rb +14 -0
- data/lib/simple_rotate/internal/process_sync.rb +115 -0
- data/lib/simple_rotate/internal/process_sync_mixin.rb +48 -0
- data/lib/simple_rotate/internal/rotate_term.rb +7 -0
- data/lib/simple_rotate/internal/validator.rb +45 -0
- data/lib/simple_rotate/private.rb +436 -0
- metadata +24 -15
- data/LICENSE.txt +0 -22
- data/README.txt +0 -2
@@ -0,0 +1,306 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# load standard attachments
|
4
|
+
require "singleton"
|
5
|
+
require "monitor"
|
6
|
+
|
7
|
+
# load internal modules
|
8
|
+
require_relative "./internal/log_level"
|
9
|
+
require_relative "./internal/rotate_term"
|
10
|
+
require_relative "./internal/validator"
|
11
|
+
require_relative "./internal/error"
|
12
|
+
|
13
|
+
# load internal classes
|
14
|
+
require_relative "./internal/error"
|
15
|
+
require_relative "./internal/process_sync"
|
16
|
+
|
17
|
+
# extends myself
|
18
|
+
require_relative "./const"
|
19
|
+
require_relative "./accesser"
|
20
|
+
require_relative "./private"
|
21
|
+
|
22
|
+
class SimpleRotate
|
23
|
+
# mix-in
|
24
|
+
include Singleton
|
25
|
+
include MonitorMixin
|
26
|
+
include LogLevel
|
27
|
+
include RotateTerm
|
28
|
+
include Validator
|
29
|
+
|
30
|
+
# return method missing error
|
31
|
+
def method_missing(name, *argv)
|
32
|
+
SimpleRotate::Error.missing(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param string|symbol $file_name
|
36
|
+
# @param string|int $limit
|
37
|
+
# @param int $rotation
|
38
|
+
# @return self
|
39
|
+
def init(file_name=File.absolute_path($0+".log"), limit="100M", rotation=0)
|
40
|
+
@file_name = file_name
|
41
|
+
@limit = limit
|
42
|
+
@rotation = rotation
|
43
|
+
|
44
|
+
# load defaults
|
45
|
+
include_defaults
|
46
|
+
|
47
|
+
# validation
|
48
|
+
valid_file_name
|
49
|
+
valid_int("rotation", @rotation)
|
50
|
+
|
51
|
+
# stdout only
|
52
|
+
return self if @only_stdout
|
53
|
+
|
54
|
+
if rotate_by_term?
|
55
|
+
# term rotation
|
56
|
+
set_days_cnt_of_term
|
57
|
+
@limit_term = @limit
|
58
|
+
@rotate_by_term = true
|
59
|
+
|
60
|
+
else
|
61
|
+
# file_size rotation
|
62
|
+
@limit_size = trim_byte(@limit)
|
63
|
+
if @limit_size <= 0
|
64
|
+
SimpleRotate::Error.argv("limit", @limit)
|
65
|
+
end
|
66
|
+
@rotate_by_term = false
|
67
|
+
end
|
68
|
+
|
69
|
+
# for process sync
|
70
|
+
@psync = ProcessSync.new(self)
|
71
|
+
|
72
|
+
# open or generate the log file
|
73
|
+
synchronize do
|
74
|
+
@psync.lock
|
75
|
+
|
76
|
+
prepare_logf
|
77
|
+
|
78
|
+
@psync.unlock
|
79
|
+
end
|
80
|
+
|
81
|
+
# if block given, colse IO
|
82
|
+
if defined? yield
|
83
|
+
yield self
|
84
|
+
e
|
85
|
+
end
|
86
|
+
|
87
|
+
return self
|
88
|
+
end
|
89
|
+
|
90
|
+
# log message out to STDOUT when use SimpleRotate#w method
|
91
|
+
def with_stdout
|
92
|
+
@with_stdout = true
|
93
|
+
return nil
|
94
|
+
end
|
95
|
+
|
96
|
+
# enable compress
|
97
|
+
def compress
|
98
|
+
@compress = true
|
99
|
+
use_zlib
|
100
|
+
return nil
|
101
|
+
end
|
102
|
+
|
103
|
+
# define the compression level
|
104
|
+
# this method load 'zlib'
|
105
|
+
# this method enable compress flag
|
106
|
+
# @param int level - 0-9
|
107
|
+
# default is 6
|
108
|
+
def compress_level(level)
|
109
|
+
@compress_level = level
|
110
|
+
valid_int("compress_level", @compress_level)
|
111
|
+
compress
|
112
|
+
return nil
|
113
|
+
end
|
114
|
+
|
115
|
+
# @param string $log message write to log file
|
116
|
+
# @return string
|
117
|
+
def w(log)
|
118
|
+
if @file_name == nil
|
119
|
+
msg = "file_name is Nil Class! Please call #init method"
|
120
|
+
SimpleRotate::Error.throw_error(msg)
|
121
|
+
end
|
122
|
+
|
123
|
+
# don't out log message if Doesn't reach threshold
|
124
|
+
return nil if (!over_threshold?)
|
125
|
+
|
126
|
+
content = get_trimmed_log(log)
|
127
|
+
|
128
|
+
# return and end func, if only_stdout enable
|
129
|
+
if @only_stdout
|
130
|
+
puts content
|
131
|
+
return log
|
132
|
+
end
|
133
|
+
|
134
|
+
# write message to file
|
135
|
+
synchronize do
|
136
|
+
@psync.lock
|
137
|
+
|
138
|
+
sync_inode if !@no_sync_inode
|
139
|
+
@logf.puts(content)
|
140
|
+
@logf.flush if @enable_wflush
|
141
|
+
@logf.fsync if @enable_wflush
|
142
|
+
|
143
|
+
@psync.unlock
|
144
|
+
end
|
145
|
+
|
146
|
+
# dump log message STDOUT, if with_stdout enable
|
147
|
+
puts content if @with_stdout
|
148
|
+
|
149
|
+
# rotate if necessary
|
150
|
+
rotate_if if !@no_wcheck
|
151
|
+
|
152
|
+
return log
|
153
|
+
end
|
154
|
+
|
155
|
+
# disable call File#flush after #w method
|
156
|
+
def enable_wflush
|
157
|
+
@enable_wflush = true
|
158
|
+
return nil
|
159
|
+
end
|
160
|
+
|
161
|
+
# enable call File#flush after #w method
|
162
|
+
def disable_wflush
|
163
|
+
@enable_wflush = false
|
164
|
+
return nil
|
165
|
+
end
|
166
|
+
|
167
|
+
# close file
|
168
|
+
def e
|
169
|
+
return nil if logf_not_usable
|
170
|
+
|
171
|
+
synchronize do
|
172
|
+
@psync.lock
|
173
|
+
|
174
|
+
@logf.close
|
175
|
+
|
176
|
+
@psync.unlock
|
177
|
+
end
|
178
|
+
|
179
|
+
return true
|
180
|
+
end
|
181
|
+
|
182
|
+
# file reopen
|
183
|
+
def reopen
|
184
|
+
return nil if logf_not_usable
|
185
|
+
|
186
|
+
if !file_closed?
|
187
|
+
SimpleRotate::Error.warning("File is already open!")
|
188
|
+
return nil
|
189
|
+
end
|
190
|
+
|
191
|
+
openadd
|
192
|
+
return @logf
|
193
|
+
end
|
194
|
+
|
195
|
+
# force rotation
|
196
|
+
def flush
|
197
|
+
return nil if logf_not_usable
|
198
|
+
return nil if @rotate_by_term
|
199
|
+
rotation(:FLUSH)
|
200
|
+
return true
|
201
|
+
end
|
202
|
+
|
203
|
+
# don't check can to rotate at #w method
|
204
|
+
def no_wcheck
|
205
|
+
@no_wcheck = true
|
206
|
+
return nil;
|
207
|
+
end
|
208
|
+
|
209
|
+
# is log file open?
|
210
|
+
# @return nil|bool
|
211
|
+
def file_closed?
|
212
|
+
return nil if logf_not_usable
|
213
|
+
return @logf.closed?
|
214
|
+
end
|
215
|
+
|
216
|
+
# skip warning message
|
217
|
+
def silence
|
218
|
+
SimpleRotate::Error.silence
|
219
|
+
return nil;
|
220
|
+
end
|
221
|
+
|
222
|
+
# set log level FATAL
|
223
|
+
# @return self
|
224
|
+
def fatal
|
225
|
+
@log_level = 5
|
226
|
+
return self
|
227
|
+
end
|
228
|
+
|
229
|
+
# set log level ERROR
|
230
|
+
# @return self
|
231
|
+
def error
|
232
|
+
@log_level = 4
|
233
|
+
return self
|
234
|
+
end
|
235
|
+
|
236
|
+
# set log level WORN
|
237
|
+
# @return self
|
238
|
+
def warn
|
239
|
+
@log_level = 3
|
240
|
+
return self
|
241
|
+
end
|
242
|
+
|
243
|
+
# set log level INFO
|
244
|
+
# @return self
|
245
|
+
def info
|
246
|
+
@log_level = 2
|
247
|
+
return self
|
248
|
+
end
|
249
|
+
|
250
|
+
# set log level DEBUG
|
251
|
+
# @return self
|
252
|
+
def debug
|
253
|
+
@log_level = 1
|
254
|
+
return self
|
255
|
+
end
|
256
|
+
|
257
|
+
# synchronize processes
|
258
|
+
def psync(sleep_time=0.1)
|
259
|
+
@is_psync = true
|
260
|
+
@enable_wflush = true
|
261
|
+
@sleep_time = sleep_time
|
262
|
+
|
263
|
+
@psync = ProcessSync.new(self)
|
264
|
+
return nil
|
265
|
+
end
|
266
|
+
|
267
|
+
# reopen file necessary
|
268
|
+
# @return bool|nil
|
269
|
+
def sync_inode
|
270
|
+
return nil if logf_not_usable
|
271
|
+
|
272
|
+
cnt = 0
|
273
|
+
begin
|
274
|
+
# check i-node number
|
275
|
+
open_inode = @logf.stat.ino
|
276
|
+
logf_inode = File.stat(@file_name).ino
|
277
|
+
raise if open_inode != logf_inode
|
278
|
+
|
279
|
+
rescue
|
280
|
+
cnt += 1
|
281
|
+
sleep(0.1)
|
282
|
+
e
|
283
|
+
openadd
|
284
|
+
|
285
|
+
if cnt <= @sync_inode_limit
|
286
|
+
retry
|
287
|
+
else
|
288
|
+
SimpleRotate::Error.warning(%{inode number didn't not match, tried #{cnt} times!})
|
289
|
+
return false
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
return true
|
294
|
+
end
|
295
|
+
|
296
|
+
# disable #sync_inode
|
297
|
+
def no_sync_inode
|
298
|
+
@no_sync_inode = true
|
299
|
+
return nil
|
300
|
+
end
|
301
|
+
|
302
|
+
#--------------------
|
303
|
+
# method alias
|
304
|
+
#--------------------
|
305
|
+
alias_method :<<, :w
|
306
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class SimpleRotate
|
2
|
+
class Error < RuntimeError
|
3
|
+
msg = "Aborted the log files rotation,"
|
4
|
+
msg += " an unexpected error has occured."
|
5
|
+
ROTATION_FAILED = msg
|
6
|
+
|
7
|
+
@@silence = false
|
8
|
+
|
9
|
+
# skip warning message
|
10
|
+
def self.silence
|
11
|
+
@@silence = true
|
12
|
+
end
|
13
|
+
|
14
|
+
# argument error
|
15
|
+
def self.argv(param, argv)
|
16
|
+
msg = "'#{param}'='#{argv}' is invalid argument value!"
|
17
|
+
self.throw_error(msg)
|
18
|
+
end
|
19
|
+
|
20
|
+
# method missing
|
21
|
+
def self.missing(name)
|
22
|
+
msg = "undifined method 'SimpleRotate##{name}'"
|
23
|
+
self.throw_error(msg)
|
24
|
+
end
|
25
|
+
|
26
|
+
# file open error
|
27
|
+
def self.open(name)
|
28
|
+
msg = "Couldn't open a '#{name}'"
|
29
|
+
self.throw_error(msg)
|
30
|
+
end
|
31
|
+
|
32
|
+
# load error
|
33
|
+
def self.load(name)
|
34
|
+
msg = "Couldn't load a '#{name}'"
|
35
|
+
self.throw_error(msg)
|
36
|
+
end
|
37
|
+
|
38
|
+
# exist error
|
39
|
+
def self.exist(name, type)
|
40
|
+
msg = "Already exists this #{type} => '#{name}'"
|
41
|
+
self.throw_error(msg)
|
42
|
+
end
|
43
|
+
|
44
|
+
# warning - don't throw error
|
45
|
+
def self.warning(msg)
|
46
|
+
warn "[WARNING] #{msg} - (SimpleRotate::Error)" if !@@silence
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param msg string
|
50
|
+
def self.throw_error(msg)
|
51
|
+
exeption = self.new(msg)
|
52
|
+
warn exeption.message if !@@silence
|
53
|
+
raise SimpleRotate::Error
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class SimpleRotate
|
2
|
+
module LogLevel
|
3
|
+
LOG_LEVEL_5 = "FATAL"
|
4
|
+
LOG_LEVEL_4 = "ERROR"
|
5
|
+
LOG_LEVEL_3 = "WARN"
|
6
|
+
LOG_LEVEL_2 = "INFO"
|
7
|
+
LOG_LEVEL_1 = "DEBUG"
|
8
|
+
LEVEL_ID_FATAL = 5
|
9
|
+
LEVEL_ID_ERROR = 4
|
10
|
+
LEVEL_ID_WARN = 3
|
11
|
+
LEVEL_ID_INFO = 2
|
12
|
+
LEVEL_ID_DEBUG = 1
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require_relative "./process_sync_mixin"
|
2
|
+
|
3
|
+
class SimpleRotate
|
4
|
+
class ProcessSync
|
5
|
+
include ProcessSyncMixin
|
6
|
+
|
7
|
+
ProcessSyncMixin.instance_methods.each do |method_name|
|
8
|
+
method = instance_method(method_name)
|
9
|
+
define_method(method_name) do |*args|
|
10
|
+
# common execution
|
11
|
+
return nil if !@enable
|
12
|
+
# -------------------
|
13
|
+
|
14
|
+
method.bind(self).call(*args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(sr)
|
19
|
+
@sr = sr
|
20
|
+
@enable = sr.instance_variable_get(:@is_psync)
|
21
|
+
@file_name = sr.instance_variable_get(:@file_name)
|
22
|
+
|
23
|
+
# #init not called
|
24
|
+
return self if @file_name == nil
|
25
|
+
|
26
|
+
@logf = sr.instance_variable_get(:@logf)
|
27
|
+
@try_limit = 3
|
28
|
+
@@tempf_name = File.dirname(@file_name) + File::SEPARATOR + ".SimpleRotate_tempfile_#{File.basename($0)}"
|
29
|
+
|
30
|
+
create_tempfile if @enable && !@@scheduled_del_lockfile
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create the temp file for locking
|
34
|
+
private
|
35
|
+
def create_tempfile
|
36
|
+
if File.exist?(@@tempf_name)
|
37
|
+
open_temp_file
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
|
41
|
+
begin
|
42
|
+
@@tempf = File.open(@@tempf_name, File::RDWR|File::CREAT|File::EXCL)
|
43
|
+
|
44
|
+
rescue
|
45
|
+
SimpleRotate::Error.warning("Couldn't create temp file => #{@@tempf_name}")
|
46
|
+
|
47
|
+
ensure
|
48
|
+
set_delete_tempfile
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def tempf_exists?
|
54
|
+
return File.exist?(@@tempf_name)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Delete the lock file at the end of the script
|
58
|
+
private
|
59
|
+
def set_delete_tempfile
|
60
|
+
return true if @@scheduled_del_lockfile
|
61
|
+
|
62
|
+
if File.exists?(@@tempf_name)
|
63
|
+
# is it empty?
|
64
|
+
if File.size(@@tempf_name) == 0
|
65
|
+
delete_at_end
|
66
|
+
else
|
67
|
+
# it is not empty
|
68
|
+
msg = "File is not empty => #{@@tempf_name}#{$-0}"
|
69
|
+
msg += "Skip to delete temp file!"
|
70
|
+
SimpleRotate::Error.warning(msg)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@@scheduled_del_lockfile = true
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def delete_at_end
|
78
|
+
at_exit do
|
79
|
+
begin
|
80
|
+
File.delete(@@tempf_name)
|
81
|
+
rescue
|
82
|
+
#SimpleRotate::Error.warning("Couldn't delete temp file => #{@@tempf_name}")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def reopen_temp_file
|
89
|
+
close_temp_file
|
90
|
+
open_temp_file
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
def open_temp_file
|
95
|
+
if @@tempf.is_a?(IO) && @@tempf.closed? || !@@tempf.is_a?(IO)
|
96
|
+
begin
|
97
|
+
@@tempf = File.open(@@tempf_name, File::RDWR|File::CREAT|File::APPEND)
|
98
|
+
rescue
|
99
|
+
SimpleRotate::Error.warning("Couldn't open temp file => #{@@tempf_name}")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
def close_temp_file
|
106
|
+
if @@tempf.is_a?(IO) && !@@tempf.closed?
|
107
|
+
begin
|
108
|
+
@@tempf.close
|
109
|
+
rescue
|
110
|
+
SimpleRotate::Error.warning("Couldn't close temp file => #{@@tempf_name}")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|