11
11
# A simple system for logging messages. See Logger for more documentation.
12
12
13
13
require 'monitor'
14
+ require 'logger/version'
15
+ require 'logger/formatter'
16
+ require 'logger/log_device'
17
+ require 'logger/severity'
14
18
15
19
# == Description
16
20
#
224
228
# })
225
229
#
226
230
class Logger
227
- VERSION = "1.3.0"
228
231
_ , name , rev = %w$ Id $
229
232
if name
230
233
name = name . chomp ( ",v" )
@@ -234,27 +237,6 @@ class Logger
234
237
rev ||= "v#{ VERSION } "
235
238
ProgName = "#{ name } /#{ rev } " . freeze
236
239
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
258
240
include Severity
259
241
260
242
# Logging severity threshold (e.g. <tt>Logger::INFO</tt>).
@@ -596,273 +578,4 @@ def format_severity(severity)
596
578
def format_message ( severity , datetime , progname , msg )
597
579
( @formatter || @default_formatter ) . call ( severity , datetime , progname , msg )
598
580
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
868
581
end
0 commit comments