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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 481428ce2f0316a88fabc15531a03bb722641a86
|
4
|
+
data.tar.gz: 482f5c5d2154f63ef017817941e049ed7078dff3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50bdabad14095319c3caaeb70df3c5ed79b6a76d637ba7a44863fd644e018624c4afb43aa5f633635a86b5250a54b53ce7cb80d29c8663d4394dd50270f7207e
|
7
|
+
data.tar.gz: 80d19f35402ae0aabc0e099986b2dda5f68efe8b7f3b5f0228628a4c16229ecf24238e743f8068c4531324836270503b6c6e5beed3798a15a3630579cc66963e
|
data/lib/simple_rotate.rb
CHANGED
@@ -1,1127 +1,6 @@
|
|
1
|
-
# SimpleRotate
|
2
|
-
# @autor: Kazuya Hotta (nyanko)
|
1
|
+
# SimpleRotate
|
3
2
|
#
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
VERSION = "1.0.0"
|
9
|
-
include Singleton
|
10
|
-
include MonitorMixin
|
11
|
-
|
12
|
-
#----------------
|
13
|
-
# modlues
|
14
|
-
#----------------
|
15
|
-
module LogLevel
|
16
|
-
LOG_LEVEL_5 = "FATAL"
|
17
|
-
LOG_LEVEL_4 = "ERROR"
|
18
|
-
LOG_LEVEL_3 = "WARN"
|
19
|
-
LOG_LEVEL_2 = "INFO"
|
20
|
-
LOG_LEVEL_1 = "DEBUG"
|
21
|
-
LEVEL_ID_FATAL = 5
|
22
|
-
LEVEL_ID_ERROR = 4
|
23
|
-
LEVEL_ID_WARN = 3
|
24
|
-
LEVEL_ID_INFO = 2
|
25
|
-
LEVEL_ID_DEBUG = 1
|
26
|
-
end
|
27
|
-
include LogLevel
|
28
|
-
|
29
|
-
module RotateTerm
|
30
|
-
TERM_DAILY = 1
|
31
|
-
TERM_WEEKLY = 7
|
32
|
-
TERM_MONTHLY = 30
|
33
|
-
end
|
34
|
-
include RotateTerm
|
35
|
-
|
36
|
-
module Validator
|
37
|
-
def valid_file_name
|
38
|
-
# stdout only
|
39
|
-
if @file_name.is_a?(Symbol) && @file_name == :STDOUT
|
40
|
-
@only_stdout = true
|
41
|
-
return true
|
42
|
-
end
|
43
|
-
|
44
|
-
# not string
|
45
|
-
if !@file_name.is_a?(String)
|
46
|
-
SimpleRotate::Error.argv("file_name", @file_name)
|
47
|
-
end
|
48
|
-
|
49
|
-
# directory?
|
50
|
-
if File.directory?(@file_name)
|
51
|
-
msg = "ERROR => #{@file_name} is a Directory!"
|
52
|
-
SimpleRotate::Error.warning(msg)
|
53
|
-
SimpleRotate::Error.argv("file_name", @file_name)
|
54
|
-
end
|
55
|
-
|
56
|
-
return true
|
57
|
-
end
|
58
|
-
|
59
|
-
def valid_int(param, argv)
|
60
|
-
if !argv.is_a?(Integer)
|
61
|
-
SimpleRotate::Error.argv(param, argv)
|
62
|
-
|
63
|
-
elsif argv < 0
|
64
|
-
msg = %{You can't specify the negative value!}
|
65
|
-
SimpleRotate::Error.warning(msg)
|
66
|
-
SimpleRotate::Error.argv(param, argv)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def valid_bool(param, argv)
|
71
|
-
argv = true if argv == 1
|
72
|
-
argv = false if argv == 0
|
73
|
-
if !(argv.instance_of?(TrueClass) || argv.instance_of?(FalseClass))
|
74
|
-
SimpleRotate::Error.argv(param, argv)
|
75
|
-
end
|
76
|
-
return true
|
77
|
-
end
|
78
|
-
end
|
79
|
-
include Validator
|
80
|
-
|
81
|
-
#----------------
|
82
|
-
# classes
|
83
|
-
#----------------
|
84
|
-
#
|
85
|
-
# error class for SimpleRotate
|
86
|
-
#
|
87
|
-
class Error < RuntimeError
|
88
|
-
msg = "aborted the log file rotation process,"
|
89
|
-
msg += " because an unexpected error has occured."
|
90
|
-
ROTATION_FAILED = msg
|
91
|
-
|
92
|
-
@@silence = false
|
93
|
-
|
94
|
-
#
|
95
|
-
# skip warning message
|
96
|
-
#
|
97
|
-
def self.silence
|
98
|
-
@@silence = true
|
99
|
-
end
|
100
|
-
|
101
|
-
#
|
102
|
-
# argument error
|
103
|
-
#
|
104
|
-
def self.argv(param, argv)
|
105
|
-
msg = "'#{param}'='#{argv}' is invalid argument value!"
|
106
|
-
self.throw_error(msg)
|
107
|
-
end
|
108
|
-
|
109
|
-
#
|
110
|
-
# method missing
|
111
|
-
#
|
112
|
-
def self.missing(name)
|
113
|
-
msg = "undifined method 'SimpleRotate##{name}'"
|
114
|
-
self.throw_error(msg)
|
115
|
-
end
|
116
|
-
|
117
|
-
#
|
118
|
-
# file open error
|
119
|
-
#
|
120
|
-
def self.open(name)
|
121
|
-
msg = "Couldn't open a '#{name}'"
|
122
|
-
self.throw_error(msg)
|
123
|
-
end
|
124
|
-
|
125
|
-
#
|
126
|
-
# load error
|
127
|
-
#
|
128
|
-
def self.load(name)
|
129
|
-
msg = "Couldn't load a '#{name}'"
|
130
|
-
self.throw_error(msg)
|
131
|
-
end
|
132
|
-
|
133
|
-
#
|
134
|
-
# exist error
|
135
|
-
#
|
136
|
-
def self.exist(name, type)
|
137
|
-
msg = "Already exists this #{type} => '#{name}'"
|
138
|
-
self.throw_error(msg)
|
139
|
-
end
|
140
|
-
|
141
|
-
#
|
142
|
-
# warning - don't throw error
|
143
|
-
#
|
144
|
-
def self.warning(msg)
|
145
|
-
warn "[WARNING] #{msg} - (SimpleRotate::Error)" if !@@silence
|
146
|
-
end
|
147
|
-
|
148
|
-
# @param msg string
|
149
|
-
#
|
150
|
-
def self.throw_error(msg)
|
151
|
-
exeption = self.new(msg)
|
152
|
-
warn exeption.message if !@@silence
|
153
|
-
raise SimpleRotate::Error
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
#
|
158
|
-
# The module for the process safe
|
159
|
-
# This module will be included by ProcessSync class
|
160
|
-
#
|
161
|
-
module ProcessSyncMixin
|
162
|
-
@@scheduled_del_lockfile = false
|
163
|
-
@@tempf_name = nil
|
164
|
-
@@tempf = nil
|
165
|
-
|
166
|
-
def locked?
|
167
|
-
return false if !tempf_exists?
|
168
|
-
|
169
|
-
# return false, if locked by another
|
170
|
-
status = @@tempf.flock(File::LOCK_EX | File::LOCK_NB)
|
171
|
-
|
172
|
-
return status == false
|
173
|
-
end
|
174
|
-
|
175
|
-
# lock the temp file
|
176
|
-
def lock
|
177
|
-
create_tempfile if !tempf_exists?
|
178
|
-
|
179
|
-
# if don't reopen temp file, can't lock...
|
180
|
-
reopen_temp_file
|
181
|
-
|
182
|
-
cnt = 0
|
183
|
-
begin
|
184
|
-
@@tempf.flock(File::LOCK_EX)
|
185
|
-
|
186
|
-
rescue
|
187
|
-
cnt += 1
|
188
|
-
if (cnt <= @try_limit)
|
189
|
-
sleep(0.5)
|
190
|
-
create_tempfile if !tempf_exists?
|
191
|
-
retry
|
192
|
-
else
|
193
|
-
SimpleRotate::Error.warning("It was not possible to lock (tried 3times) => #{@@tempf_name}")
|
194
|
-
return false
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
# unlock the temp file
|
200
|
-
def unlock
|
201
|
-
return nil if !tempf_exists?
|
202
|
-
|
203
|
-
begin
|
204
|
-
@@tempf.flock(File::LOCK_UN)
|
205
|
-
rescue
|
206
|
-
SimpleRotate::Error.warning("It was not possible to unlock => #{@@tempf_name}")
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
#
|
212
|
-
# The classes for process-safe
|
213
|
-
#
|
214
|
-
class ProcessSync
|
215
|
-
include ProcessSyncMixin
|
216
|
-
|
217
|
-
ProcessSyncMixin.instance_methods.each do |method_name|
|
218
|
-
method = instance_method(method_name)
|
219
|
-
define_method(method_name) do |*args|
|
220
|
-
###################
|
221
|
-
# common execution
|
222
|
-
###################
|
223
|
-
# Processing to be performed at the beginning of the method
|
224
|
-
return nil if !@enable
|
225
|
-
###################
|
226
|
-
method.bind(self).call(*args)
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
def initialize(sr)
|
231
|
-
@sr = sr
|
232
|
-
@enable = sr.instance_variable_get(:@psafe_mode)
|
233
|
-
@file_name = sr.instance_variable_get(:@file_name)
|
234
|
-
|
235
|
-
# #init not called
|
236
|
-
return self if @file_name == nil
|
237
|
-
|
238
|
-
@logf = sr.instance_variable_get(:@logf)
|
239
|
-
@try_limit = 3
|
240
|
-
@@tempf_name = File.dirname(@file_name) + File::SEPARATOR + ".SimpleRotate_tempfile_#{File.basename($0)}"
|
241
|
-
|
242
|
-
create_tempfile if @enable && !@@scheduled_del_lockfile
|
243
|
-
end
|
244
|
-
|
245
|
-
# Create the temp file for locking
|
246
|
-
private
|
247
|
-
def create_tempfile
|
248
|
-
if File.exist?(@@tempf_name)
|
249
|
-
open_temp_file
|
250
|
-
return nil
|
251
|
-
end
|
252
|
-
|
253
|
-
begin
|
254
|
-
@@tempf = File.open(@@tempf_name, File::RDWR|File::CREAT|File::EXCL)
|
255
|
-
|
256
|
-
rescue
|
257
|
-
SimpleRotate::Error.warning("Couldn't create temp file => #{@@tempf_name}")
|
258
|
-
|
259
|
-
ensure
|
260
|
-
set_delete_tempfile
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
private
|
265
|
-
def tempf_exists?
|
266
|
-
return File.exist?(@@tempf_name)
|
267
|
-
end
|
268
|
-
|
269
|
-
# Delete the lock file at the end of the script
|
270
|
-
private
|
271
|
-
def set_delete_tempfile
|
272
|
-
return true if @@scheduled_del_lockfile
|
273
|
-
|
274
|
-
if File.exists?(@@tempf_name)
|
275
|
-
# is it empty?
|
276
|
-
if File.size(@@tempf_name) == 0
|
277
|
-
delete_at_end
|
278
|
-
else
|
279
|
-
# it is not empty
|
280
|
-
msg = "File is not empty => #{@@tempf_name}#{$-0}"
|
281
|
-
msg += "Skip to delete temp file!"
|
282
|
-
SimpleRotate::Error.warning(msg)
|
283
|
-
end
|
284
|
-
end
|
285
|
-
@@scheduled_del_lockfile = true
|
286
|
-
end
|
287
|
-
|
288
|
-
private
|
289
|
-
def delete_at_end
|
290
|
-
at_exit do
|
291
|
-
begin
|
292
|
-
File.delete(@@tempf_name)
|
293
|
-
rescue
|
294
|
-
#SimpleRotate::Error.warning("Couldn't delete temp file => #{@@tempf_name}")
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
private
|
300
|
-
def reopen_temp_file
|
301
|
-
close_temp_file
|
302
|
-
open_temp_file
|
303
|
-
end
|
304
|
-
|
305
|
-
private
|
306
|
-
def open_temp_file
|
307
|
-
if @@tempf.is_a?(IO) && @@tempf.closed? || !@@tempf.is_a?(IO)
|
308
|
-
begin
|
309
|
-
@@tempf = File.open(@@tempf_name, File::RDWR|File::CREAT|File::APPEND)
|
310
|
-
rescue
|
311
|
-
SimpleRotate::Error.warning("Couldn't open temp file => #{@@tempf_name}")
|
312
|
-
end
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
private
|
317
|
-
def close_temp_file
|
318
|
-
if @@tempf.is_a?(IO) && !@@tempf.closed?
|
319
|
-
begin
|
320
|
-
@@tempf.close
|
321
|
-
rescue
|
322
|
-
SimpleRotate::Error.warning("Couldn't close temp file => #{@@tempf_name}")
|
323
|
-
end
|
324
|
-
end
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
#--------------------
|
329
|
-
# access definitions
|
330
|
-
#--------------------
|
331
|
-
attr_accessor :threshold,
|
332
|
-
:date_format,
|
333
|
-
:logging_format,
|
334
|
-
:rename_format,
|
335
|
-
:allow_overwrite,
|
336
|
-
:sleep_time
|
337
|
-
|
338
|
-
attr_reader :limit
|
339
|
-
|
340
|
-
#----------------
|
341
|
-
# public methods
|
342
|
-
#----------------
|
343
|
-
#
|
344
|
-
# return method missing error
|
345
|
-
#
|
346
|
-
def method_missing(name, *argv)
|
347
|
-
SimpleRotate::Error.missing(name)
|
348
|
-
end
|
349
|
-
|
350
|
-
#
|
351
|
-
# @param string|symbol $file_name
|
352
|
-
# @param string|int $limit
|
353
|
-
# @param int $rotation
|
354
|
-
# @return self
|
355
|
-
#
|
356
|
-
def init(file_name=File.absolute_path($0+".log"), limit='1M', rotation=0)
|
357
|
-
@file_name = file_name
|
358
|
-
@limit = limit
|
359
|
-
@rotation = rotation
|
360
|
-
|
361
|
-
# load defaults
|
362
|
-
include_defaults
|
363
|
-
|
364
|
-
# validation
|
365
|
-
valid_file_name
|
366
|
-
valid_int("rotation", @rotation)
|
367
|
-
|
368
|
-
# stdout only
|
369
|
-
return self if @only_stdout
|
370
|
-
|
371
|
-
if rotate_by_term?
|
372
|
-
# term rotation
|
373
|
-
set_days_cnt_of_term
|
374
|
-
@limit_term = @limit
|
375
|
-
@rotate_by_term = true
|
376
|
-
|
377
|
-
else
|
378
|
-
# file_size rotation
|
379
|
-
@limit_size = trim_byte(@limit)
|
380
|
-
if @limit_size <= 0
|
381
|
-
SimpleRotate::Error.argv("limit", @limit)
|
382
|
-
end
|
383
|
-
@rotate_by_term = false
|
384
|
-
end
|
385
|
-
|
386
|
-
# for process safe
|
387
|
-
@psync = ProcessSync.new(self)
|
388
|
-
|
389
|
-
# open or generate the log file
|
390
|
-
synchronize do
|
391
|
-
@psync.lock
|
392
|
-
|
393
|
-
prepare_logf
|
394
|
-
|
395
|
-
@psync.unlock
|
396
|
-
end
|
397
|
-
|
398
|
-
# if block given, colse IO
|
399
|
-
if defined? yield
|
400
|
-
yield self
|
401
|
-
e
|
402
|
-
end
|
403
|
-
|
404
|
-
return self
|
405
|
-
end
|
406
|
-
|
407
|
-
#
|
408
|
-
# log message out to STDOUT when use SimpleRotate#w method
|
409
|
-
#
|
410
|
-
def with_stdout
|
411
|
-
@with_stdout = true
|
412
|
-
end
|
413
|
-
|
414
|
-
#
|
415
|
-
# enable compress
|
416
|
-
#
|
417
|
-
def compress
|
418
|
-
@compress = true
|
419
|
-
use_zlib
|
420
|
-
end
|
421
|
-
|
422
|
-
#
|
423
|
-
# define the compression level
|
424
|
-
# this method load 'zlib'
|
425
|
-
# this method enable compress flag
|
426
|
-
# @param int level - 0-9
|
427
|
-
# default is 6
|
428
|
-
#
|
429
|
-
def compress_level(level)
|
430
|
-
@compress_level = level
|
431
|
-
valid_int("compress_level", @compress_level)
|
432
|
-
compress
|
433
|
-
|
434
|
-
return level
|
435
|
-
end
|
436
|
-
|
437
|
-
#
|
438
|
-
# @param string $log message write to log file
|
439
|
-
# @return string
|
440
|
-
#
|
441
|
-
def w(log)
|
442
|
-
if @file_name == nil
|
443
|
-
msg = "file_name is Nil Class! Please call #init method"
|
444
|
-
SimpleRotate::Error.throw_error(msg)
|
445
|
-
end
|
446
|
-
|
447
|
-
# don't out log message if Doesn't reach threshold
|
448
|
-
return nil if (!over_threshold?)
|
449
|
-
|
450
|
-
content = get_trimmed_log(log)
|
451
|
-
|
452
|
-
# return and end func, if only_stdout enable
|
453
|
-
if @only_stdout
|
454
|
-
puts content
|
455
|
-
return log
|
456
|
-
end
|
457
|
-
|
458
|
-
# write message to file
|
459
|
-
synchronize do
|
460
|
-
@psync.lock
|
461
|
-
|
462
|
-
sync_inode
|
463
|
-
@logf.puts(content)
|
464
|
-
@logf.flush if @enable_wflush
|
465
|
-
@logf.fsync if @enable_wflush
|
466
|
-
|
467
|
-
@psync.unlock
|
468
|
-
end
|
469
|
-
|
470
|
-
# dump log message STDOUT, if with_stdout enable
|
471
|
-
puts content if @with_stdout
|
472
|
-
|
473
|
-
# rotate if necessary
|
474
|
-
rotate_if if !@no_wcheck
|
475
|
-
|
476
|
-
return log
|
477
|
-
end
|
478
|
-
|
479
|
-
#
|
480
|
-
# disable call File#flush after #w method
|
481
|
-
#
|
482
|
-
def enable_wflush
|
483
|
-
@enable_wflush = true
|
484
|
-
end
|
485
|
-
|
486
|
-
#
|
487
|
-
# enable call File#flush after #w method
|
488
|
-
#
|
489
|
-
def disable_wflush
|
490
|
-
@enable_wflush = false
|
491
|
-
end
|
492
|
-
|
493
|
-
#
|
494
|
-
# close file
|
495
|
-
#
|
496
|
-
def e
|
497
|
-
return nil if logf_not_usable
|
498
|
-
|
499
|
-
synchronize do
|
500
|
-
@psync.lock
|
501
|
-
|
502
|
-
@logf.close
|
503
|
-
|
504
|
-
@psync.unlock
|
505
|
-
end
|
506
|
-
end
|
507
|
-
|
508
|
-
#
|
509
|
-
# file reopen
|
510
|
-
#
|
511
|
-
def reopen
|
512
|
-
return nil if logf_not_usable
|
513
|
-
|
514
|
-
if !file_closed?
|
515
|
-
SimpleRotate::Error.warning("File is already open!")
|
516
|
-
return nil
|
517
|
-
end
|
518
|
-
|
519
|
-
openadd
|
520
|
-
return @logf
|
521
|
-
end
|
522
|
-
|
523
|
-
#
|
524
|
-
# force rotation
|
525
|
-
#
|
526
|
-
def flush
|
527
|
-
return nil if logf_not_usable
|
528
|
-
return nil if @rotate_by_term
|
529
|
-
rotation(:FLUSH)
|
530
|
-
end
|
531
|
-
|
532
|
-
#
|
533
|
-
# don't check can to rotate at #w method
|
534
|
-
#
|
535
|
-
def no_wcheck
|
536
|
-
@no_wcheck = true
|
537
|
-
end
|
538
|
-
|
539
|
-
#
|
540
|
-
# is log file open?
|
541
|
-
# @return nil|bool
|
542
|
-
#
|
543
|
-
def file_closed?
|
544
|
-
return nil if logf_not_usable
|
545
|
-
return @logf.closed?
|
546
|
-
end
|
547
|
-
|
548
|
-
#
|
549
|
-
# skip warning message
|
550
|
-
#
|
551
|
-
def silence
|
552
|
-
SimpleRotate::Error.silence
|
553
|
-
end
|
554
|
-
|
555
|
-
#
|
556
|
-
# set log level FATAL
|
557
|
-
# @return self
|
558
|
-
#
|
559
|
-
def fatal
|
560
|
-
@log_level = 5
|
561
|
-
return self
|
562
|
-
end
|
563
|
-
|
564
|
-
#
|
565
|
-
# set log level ERROR
|
566
|
-
# @return self
|
567
|
-
#
|
568
|
-
def error
|
569
|
-
@log_level = 4
|
570
|
-
return self
|
571
|
-
end
|
572
|
-
|
573
|
-
#
|
574
|
-
# set log level WORN
|
575
|
-
# @return self
|
576
|
-
#
|
577
|
-
def warn
|
578
|
-
@log_level = 3
|
579
|
-
return self
|
580
|
-
end
|
581
|
-
|
582
|
-
#
|
583
|
-
# set log level INFO
|
584
|
-
# @return self
|
585
|
-
#
|
586
|
-
def info
|
587
|
-
@log_level = 2
|
588
|
-
return self
|
589
|
-
end
|
590
|
-
|
591
|
-
#
|
592
|
-
# set log level DEBUG
|
593
|
-
# @return self
|
594
|
-
#
|
595
|
-
def debug
|
596
|
-
@log_level = 1
|
597
|
-
return self
|
598
|
-
end
|
599
|
-
|
600
|
-
#
|
601
|
-
# try to be a safe process
|
602
|
-
#
|
603
|
-
def psafe_mode(sleep_time=0.1)
|
604
|
-
@psafe_mode = true
|
605
|
-
@enable_wflush = true
|
606
|
-
@sleep_time = sleep_time
|
607
|
-
|
608
|
-
@psync = ProcessSync.new(self)
|
609
|
-
end
|
610
|
-
|
611
|
-
#
|
612
|
-
# Reopen file necessary
|
613
|
-
# @return bool|nil
|
614
|
-
#
|
615
|
-
def sync_inode
|
616
|
-
return nil if logf_not_usable
|
617
|
-
|
618
|
-
cnt = 0
|
619
|
-
begin
|
620
|
-
# check i-node number
|
621
|
-
open_inode = @logf.stat.ino
|
622
|
-
logf_inode = File.stat(@file_name).ino
|
623
|
-
raise if open_inode != logf_inode
|
624
|
-
rescue
|
625
|
-
cnt += 1
|
626
|
-
sleep(0.1)
|
627
|
-
e
|
628
|
-
openadd
|
629
|
-
|
630
|
-
if cnt <= @sync_inode_limit
|
631
|
-
retry
|
632
|
-
else
|
633
|
-
SimpleRotate::Error.warning(%{inode number didn't not match, tried #{cnt} times!})
|
634
|
-
return false
|
635
|
-
end
|
636
|
-
end
|
637
|
-
|
638
|
-
return true
|
639
|
-
end
|
640
|
-
|
641
|
-
#
|
642
|
-
# Disable #sync_inode
|
643
|
-
#
|
644
|
-
def no_sync_inode
|
645
|
-
@no_sync_inode = true
|
646
|
-
end
|
647
|
-
|
648
|
-
#----------------
|
649
|
-
# private methods
|
650
|
-
#----------------
|
651
|
-
#
|
652
|
-
# log file is not IO class or stdout only
|
653
|
-
# @return bool
|
654
|
-
#
|
655
|
-
private
|
656
|
-
def logf_not_usable
|
657
|
-
!@logf.is_a?(IO) || @only_stdout
|
658
|
-
end
|
659
|
-
|
660
|
-
#
|
661
|
-
# load zlib lib
|
662
|
-
#
|
663
|
-
private
|
664
|
-
def use_zlib
|
665
|
-
begin
|
666
|
-
require "zlib"
|
667
|
-
@compress_level = Zlib::DEFAULT_COMPRESSION if @compress_level == nil
|
668
|
-
|
669
|
-
rescue LoadError
|
670
|
-
SimpleRotate::Error.load("zlib")
|
671
|
-
end
|
672
|
-
end
|
673
|
-
|
674
|
-
#
|
675
|
-
# open or generate the log file
|
676
|
-
#
|
677
|
-
private
|
678
|
-
def prepare_logf
|
679
|
-
if File.exist?(@file_name)
|
680
|
-
# open the exists file, add mode
|
681
|
-
openadd
|
682
|
-
|
683
|
-
# rotate it if necessary
|
684
|
-
rotate_if
|
685
|
-
|
686
|
-
else
|
687
|
-
gen_new_logf
|
688
|
-
end
|
689
|
-
end
|
690
|
-
|
691
|
-
#
|
692
|
-
# generate new log file
|
693
|
-
#
|
694
|
-
private
|
695
|
-
def gen_new_logf
|
696
|
-
begin
|
697
|
-
@logf = File.open(@file_name, File::RDWR|File::CREAT|File::TRUNC)
|
698
|
-
gtime = Time.new.to_i
|
699
|
-
@logf.puts("created@#{gtime}@Please don't delete this line")
|
700
|
-
@logf.close
|
701
|
-
|
702
|
-
rescue
|
703
|
-
SimpleRotate::Error.open(@file_name)
|
704
|
-
end
|
705
|
-
|
706
|
-
openadd
|
707
|
-
end
|
708
|
-
|
709
|
-
#
|
710
|
-
# if file or directory exist, call error
|
711
|
-
#
|
712
|
-
private
|
713
|
-
def exist_error(file)
|
714
|
-
SimpleRotate::Error.exist(file, "File") if File.exist?(file)
|
715
|
-
SimpleRotate::Error.exist(file, "Directory") if Dir.exist?(file)
|
716
|
-
|
717
|
-
return true
|
718
|
-
end
|
719
|
-
|
720
|
-
#
|
721
|
-
# define default instance vars
|
722
|
-
#
|
723
|
-
private
|
724
|
-
def include_defaults
|
725
|
-
que = []
|
726
|
-
que << [%{@threshold}, %{LOG_LEVEL_2}]
|
727
|
-
que << [%{@log_level}, %{2}]
|
728
|
-
que << [%{@logging_format}, %{"[$DATE] - $LEVEL : $LOG"}]
|
729
|
-
que << [%{@date_format}, %{"%Y/%m/%d %H:%M:%S"}]
|
730
|
-
que << [%{@term_format}, %{"%Y%m%d"}]
|
731
|
-
que << [%{@rename_format}, %{"."}]
|
732
|
-
que << [%{@with_stdout}, %{false}]
|
733
|
-
que << [%{@only_stdout}, %{false}]
|
734
|
-
que << [%{@no_wcheck}, %{false}]
|
735
|
-
que << [%{@sync_inode_limit}, %{3}]
|
736
|
-
que << [%{@no_sync_inode}, %{false}]
|
737
|
-
que << [%{@enable_wflush}, %{false}]
|
738
|
-
que << [%{@compress}, %{false}]
|
739
|
-
que << [%{@psafe_mode}, %{false}]
|
740
|
-
que << [%{@sleep_time}, %{0}]
|
741
|
-
|
742
|
-
que.each do |q|
|
743
|
-
if eval(%{#{q[0]} == nil})
|
744
|
-
eval(%{#{q[0]} = #{q[1]}})
|
745
|
-
end
|
746
|
-
end
|
747
|
-
end
|
748
|
-
|
749
|
-
#
|
750
|
-
# Whether to rotate by file age?
|
751
|
-
# @return bool
|
752
|
-
#
|
753
|
-
private
|
754
|
-
def rotate_by_term?
|
755
|
-
if @limit.is_a?(Integer)
|
756
|
-
return false
|
757
|
-
|
758
|
-
elsif @limit.is_a?(String)
|
759
|
-
return @limit.to_i == 0
|
760
|
-
|
761
|
-
else
|
762
|
-
SimpleRotate::Error.argv("limit", @limit)
|
763
|
-
end
|
764
|
-
end
|
765
|
-
|
766
|
-
#
|
767
|
-
# Open the log file, add mode
|
768
|
-
#
|
769
|
-
private
|
770
|
-
def openadd
|
771
|
-
@logf = File.open(@file_name, File::RDWR|File::CREAT|File::APPEND)
|
772
|
-
|
773
|
-
# refresh object
|
774
|
-
@psync = ProcessSync.new(self)
|
775
|
-
end
|
776
|
-
|
777
|
-
#
|
778
|
-
# get cretated time of the log file
|
779
|
-
# @return Time
|
780
|
-
#
|
781
|
-
private
|
782
|
-
def get_logf_generate_time
|
783
|
-
pos = @logf.pos
|
784
|
-
begin
|
785
|
-
@logf.rewind
|
786
|
-
stamp = @logf.readline.split("@")
|
787
|
-
@logf.seek(pos)
|
788
|
-
gtime = Time.at(stamp[1].to_i)
|
789
|
-
|
790
|
-
rescue StandardError, SyntaxError
|
791
|
-
msg = "Can't get file creation time!"
|
792
|
-
gtime = Time.now
|
793
|
-
SimpleRotate::Error.warning(msg)
|
794
|
-
end
|
795
|
-
|
796
|
-
return gtime
|
797
|
-
end
|
798
|
-
|
799
|
-
#
|
800
|
-
# @return int
|
801
|
-
#
|
802
|
-
private
|
803
|
-
def set_days_cnt_of_term
|
804
|
-
begin
|
805
|
-
@dayc = eval("TERM_#{@limit}")
|
806
|
-
rescue NameError
|
807
|
-
SimpleRotate::Error.argv("limit", @limit)
|
808
|
-
end
|
809
|
-
end
|
810
|
-
|
811
|
-
#
|
812
|
-
# log file size over 'limit_size'?
|
813
|
-
# @return bool|nil
|
814
|
-
#
|
815
|
-
private
|
816
|
-
def over_size?
|
817
|
-
return nil if logf_not_usable
|
818
|
-
|
819
|
-
begin
|
820
|
-
rst = File.size(@file_name) > @limit_size
|
821
|
-
rescue
|
822
|
-
rst = false
|
823
|
-
end
|
824
|
-
|
825
|
-
return rst
|
826
|
-
end
|
827
|
-
|
828
|
-
private
|
829
|
-
def safe_over_size?
|
830
|
-
rst = nil
|
831
|
-
synchronize do
|
832
|
-
@psync.lock
|
833
|
-
rst = over_size?
|
834
|
-
@psync.unlock
|
835
|
-
end
|
836
|
-
|
837
|
-
return rst
|
838
|
-
end
|
839
|
-
|
840
|
-
#
|
841
|
-
# logfile's elapsed days is over limit?
|
842
|
-
# @return bool
|
843
|
-
#
|
844
|
-
private
|
845
|
-
def over_term?
|
846
|
-
return nil if logf_not_usable
|
847
|
-
|
848
|
-
begin
|
849
|
-
now_time = Time.now
|
850
|
-
gen_time = get_logf_generate_time
|
851
|
-
estimated_time = gen_time + (60 * 60 * 24 * @dayc)
|
852
|
-
rst = estimated_time <= now_time
|
853
|
-
rescue
|
854
|
-
rst = false
|
855
|
-
end
|
856
|
-
|
857
|
-
return rst
|
858
|
-
end
|
859
|
-
|
860
|
-
private
|
861
|
-
def safe_over_term?
|
862
|
-
rst = nil
|
863
|
-
synchronize do
|
864
|
-
@psync.lock
|
865
|
-
rst = over_term?
|
866
|
-
@psync.unlock
|
867
|
-
end
|
868
|
-
|
869
|
-
return rst
|
870
|
-
end
|
871
|
-
|
872
|
-
#
|
873
|
-
# Format the text for logging
|
874
|
-
# the following characters are recognized
|
875
|
-
# $DATE => date
|
876
|
-
# $LEVEL => log's severity
|
877
|
-
# $LOG => your log message
|
878
|
-
# $PID => process ID
|
879
|
-
# $FILE => execute file name
|
880
|
-
#
|
881
|
-
# @param string $log
|
882
|
-
# @return string
|
883
|
-
#
|
884
|
-
private
|
885
|
-
def get_trimmed_log(log)
|
886
|
-
log = log.to_s
|
887
|
-
date = Time.now.strftime(@date_format)
|
888
|
-
level = eval("LOG_LEVEL_#{@log_level}")
|
889
|
-
return @logging_format.gsub("$DATE", date)
|
890
|
-
.gsub("$LEVEL", level)
|
891
|
-
.gsub("$LOG", log)
|
892
|
-
.gsub("$PID", $$.to_s)
|
893
|
-
.gsub("$FILE-FUL", File.absolute_path($0))
|
894
|
-
.gsub("$FILE", File.basename($0))
|
895
|
-
end
|
896
|
-
|
897
|
-
#
|
898
|
-
# Whether that is the output of the log level that exceeds the threshold
|
899
|
-
# @return boolean
|
900
|
-
#
|
901
|
-
private
|
902
|
-
def over_threshold?
|
903
|
-
begin
|
904
|
-
return (@log_level >= eval("LEVEL_ID_#{@threshold}"))
|
905
|
-
rescue NameError
|
906
|
-
SimpleRotate::Error.argv("threshold", @threshold)
|
907
|
-
end
|
908
|
-
end
|
909
|
-
|
910
|
-
#
|
911
|
-
# need rotate?
|
912
|
-
# @return bool
|
913
|
-
#
|
914
|
-
private
|
915
|
-
def reached_limit?(mode=:NO_LOCK)
|
916
|
-
# file age rotation
|
917
|
-
if @rotate_by_term
|
918
|
-
is_over_term = nil
|
919
|
-
if mode == :NO_LOCK
|
920
|
-
is_over_term = over_term?
|
921
|
-
elsif mode == :LOCK
|
922
|
-
is_over_term = safe_over_term?
|
923
|
-
end
|
924
|
-
|
925
|
-
return is_over_term
|
926
|
-
end
|
927
|
-
|
928
|
-
# file size rotation
|
929
|
-
is_over_size = nil
|
930
|
-
if mode == :NO_LOCK
|
931
|
-
is_over_size = over_size?
|
932
|
-
elsif mode == :LOCK
|
933
|
-
is_over_size = safe_over_size?
|
934
|
-
end
|
935
|
-
|
936
|
-
return is_over_size
|
937
|
-
end
|
938
|
-
|
939
|
-
#
|
940
|
-
# Rotates as necessary
|
941
|
-
# @return bool
|
942
|
-
#
|
943
|
-
private
|
944
|
-
def rotate_if
|
945
|
-
if reached_limit?(:LOCK)
|
946
|
-
rotation
|
947
|
-
return true
|
948
|
-
|
949
|
-
else
|
950
|
-
# no need to rotate
|
951
|
-
return false
|
952
|
-
end
|
953
|
-
end
|
954
|
-
|
955
|
-
#
|
956
|
-
# prepare & call #do_rotation
|
957
|
-
#
|
958
|
-
private
|
959
|
-
def rotation(mode=:NO_SPEC)
|
960
|
-
synchronize do
|
961
|
-
# if rotationing now by another process, return
|
962
|
-
if @psync.locked? #=> if not process safe mode, will be return nil
|
963
|
-
return false
|
964
|
-
end
|
965
|
-
|
966
|
-
# lock another process if enable
|
967
|
-
@psync.lock
|
968
|
-
|
969
|
-
do_rotate(mode)
|
970
|
-
|
971
|
-
# unlock another process if enable
|
972
|
-
@psync.unlock
|
973
|
-
end
|
974
|
-
end
|
975
|
-
|
976
|
-
#
|
977
|
-
# Rotate the log file now, and open a new one
|
978
|
-
#
|
979
|
-
private
|
980
|
-
def do_rotate(mode)
|
981
|
-
return nil if logf_not_usable
|
982
|
-
|
983
|
-
# check already executed rotation?
|
984
|
-
if mode != :FLUSH && !reached_limit?
|
985
|
-
return false
|
986
|
-
end
|
987
|
-
|
988
|
-
# file age rotation
|
989
|
-
if @rotate_by_term
|
990
|
-
rtn = do_term_rotate
|
991
|
-
return rtn
|
992
|
-
end
|
993
|
-
|
994
|
-
# file size rotation
|
995
|
-
cnt = 1
|
996
|
-
rotate_name = "#{@file_name}#{@rename_format}#{cnt}"
|
997
|
-
rotate_name += ".gz" if @compress
|
998
|
-
|
999
|
-
if File.exist?(rotate_name)
|
1000
|
-
while File.exist?(rotate_name)
|
1001
|
-
cnt += 1
|
1002
|
-
rotate_name = "#{@file_name}#{@rename_format}#{cnt}"
|
1003
|
-
rotate_name += ".gz" if @compress
|
1004
|
-
end
|
1005
|
-
|
1006
|
-
rename_wait_que = Array.new
|
1007
|
-
for nc in 1...cnt
|
1008
|
-
break if @rotation == 1
|
1009
|
-
if (@compress)
|
1010
|
-
rename_wait_que << "File.rename('#{@file_name}#{@rename_format}#{nc}.gz', '#{@file_name}#{@rename_format}#{nc+1}.gz')"
|
1011
|
-
else
|
1012
|
-
rename_wait_que << "File.rename('#{@file_name}#{@rename_format}#{nc}', '#{@file_name}#{@rename_format}#{nc+1}')"
|
1013
|
-
end
|
1014
|
-
|
1015
|
-
if @rotation
|
1016
|
-
next if @rotation == 0
|
1017
|
-
break if @rotation <= nc+1
|
1018
|
-
end
|
1019
|
-
end
|
1020
|
-
|
1021
|
-
rename_wait_que.reverse!
|
1022
|
-
|
1023
|
-
begin
|
1024
|
-
rename_wait_que.each do |do_rename|
|
1025
|
-
eval(do_rename)
|
1026
|
-
end
|
1027
|
-
rescue
|
1028
|
-
SimpleRotate::Error.warning(SimpleRotate::Error::ROTATION_FAILED)
|
1029
|
-
return false
|
1030
|
-
end
|
1031
|
-
end
|
1032
|
-
|
1033
|
-
most_recent_name = "#{@file_name}#{@rename_format}1"
|
1034
|
-
post_execute_rotate(most_recent_name)
|
1035
|
-
end
|
1036
|
-
|
1037
|
-
#
|
1038
|
-
# Rotate the log file now, and open a new one
|
1039
|
-
# for rotate by term
|
1040
|
-
#
|
1041
|
-
private
|
1042
|
-
def do_term_rotate
|
1043
|
-
date = Time.now.strftime(@term_format)
|
1044
|
-
rotate_name = "#{@file_name}#{@rename_format}#{date}"
|
1045
|
-
|
1046
|
-
# Don't rotation If a file with the same name already exists
|
1047
|
-
return false if File.exists?(rotate_name)
|
1048
|
-
return false if File.exists?("#{rotate_name}.gz")
|
1049
|
-
|
1050
|
-
post_execute_rotate(rotate_name)
|
1051
|
-
end
|
1052
|
-
|
1053
|
-
#
|
1054
|
-
# rename log_file & generate new one
|
1055
|
-
#
|
1056
|
-
private
|
1057
|
-
def post_execute_rotate(after_name)
|
1058
|
-
begin
|
1059
|
-
@logf.close
|
1060
|
-
File.rename(@file_name, after_name)
|
1061
|
-
do_compress(after_name) if @compress
|
1062
|
-
prepare_logf
|
1063
|
-
|
1064
|
-
# sleep after rotation
|
1065
|
-
sleep(@sleep_time) if @sleep_time > 0
|
1066
|
-
|
1067
|
-
rescue
|
1068
|
-
SimpleRotate::Error.warning(SimpleRotate::Error::ROTATION_FAILED)
|
1069
|
-
reopen if file_closed?
|
1070
|
-
end
|
1071
|
-
end
|
1072
|
-
|
1073
|
-
#
|
1074
|
-
# compress rotated file
|
1075
|
-
#
|
1076
|
-
private
|
1077
|
-
def do_compress(file)
|
1078
|
-
contents = nil
|
1079
|
-
File.open(file, File::RDONLY) do |f|
|
1080
|
-
contents = f.read
|
1081
|
-
end
|
1082
|
-
|
1083
|
-
newf = "#{file}.gz"
|
1084
|
-
|
1085
|
-
io = File.open(newf, File::WRONLY|File::CREAT|File::TRUNC)
|
1086
|
-
Zlib::GzipWriter.wrap(io, @compress_level) do |writer|
|
1087
|
-
writer.mtime = File.mtime(file).to_i
|
1088
|
-
writer.orig_name = file
|
1089
|
-
writer.write(contents)
|
1090
|
-
end
|
1091
|
-
|
1092
|
-
File.delete(file)
|
1093
|
-
end
|
1094
|
-
|
1095
|
-
#
|
1096
|
-
# convert 'limit_size' to integer
|
1097
|
-
# @param string|int $limit_size
|
1098
|
-
# @return int
|
1099
|
-
#
|
1100
|
-
private
|
1101
|
-
def trim_byte(limit)
|
1102
|
-
return limit if limit.is_a?(Integer)
|
1103
|
-
|
1104
|
-
kiro = "000"
|
1105
|
-
mega = kiro + "000"
|
1106
|
-
giga = mega + "000"
|
1107
|
-
tera = giga + "000"
|
1108
|
-
limit_size = limit
|
1109
|
-
|
1110
|
-
if /K$/ =~ limit_size
|
1111
|
-
limit_size = limit_size.sub(/K$/, "") + kiro
|
1112
|
-
elsif /M$/ =~ limit_size
|
1113
|
-
limit_size = limit_size.sub(/M$/, "") + mega
|
1114
|
-
elsif /G$/ =~ limit_size
|
1115
|
-
limit_size = limit_size.sub(/G$/, "") + giga
|
1116
|
-
elsif /T$/ =~ limit_size
|
1117
|
-
limit_size = limit_size.sub(/T$/, "") + tera
|
1118
|
-
end
|
1119
|
-
|
1120
|
-
return limit_size.to_i
|
1121
|
-
end
|
1122
|
-
|
1123
|
-
#--------------------
|
1124
|
-
# method alias
|
1125
|
-
#--------------------
|
1126
|
-
alias_method :<<, :w
|
1127
|
-
end
|
3
|
+
# Simple and safety logger
|
4
|
+
# @autor: Kazuya Hotta
|
5
|
+
#
|
6
|
+
require_relative "simple_rotate/core.rb"
|