class SimpleRotate
Constants
- DESCRIPTION
- HOMEPAGE
- LIBS_NAME
- SUMMARY
- VERSION
Attributes
Public Instance Methods
enable compress
# File lib/simple_rotate/core.rb, line 97 def compress @compress = true use_zlib return nil end
define the compression level this method load 'zlib' this method enable compress flag @param int level - 0-9
default is 6
# File lib/simple_rotate/core.rb, line 108 def compress_level(level) @compress_level = level valid_int("compress_level", @compress_level) compress return nil end
set log level DEBUG @return self
# File lib/simple_rotate/core.rb, line 252 def debug @log_level = 1 return self end
enable call File#flush after w
method
# File lib/simple_rotate/core.rb, line 162 def disable_wflush @enable_wflush = false return nil end
close file
# File lib/simple_rotate/core.rb, line 168 def e return nil if logf_not_usable synchronize do @psync.lock @logf.close @psync.unlock end return true end
disable call File#flush after w
method
# File lib/simple_rotate/core.rb, line 156 def enable_wflush @enable_wflush = true return nil end
set log level ERROR @return self
# File lib/simple_rotate/core.rb, line 231 def error @log_level = 4 return self end
set log level FATAL @return self
# File lib/simple_rotate/core.rb, line 224 def fatal @log_level = 5 return self end
is log file open? @return nil|bool
# File lib/simple_rotate/core.rb, line 211 def file_closed? return nil if logf_not_usable return @logf.closed? end
force rotation
# File lib/simple_rotate/core.rb, line 196 def flush return nil if logf_not_usable return nil if @rotate_by_term rotation(:FLUSH) return true end
set log level INFO @return self
# File lib/simple_rotate/core.rb, line 245 def info @log_level = 2 return self end
@param string|symbol $file_name @param string|int $limit @param int $rotation @return self
# File lib/simple_rotate/core.rb, line 39 def init(file_name=File.absolute_path($0+".log"), limit="100M", rotation=0) @file_name = file_name @limit = limit @rotation = rotation # load defaults include_defaults # validation valid_file_name valid_int("rotation", @rotation) # stdout only return self if @only_stdout if rotate_by_term? # term rotation set_days_cnt_of_term @limit_term = @limit @rotate_by_term = true else # file_size rotation @limit_size = trim_byte(@limit) if @limit_size <= 0 SimpleRotate::Error.argv("limit", @limit) end @rotate_by_term = false end # for process sync @psync = ProcessSync.new(self) # open or generate the log file synchronize do @psync.lock prepare_logf @psync.unlock end # if block is given, colse IO if defined? yield yield self e end return self end
return method missing error
# File lib/simple_rotate/core.rb, line 31 def method_missing(name, *argv) SimpleRotate::Error.missing(name) end
disable sync_inode
# File lib/simple_rotate/core.rb, line 297 def no_sync_inode @no_sync_inode = true return nil end
don't check can to rotate at w
method
# File lib/simple_rotate/core.rb, line 204 def no_wcheck @no_wcheck = true return nil; end
synchronize processes
# File lib/simple_rotate/core.rb, line 258 def psync(sleep_time=0.1) @is_psync = true @enable_wflush = true @sleep_time = sleep_time @psync = ProcessSync.new(self) return nil end
file reopen
# File lib/simple_rotate/core.rb, line 183 def reopen return nil if logf_not_usable if !file_closed? SimpleRotate::Error.warning("File is already open!") return nil end openadd return @logf end
skip warning message
# File lib/simple_rotate/core.rb, line 217 def silence SimpleRotate::Error.silence return nil; end
reopen file necessary @return bool|nil
# File lib/simple_rotate/core.rb, line 269 def sync_inode return nil if logf_not_usable cnt = 0 begin # check i-node number open_inode = @logf.stat.ino logf_inode = File.stat(@file_name).ino raise if open_inode != logf_inode rescue cnt += 1 sleep(0.1) e openadd if cnt <= @sync_inode_limit retry else SimpleRotate::Error.warning(%{inode number didn't not match, tried #{cnt} times!}) return false end end return true end
@param string $log message write to log file @return string
# File lib/simple_rotate/core.rb, line 117 def w(log) if @file_name == nil msg = "file_name is Nil Class! Please call #init method" SimpleRotate::Error.throw_error(msg) end # don't out log message if Doesn't reach threshold return nil if (!over_threshold?) content = get_trimmed_log(log) # return and end func, if only_stdout enable if @only_stdout puts content return log end # write message to file synchronize do @psync.lock sync_inode if !@no_sync_inode @logf.puts(content) @logf.flush if @enable_wflush @logf.fsync if @enable_wflush @psync.unlock end # dump log message STDOUT, if with_stdout enable puts content if @with_stdout # rotate if necessary rotate_if if !@no_wcheck return log end
set log level WORN @return self
# File lib/simple_rotate/core.rb, line 238 def warn @log_level = 3 return self end
log message out to STDOUT when use SimpleRotate#w
method
# File lib/simple_rotate/core.rb, line 91 def with_stdout @with_stdout = true return nil end
Private Instance Methods
compress rotated file
# File lib/simple_rotate/private.rb, line 398 def do_compress(file) contents = nil File.open(file, File::RDONLY) do |f| contents = f.read end newf = "#{file}.gz" io = File.open(newf, File::WRONLY|File::CREAT|File::TRUNC) Zlib::GzipWriter.wrap(io, @compress_level) do |writer| writer.mtime = File.mtime(file).to_i writer.orig_name = file writer.write(contents) end File.delete(file) end
rotate the log file and open a new one
# File lib/simple_rotate/private.rb, line 306 def do_rotate(mode) return nil if logf_not_usable # check already executed rotation? if mode != :FLUSH && !reached_limit? return false end # file age rotation if @rotate_by_term rtn = do_term_rotate return rtn end # file size rotation cnt = 1 rotate_name = "#{@file_name}#{@rename_format}#{cnt}" rotate_name += ".gz" if @compress if File.exist?(rotate_name) while File.exist?(rotate_name) cnt += 1 rotate_name = "#{@file_name}#{@rename_format}#{cnt}" rotate_name += ".gz" if @compress end rename_wait_que = Array.new for nc in 1...cnt break if @rotation == 1 if (@compress) rename_wait_que << "File.rename('#{@file_name}#{@rename_format}#{nc}.gz', '#{@file_name}#{@rename_format}#{nc+1}.gz')" else rename_wait_que << "File.rename('#{@file_name}#{@rename_format}#{nc}', '#{@file_name}#{@rename_format}#{nc+1}')" end if @rotation next if @rotation == 0 break if @rotation <= nc+1 end end rename_wait_que.reverse! begin rename_wait_que.each do |do_rename| eval(do_rename) end rescue SimpleRotate::Error.warning(SimpleRotate::Error::ROTATION_FAILED) return false end end most_recent_name = "#{@file_name}#{@rename_format}1" post_execute_rotate(most_recent_name) end
Rotate the log file now, and open a new one for rotate by term
# File lib/simple_rotate/private.rb, line 367 def do_term_rotate date = Time.now.strftime(@term_format) rotate_name = "#{@file_name}#{@rename_format}#{date}" # Don't rotation If a file with the same name already exists return false if File.exists?(rotate_name) return false if File.exists?("#{rotate_name}.gz") post_execute_rotate(rotate_name) end
if file or directory exist, call error
# File lib/simple_rotate/private.rb, line 57 def exist_error(file) SimpleRotate::Error.exist(file, "File") if File.exist?(file) SimpleRotate::Error.exist(file, "Directory") if Dir.exist?(file) return true end
generate new log file
# File lib/simple_rotate/private.rb, line 41 def gen_new_logf begin @logf = File.open(@file_name, File::RDWR|File::CREAT|File::TRUNC) gtime = Time.new.to_i @logf.puts("created@#{gtime}@Please don't delete this line") @logf.close rescue SimpleRotate::Error.open(@file_name) end openadd end
get cretated time of the log file @return Time
# File lib/simple_rotate/private.rb, line 118 def get_logf_generate_time pos = @logf.pos begin @logf.rewind stamp = @logf.readline.split("@") @logf.seek(pos) gtime = Time.at(stamp[1].to_i) rescue StandardError, SyntaxError msg = "Can't get file creation time!" gtime = Time.now SimpleRotate::Error.warning(msg) end return gtime end
Format the text for logging the following characters are recognized $DATE => date $LEVEL => log's severity $LOG => your log message $PID => process ID $FILE => execute file name
@param string $log @return string
# File lib/simple_rotate/private.rb, line 215 def get_trimmed_log(log) if log == nil log = log.inspect else log = log.to_s end date = Time.now.strftime(@date_format) level = eval("LOG_LEVEL_#{@log_level}") return @logging_format.gsub("$DATE", date) .gsub("$LEVEL", level) .gsub("$LOG", log) .gsub("$PID", $$.to_s) .gsub("$FILE-FUL", File.absolute_path($0)) .gsub("$FILE", File.basename($0)) end
define default instance vars
# File lib/simple_rotate/private.rb, line 66 def include_defaults que = [] que << [%{@threshold}, %{LOG_LEVEL_2}] que << [%{@log_level}, %{2}] que << [%{@logging_format}, %{"[$DATE] - $LEVEL : $LOG"}] que << [%{@date_format}, %{"%Y/%m/%d %H:%M:%S"}] que << [%{@term_format}, %{"%Y%m%d"}] que << [%{@rename_format}, %{"."}] que << [%{@with_stdout}, %{false}] que << [%{@only_stdout}, %{false}] que << [%{@no_wcheck}, %{false}] que << [%{@sync_inode_limit}, %{3}] que << [%{@no_sync_inode}, %{false}] que << [%{@enable_wflush}, %{false}] que << [%{@compress}, %{false}] que << [%{@is_psync}, %{false}] que << [%{@sleep_time}, %{0}] que.each do |q| if !eval(%{self.instance_variable_defined? :#{q[0]}}) eval(%{#{q[0]} = #{q[1]}}) end end end
log file is not IO class or stdout only @return bool
# File lib/simple_rotate/private.rb, line 8 def logf_not_usable !@logf.is_a?(IO) || @only_stdout end
Open the log file, add mode
# File lib/simple_rotate/private.rb, line 108 def openadd @logf = File.open(@file_name, File::RDWR|File::CREAT|File::APPEND) # refresh object @psync = ProcessSync.new(self) end
log file size over 'limit_size'? @return bool|nil
# File lib/simple_rotate/private.rb, line 149 def over_size? return nil if logf_not_usable begin rst = File.size(@file_name) > @limit_size rescue rst = false end return rst end
logfile's elapsed days is over limit? @return bool
# File lib/simple_rotate/private.rb, line 176 def over_term? return nil if logf_not_usable begin now_time = Time.now gen_time = get_logf_generate_time estimated_time = gen_time + (60 * 60 * 24 * @dayc) rst = estimated_time <= now_time rescue rst = false end return rst end
Whether that is the output of the log level that exceeds the threshold @return boolean
# File lib/simple_rotate/private.rb, line 235 def over_threshold? begin return (@log_level >= eval("LEVEL_ID_#{@threshold}")) rescue NameError SimpleRotate::Error.argv("threshold", @threshold) end end
rename log_file & generate new one
# File lib/simple_rotate/private.rb, line 380 def post_execute_rotate(after_name) begin @logf.close File.rename(@file_name, after_name) do_compress(after_name) if @compress prepare_logf # sleep after rotation sleep(@sleep_time) if @sleep_time > 0 rescue SimpleRotate::Error.warning(SimpleRotate::Error::ROTATION_FAILED) reopen if file_closed? end end
open or generate the log file
# File lib/simple_rotate/private.rb, line 26 def prepare_logf if File.exist?(@file_name) # open the exists file, add mode openadd # rotate it if necessary rotate_if else gen_new_logf end end
need rotate? @return bool
# File lib/simple_rotate/private.rb, line 247 def reached_limit?(mode=:NO_LOCK) # file age rotation if @rotate_by_term is_over_term = nil if mode == :NO_LOCK is_over_term = over_term? elsif mode == :LOCK is_over_term = safe_over_term? end return is_over_term end # file size rotation is_over_size = nil if mode == :NO_LOCK is_over_size = over_size? elsif mode == :LOCK is_over_size = safe_over_size? end return is_over_size end
Whether to rotate by file age? @return bool
# File lib/simple_rotate/private.rb, line 94 def rotate_by_term? if @limit.is_a?(Integer) return false elsif @limit.is_a?(String) return @limit.to_i == 0 else SimpleRotate::Error.argv("limit", @limit) end end
Rotates as necessary @return bool
# File lib/simple_rotate/private.rb, line 274 def rotate_if if reached_limit?(:LOCK) rotation return true else # no need to rotate return false end end
prepare & call do_rotation
# File lib/simple_rotate/private.rb, line 287 def rotation(mode=:NO_SPEC) synchronize do # if rotationing now by another process, return if @psync.locked? #=> when didn't call #psync, will be return nil return false end # lock another process if enable @psync.lock do_rotate(mode) # unlock another process if enable @psync.unlock end end
# File lib/simple_rotate/private.rb, line 162 def safe_over_size? rst = nil synchronize do @psync.lock rst = over_size? @psync.unlock end return rst end
# File lib/simple_rotate/private.rb, line 193 def safe_over_term? rst = nil synchronize do @psync.lock rst = over_term? @psync.unlock end return rst end
@return int
# File lib/simple_rotate/private.rb, line 137 def set_days_cnt_of_term begin @dayc = eval("TERM_#{@limit}") rescue NameError SimpleRotate::Error.argv("limit", @limit) end end
convert 'limit_size' to integer @param string|int $limit_size @return int
# File lib/simple_rotate/private.rb, line 420 def trim_byte(limit) return limit if limit.is_a?(Integer) kiro = "000" mega = kiro + "000" giga = mega + "000" tera = giga + "000" limit_size = limit if /K$/ =~ limit_size limit_size = limit_size.sub(/K$/, "") + kiro elsif /M$/ =~ limit_size limit_size = limit_size.sub(/M$/, "") + mega elsif /G$/ =~ limit_size limit_size = limit_size.sub(/G$/, "") + giga elsif /T$/ =~ limit_size limit_size = limit_size.sub(/T$/, "") + tera end return limit_size.to_i end
load zlib lib
# File lib/simple_rotate/private.rb, line 14 def use_zlib begin require "zlib" @compress_level = Zlib::DEFAULT_COMPRESSION if @compress_level == nil rescue LoadError SimpleRotate::Error.load("zlib") end end