class SimpleRotate

Constants

DESCRIPTION
HOMEPAGE
LIBS_NAME
SUMMARY
VERSION

Attributes

allow_overwrite[RW]
date_format[RW]
limit[R]
logging_format[RW]
rename_format[RW]
sleep_time[RW]
threshold[RW]

Public Instance Methods

<<(log)
Alias for: w
compress() click to toggle source

enable compress

# File lib/simple_rotate/core.rb, line 97
def compress
    @compress = true
    use_zlib
    return nil
end
compress_level(level) click to toggle source

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
debug() click to toggle source

set log level DEBUG @return self

# File lib/simple_rotate/core.rb, line 252
def debug
    @log_level = 1
    return self
end
disable_wflush() click to toggle source

enable call File#flush after w method

# File lib/simple_rotate/core.rb, line 162
def disable_wflush
    @enable_wflush = false
    return nil
end
e() click to toggle source

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
enable_wflush() click to toggle source

disable call File#flush after w method

# File lib/simple_rotate/core.rb, line 156
def enable_wflush
    @enable_wflush = true
    return nil
end
error() click to toggle source

set log level ERROR @return self

# File lib/simple_rotate/core.rb, line 231
def error
    @log_level = 4
    return self
end
fatal() click to toggle source

set log level FATAL @return self

# File lib/simple_rotate/core.rb, line 224
def fatal
    @log_level = 5
    return self
end
file_closed?() click to toggle source

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
flush() click to toggle source

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
info() click to toggle source

set log level INFO @return self

# File lib/simple_rotate/core.rb, line 245
def info
    @log_level = 2
    return self
end
init(file_name=File.absolute_path($0+".log"), limit="100M", rotation=0) { || ... } click to toggle source

@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
method_missing(name, *argv) click to toggle source

return method missing error

# File lib/simple_rotate/core.rb, line 31
def method_missing(name, *argv)
    SimpleRotate::Error.missing(name)
end
no_sync_inode() click to toggle source

disable sync_inode

# File lib/simple_rotate/core.rb, line 297
def no_sync_inode
    @no_sync_inode = true
    return nil
end
no_wcheck() click to toggle source

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
psync(sleep_time=0.1) click to toggle source

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
reopen() click to toggle source

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
silence() click to toggle source

skip warning message

# File lib/simple_rotate/core.rb, line 217
def silence
    SimpleRotate::Error.silence
    return nil;
end
sync_inode() click to toggle source

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
w(log) click to toggle source

@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
Also aliased as: <<
warn() click to toggle source

set log level WORN @return self

# File lib/simple_rotate/core.rb, line 238
def warn
    @log_level = 3
    return self
end
with_stdout() click to toggle source

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

do_compress(file) click to toggle source

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
do_rotate(mode) click to toggle source

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
do_term_rotate() click to toggle source

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
exist_error(file) click to toggle source

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
gen_new_logf() click to toggle source

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_logf_generate_time() click to toggle source

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
get_trimmed_log(log) click to toggle source

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
include_defaults() click to toggle source

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
logf_not_usable() click to toggle source

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
openadd() click to toggle source

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
over_size?() click to toggle source

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
over_term?() click to toggle source

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
over_threshold?() click to toggle source

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
post_execute_rotate(after_name) click to toggle source

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
prepare_logf() click to toggle source

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
reached_limit?(mode=:NO_LOCK) click to toggle source

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
rotate_by_term?() click to toggle source

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
rotate_if() click to toggle source

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
rotation(mode=:NO_SPEC) click to toggle source

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
safe_over_size?() click to toggle source
# 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
safe_over_term?() click to toggle source
# 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
set_days_cnt_of_term() click to toggle source

@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
trim_byte(limit) click to toggle source

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
use_zlib() click to toggle source

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