1111# A simple system for logging messages. See Logger for more documentation.
1212
1313require 'monitor'
14+ require 'logger/version'
15+ require 'logger/formatter'
16+ require 'logger/log_device'
17+ require 'logger/severity'
1418
1519# == Description
1620#
224228# })
225229#
226230class Logger
227- VERSION = "1.3.0"
228231 _ , name , rev = %w$ Id $
229232 if name
230233 name = name . chomp ( ",v" )
@@ -234,27 +237,6 @@ class Logger
234237 rev ||= "v#{ VERSION } "
235238 ProgName = "#{ name } /#{ rev } " . freeze
236239
237- class Error < RuntimeError # :nodoc:
238- end
239- # not used after 1.2.7. just for compat.
240- class ShiftingError < Error # :nodoc:
241- end
242-
243- # Logging severity.
244- module Severity
245- # Low-level information, mostly for developers.
246- DEBUG = 0
247- # Generic (useful) information about system operation.
248- INFO = 1
249- # A warning.
250- WARN = 2
251- # A handleable error condition.
252- ERROR = 3
253- # An unhandleable error that results in a program crash.
254- FATAL = 4
255- # An unknown message that should always be logged.
256- UNKNOWN = 5
257- end
258240 include Severity
259241
260242 # Logging severity threshold (e.g. <tt>Logger::INFO</tt>).
@@ -596,273 +578,4 @@ def format_severity(severity)
596578 def format_message ( severity , datetime , progname , msg )
597579 ( @formatter || @default_formatter ) . call ( severity , datetime , progname , msg )
598580 end
599-
600-
601- # Default formatter for log messages.
602- class Formatter
603- Format = "%s, [%s#%d] %5s -- %s: %s\n " . freeze
604-
605- attr_accessor :datetime_format
606-
607- def initialize
608- @datetime_format = nil
609- end
610-
611- def call ( severity , time , progname , msg )
612- Format % [ severity [ 0 ..0 ] , format_datetime ( time ) , $$, severity , progname ,
613- msg2str ( msg ) ]
614- end
615-
616- private
617-
618- def format_datetime ( time )
619- time . strftime ( @datetime_format || "%Y-%m-%dT%H:%M:%S.%6N " . freeze )
620- end
621-
622- def msg2str ( msg )
623- case msg
624- when ::String
625- msg
626- when ::Exception
627- "#{ msg . message } (#{ msg . class } )\n " <<
628- ( msg . backtrace || [ ] ) . join ( "\n " )
629- else
630- msg . inspect
631- end
632- end
633- end
634-
635- module Period
636- module_function
637-
638- SiD = 24 * 60 * 60
639-
640- def next_rotate_time ( now , shift_age )
641- case shift_age
642- when 'daily'
643- t = Time . mktime ( now . year , now . month , now . mday ) + SiD
644- when 'weekly'
645- t = Time . mktime ( now . year , now . month , now . mday ) + SiD * ( 7 - now . wday )
646- when 'monthly'
647- t = Time . mktime ( now . year , now . month , 1 ) + SiD * 32
648- return Time . mktime ( t . year , t . month , 1 )
649- else
650- return now
651- end
652- if t . hour . nonzero? or t . min . nonzero? or t . sec . nonzero?
653- hour = t . hour
654- t = Time . mktime ( t . year , t . month , t . mday )
655- t += SiD if hour > 12
656- end
657- t
658- end
659-
660- def previous_period_end ( now , shift_age )
661- case shift_age
662- when 'daily'
663- t = Time . mktime ( now . year , now . month , now . mday ) - SiD / 2
664- when 'weekly'
665- t = Time . mktime ( now . year , now . month , now . mday ) - ( SiD * now . wday + SiD / 2 )
666- when 'monthly'
667- t = Time . mktime ( now . year , now . month , 1 ) - SiD / 2
668- else
669- return now
670- end
671- Time . mktime ( t . year , t . month , t . mday , 23 , 59 , 59 )
672- end
673- end
674-
675- # Device used for logging messages.
676- class LogDevice
677- include Period
678-
679- attr_reader :dev
680- attr_reader :filename
681- include MonitorMixin
682-
683- def initialize ( log = nil , shift_age : nil , shift_size : nil , shift_period_suffix : nil )
684- @dev = @filename = @shift_age = @shift_size = @shift_period_suffix = nil
685- mon_initialize
686- set_dev ( log )
687- if @filename
688- @shift_age = shift_age || 7
689- @shift_size = shift_size || 1048576
690- @shift_period_suffix = shift_period_suffix || '%Y%m%d'
691-
692- unless @shift_age . is_a? ( Integer )
693- base_time = @dev . respond_to? ( :stat ) ? @dev . stat . mtime : Time . now
694- @next_rotate_time = next_rotate_time ( base_time , @shift_age )
695- end
696- end
697- end
698-
699- def write ( message )
700- begin
701- synchronize do
702- if @shift_age and @dev . respond_to? ( :stat )
703- begin
704- check_shift_log
705- rescue
706- warn ( "log shifting failed. #{ $!} " )
707- end
708- end
709- begin
710- @dev . write ( message )
711- rescue
712- warn ( "log writing failed. #{ $!} " )
713- end
714- end
715- rescue Exception => ignored
716- warn ( "log writing failed. #{ ignored } " )
717- end
718- end
719-
720- def close
721- begin
722- synchronize do
723- @dev . close rescue nil
724- end
725- rescue Exception
726- @dev . close rescue nil
727- end
728- end
729-
730- def reopen ( log = nil )
731- # reopen the same filename if no argument, do nothing for IO
732- log ||= @filename if @filename
733- if log
734- synchronize do
735- if @filename and @dev
736- @dev . close rescue nil # close only file opened by Logger
737- @filename = nil
738- end
739- set_dev ( log )
740- end
741- end
742- self
743- end
744-
745- private
746-
747- def set_dev ( log )
748- if log . respond_to? ( :write ) and log . respond_to? ( :close )
749- @dev = log
750- else
751- @dev = open_logfile ( log )
752- @dev . sync = true
753- @filename = log
754- end
755- end
756-
757- def open_logfile ( filename )
758- begin
759- File . open ( filename , ( File ::WRONLY | File ::APPEND ) )
760- rescue Errno ::ENOENT
761- create_logfile ( filename )
762- end
763- end
764-
765- def create_logfile ( filename )
766- begin
767- logdev = File . open ( filename , ( File ::WRONLY | File ::APPEND | File ::CREAT | File ::EXCL ) )
768- logdev . flock ( File ::LOCK_EX )
769- logdev . sync = true
770- add_log_header ( logdev )
771- logdev . flock ( File ::LOCK_UN )
772- rescue Errno ::EEXIST
773- # file is created by another process
774- logdev = open_logfile ( filename )
775- logdev . sync = true
776- end
777- logdev
778- end
779-
780- def add_log_header ( file )
781- file . write (
782- "# Logfile created on %s by %s\n " % [ Time . now . to_s , Logger ::ProgName ]
783- ) if file . size == 0
784- end
785-
786- def check_shift_log
787- if @shift_age . is_a? ( Integer )
788- # Note: always returns false if '0'.
789- if @filename && ( @shift_age > 0 ) && ( @dev . stat . size > @shift_size )
790- lock_shift_log { shift_log_age }
791- end
792- else
793- now = Time . now
794- if now >= @next_rotate_time
795- @next_rotate_time = next_rotate_time ( now , @shift_age )
796- lock_shift_log { shift_log_period ( previous_period_end ( now , @shift_age ) ) }
797- end
798- end
799- end
800-
801- if /mswin|mingw/ =~ RUBY_PLATFORM
802- def lock_shift_log
803- yield
804- end
805- else
806- def lock_shift_log
807- retry_limit = 8
808- retry_sleep = 0.1
809- begin
810- File . open ( @filename , File ::WRONLY | File ::APPEND ) do |lock |
811- lock . flock ( File ::LOCK_EX ) # inter-process locking. will be unlocked at closing file
812- if File . identical? ( @filename , lock ) and File . identical? ( lock , @dev )
813- yield # log shifting
814- else
815- # log shifted by another process (i-node before locking and i-node after locking are different)
816- @dev . close rescue nil
817- @dev = open_logfile ( @filename )
818- @dev . sync = true
819- end
820- end
821- rescue Errno ::ENOENT
822- # @filename file would not exist right after #rename and before #create_logfile
823- if retry_limit <= 0
824- warn ( "log rotation inter-process lock failed. #{ $!} " )
825- else
826- sleep retry_sleep
827- retry_limit -= 1
828- retry_sleep *= 2
829- retry
830- end
831- end
832- rescue
833- warn ( "log rotation inter-process lock failed. #{ $!} " )
834- end
835- end
836-
837- def shift_log_age
838- ( @shift_age -3 ) . downto ( 0 ) do |i |
839- if FileTest . exist? ( "#{ @filename } .#{ i } " )
840- File . rename ( "#{ @filename } .#{ i } " , "#{ @filename } .#{ i +1 } " )
841- end
842- end
843- @dev . close rescue nil
844- File . rename ( "#{ @filename } " , "#{ @filename } .0" )
845- @dev = create_logfile ( @filename )
846- return true
847- end
848-
849- def shift_log_period ( period_end )
850- suffix = period_end . strftime ( @shift_period_suffix )
851- age_file = "#{ @filename } .#{ suffix } "
852- if FileTest . exist? ( age_file )
853- # try to avoid filename crash caused by Timestamp change.
854- idx = 0
855- # .99 can be overridden; avoid too much file search with 'loop do'
856- while idx < 100
857- idx += 1
858- age_file = "#{ @filename } .#{ suffix } .#{ idx } "
859- break unless FileTest . exist? ( age_file )
860- end
861- end
862- @dev . close rescue nil
863- File . rename ( "#{ @filename } " , age_file )
864- @dev = create_logfile ( @filename )
865- return true
866- end
867- end
868581end
0 commit comments