diff options
author | Colby Swandale <me@colby.fyi> | 2018-12-12 14:08:35 +1100 |
---|---|---|
committer | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2019-07-15 14:43:07 +0900 |
commit | bbe157f34046ec9ef81e6ad9ed37c86a0d9155e4 (patch) | |
tree | 795ef0226ac95ede84fcee787dbe6cec19418463 /lib | |
parent | 136196785b10f7cffa1e4d557bc3bcc0e863813a (diff) | |
download | ruby-bbe157f34046ec9ef81e6ad9ed37c86a0d9155e4.tar.gz |
[ruby/logger] split logger classes/modules into separate files
https://github.com/ruby/logger/commit/f10ce9fff2
Diffstat (limited to 'lib')
-rw-r--r-- | lib/logger.gemspec | 5 | ||||
-rw-r--r-- | lib/logger.rb | 295 | ||||
-rw-r--r-- | lib/logger/errors.rb | 7 | ||||
-rw-r--r-- | lib/logger/formatter.rb | 35 | ||||
-rw-r--r-- | lib/logger/log_device.rb | 197 | ||||
-rw-r--r-- | lib/logger/period.rb | 41 | ||||
-rw-r--r-- | lib/logger/severity.rb | 17 | ||||
-rw-r--r-- | lib/logger/version.rb | 5 |
8 files changed, 308 insertions, 294 deletions
diff --git a/lib/logger.gemspec b/lib/logger.gemspec index cc453c5806..da28b629be 100644 --- a/lib/logger.gemspec +++ b/lib/logger.gemspec @@ -1,8 +1,7 @@ begin - require_relative "lib/logger" + require File.expand_path("../lib/logger/version", __FILE__) rescue LoadError - # for Ruby core repository - require_relative "logger" + require File.expand_path("../logger/version", __FILE__) end Gem::Specification.new do |spec| diff --git a/lib/logger.rb b/lib/logger.rb index 7e65af34eb..489df1dcda 100644 --- a/lib/logger.rb +++ b/lib/logger.rb @@ -11,6 +11,10 @@ # A simple system for logging messages. See Logger for more documentation. require 'monitor' +require 'logger/version' +require 'logger/formatter' +require 'logger/log_device' +require 'logger/severity' # == Description # @@ -224,7 +228,6 @@ require 'monitor' # }) # class Logger - VERSION = "1.3.0" _, name, rev = %w$Id$ if name name = name.chomp(",v") @@ -234,27 +237,6 @@ class Logger rev ||= "v#{VERSION}" ProgName = "#{name}/#{rev}".freeze - class Error < RuntimeError # :nodoc: - end - # not used after 1.2.7. just for compat. - class ShiftingError < Error # :nodoc: - end - - # Logging severity. - module Severity - # Low-level information, mostly for developers. - DEBUG = 0 - # Generic (useful) information about system operation. - INFO = 1 - # A warning. - WARN = 2 - # A handleable error condition. - ERROR = 3 - # An unhandleable error that results in a program crash. - FATAL = 4 - # An unknown message that should always be logged. - UNKNOWN = 5 - end include Severity # Logging severity threshold (e.g. <tt>Logger::INFO</tt>). @@ -596,273 +578,4 @@ private def format_message(severity, datetime, progname, msg) (@formatter || @default_formatter).call(severity, datetime, progname, msg) end - - - # Default formatter for log messages. - class Formatter - Format = "%s, [%s#%d] %5s -- %s: %s\n".freeze - - attr_accessor :datetime_format - - def initialize - @datetime_format = nil - end - - def call(severity, time, progname, msg) - Format % [severity[0..0], format_datetime(time), $$, severity, progname, - msg2str(msg)] - end - - private - - def format_datetime(time) - time.strftime(@datetime_format || "%Y-%m-%dT%H:%M:%S.%6N ".freeze) - end - - def msg2str(msg) - case msg - when ::String - msg - when ::Exception - "#{ msg.message } (#{ msg.class })\n" << - (msg.backtrace || []).join("\n") - else - msg.inspect - end - end - end - - module Period - module_function - - SiD = 24 * 60 * 60 - - def next_rotate_time(now, shift_age) - case shift_age - when 'daily' - t = Time.mktime(now.year, now.month, now.mday) + SiD - when 'weekly' - t = Time.mktime(now.year, now.month, now.mday) + SiD * (7 - now.wday) - when 'monthly' - t = Time.mktime(now.year, now.month, 1) + SiD * 32 - return Time.mktime(t.year, t.month, 1) - else - return now - end - if t.hour.nonzero? or t.min.nonzero? or t.sec.nonzero? - hour = t.hour - t = Time.mktime(t.year, t.month, t.mday) - t += SiD if hour > 12 - end - t - end - - def previous_period_end(now, shift_age) - case shift_age - when 'daily' - t = Time.mktime(now.year, now.month, now.mday) - SiD / 2 - when 'weekly' - t = Time.mktime(now.year, now.month, now.mday) - (SiD * now.wday + SiD / 2) - when 'monthly' - t = Time.mktime(now.year, now.month, 1) - SiD / 2 - else - return now - end - Time.mktime(t.year, t.month, t.mday, 23, 59, 59) - end - end - - # Device used for logging messages. - class LogDevice - include Period - - attr_reader :dev - attr_reader :filename - include MonitorMixin - - def initialize(log = nil, shift_age: nil, shift_size: nil, shift_period_suffix: nil) - @dev = @filename = @shift_age = @shift_size = @shift_period_suffix = nil - mon_initialize - set_dev(log) - if @filename - @shift_age = shift_age || 7 - @shift_size = shift_size || 1048576 - @shift_period_suffix = shift_period_suffix || '%Y%m%d' - - unless @shift_age.is_a?(Integer) - base_time = @dev.respond_to?(:stat) ? @dev.stat.mtime : Time.now - @next_rotate_time = next_rotate_time(base_time, @shift_age) - end - end - end - - def write(message) - begin - synchronize do - if @shift_age and @dev.respond_to?(:stat) - begin - check_shift_log - rescue - warn("log shifting failed. #{$!}") - end - end - begin - @dev.write(message) - rescue - warn("log writing failed. #{$!}") - end - end - rescue Exception => ignored - warn("log writing failed. #{ignored}") - end - end - - def close - begin - synchronize do - @dev.close rescue nil - end - rescue Exception - @dev.close rescue nil - end - end - - def reopen(log = nil) - # reopen the same filename if no argument, do nothing for IO - log ||= @filename if @filename - if log - synchronize do - if @filename and @dev - @dev.close rescue nil # close only file opened by Logger - @filename = nil - end - set_dev(log) - end - end - self - end - - private - - def set_dev(log) - if log.respond_to?(:write) and log.respond_to?(:close) - @dev = log - else - @dev = open_logfile(log) - @dev.sync = true - @filename = log - end - end - - def open_logfile(filename) - begin - File.open(filename, (File::WRONLY | File::APPEND)) - rescue Errno::ENOENT - create_logfile(filename) - end - end - - def create_logfile(filename) - begin - logdev = File.open(filename, (File::WRONLY | File::APPEND | File::CREAT | File::EXCL)) - logdev.flock(File::LOCK_EX) - logdev.sync = true - add_log_header(logdev) - logdev.flock(File::LOCK_UN) - rescue Errno::EEXIST - # file is created by another process - logdev = open_logfile(filename) - logdev.sync = true - end - logdev - end - - def add_log_header(file) - file.write( - "# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName] - ) if file.size == 0 - end - - def check_shift_log - if @shift_age.is_a?(Integer) - # Note: always returns false if '0'. - if @filename && (@shift_age > 0) && (@dev.stat.size > @shift_size) - lock_shift_log { shift_log_age } - end - else - now = Time.now - if now >= @next_rotate_time - @next_rotate_time = next_rotate_time(now, @shift_age) - lock_shift_log { shift_log_period(previous_period_end(now, @shift_age)) } - end - end - end - - if /mswin|mingw/ =~ RUBY_PLATFORM - def lock_shift_log - yield - end - else - def lock_shift_log - retry_limit = 8 - retry_sleep = 0.1 - begin - File.open(@filename, File::WRONLY | File::APPEND) do |lock| - lock.flock(File::LOCK_EX) # inter-process locking. will be unlocked at closing file - if File.identical?(@filename, lock) and File.identical?(lock, @dev) - yield # log shifting - else - # log shifted by another process (i-node before locking and i-node after locking are different) - @dev.close rescue nil - @dev = open_logfile(@filename) - @dev.sync = true - end - end - rescue Errno::ENOENT - # @filename file would not exist right after #rename and before #create_logfile - if retry_limit <= 0 - warn("log rotation inter-process lock failed. #{$!}") - else - sleep retry_sleep - retry_limit -= 1 - retry_sleep *= 2 - retry - end - end - rescue - warn("log rotation inter-process lock failed. #{$!}") - end - end - - def shift_log_age - (@shift_age-3).downto(0) do |i| - if FileTest.exist?("#{@filename}.#{i}") - File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}") - end - end - @dev.close rescue nil - File.rename("#{@filename}", "#{@filename}.0") - @dev = create_logfile(@filename) - return true - end - - def shift_log_period(period_end) - suffix = period_end.strftime(@shift_period_suffix) - age_file = "#{@filename}.#{suffix}" - if FileTest.exist?(age_file) - # try to avoid filename crash caused by Timestamp change. - idx = 0 - # .99 can be overridden; avoid too much file search with 'loop do' - while idx < 100 - idx += 1 - age_file = "#{@filename}.#{suffix}.#{idx}" - break unless FileTest.exist?(age_file) - end - end - @dev.close rescue nil - File.rename("#{@filename}", age_file) - @dev = create_logfile(@filename) - return true - end - end end diff --git a/lib/logger/errors.rb b/lib/logger/errors.rb new file mode 100644 index 0000000000..e0fb4c643f --- /dev/null +++ b/lib/logger/errors.rb @@ -0,0 +1,7 @@ +class Logger + class Error < RuntimeError # :nodoc: + end + # not used after 1.2.7. just for compat. + class ShiftingError < Error # :nodoc: + end +end diff --git a/lib/logger/formatter.rb b/lib/logger/formatter.rb new file mode 100644 index 0000000000..13cb1a8390 --- /dev/null +++ b/lib/logger/formatter.rb @@ -0,0 +1,35 @@ +class Logger + # Default formatter for log messages. + class Formatter + Format = "%s, [%s#%d] %5s -- %s: %s\n".freeze + + attr_accessor :datetime_format + + def initialize + @datetime_format = nil + end + + def call(severity, time, progname, msg) + Format % [severity[0..0], format_datetime(time), $$, severity, progname, + msg2str(msg)] + end + + private + + def format_datetime(time) + time.strftime(@datetime_format || "%Y-%m-%dT%H:%M:%S.%6N ".freeze) + end + + def msg2str(msg) + case msg + when ::String + msg + when ::Exception + "#{ msg.message } (#{ msg.class })\n" << + (msg.backtrace || []).join("\n") + else + msg.inspect + end + end + end +end diff --git a/lib/logger/log_device.rb b/lib/logger/log_device.rb new file mode 100644 index 0000000000..7cfb45b556 --- /dev/null +++ b/lib/logger/log_device.rb @@ -0,0 +1,197 @@ +require 'logger/period' + +class Logger + # Device used for logging messages. + class LogDevice + include Period + + attr_reader :dev + attr_reader :filename + include MonitorMixin + + def initialize(log = nil, shift_age: nil, shift_size: nil, shift_period_suffix: nil) + @dev = @filename = @shift_age = @shift_size = @shift_period_suffix = nil + mon_initialize + set_dev(log) + if @filename + @shift_age = shift_age || 7 + @shift_size = shift_size || 1048576 + @shift_period_suffix = shift_period_suffix || '%Y%m%d' + + unless @shift_age.is_a?(Integer) + base_time = @dev.respond_to?(:stat) ? @dev.stat.mtime : Time.now + @next_rotate_time = next_rotate_time(base_time, @shift_age) + end + end + end + + def write(message) + begin + synchronize do + if @shift_age and @dev.respond_to?(:stat) + begin + check_shift_log + rescue + warn("log shifting failed. #{$!}") + end + end + begin + @dev.write(message) + rescue + warn("log writing failed. #{$!}") + end + end + rescue Exception => ignored + warn("log writing failed. #{ignored}") + end + end + + def close + begin + synchronize do + @dev.close rescue nil + end + rescue Exception + @dev.close rescue nil + end + end + + def reopen(log = nil) + # reopen the same filename if no argument, do nothing for IO + log ||= @filename if @filename + if log + synchronize do + if @filename and @dev + @dev.close rescue nil # close only file opened by Logger + @filename = nil + end + set_dev(log) + end + end + self + end + + private + + def set_dev(log) + if log.respond_to?(:write) and log.respond_to?(:close) + @dev = log + else + @dev = open_logfile(log) + @dev.sync = true + @filename = log + end + end + + def open_logfile(filename) + begin + File.open(filename, (File::WRONLY | File::APPEND)) + rescue Errno::ENOENT + create_logfile(filename) + end + end + + def create_logfile(filename) + begin + logdev = File.open(filename, (File::WRONLY | File::APPEND | File::CREAT | File::EXCL)) + logdev.flock(File::LOCK_EX) + logdev.sync = true + add_log_header(logdev) + logdev.flock(File::LOCK_UN) + rescue Errno::EEXIST + # file is created by another process + logdev = open_logfile(filename) + logdev.sync = true + end + logdev + end + + def add_log_header(file) + file.write( + "# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName] + ) if file.size == 0 + end + + def check_shift_log + if @shift_age.is_a?(Integer) + # Note: always returns false if '0'. + if @filename && (@shift_age > 0) && (@dev.stat.size > @shift_size) + lock_shift_log { shift_log_age } + end + else + now = Time.now + if now >= @next_rotate_time + @next_rotate_time = next_rotate_time(now, @shift_age) + lock_shift_log { shift_log_period(previous_period_end(now, @shift_age)) } + end + end + end + + if /mswin|mingw/ =~ RUBY_PLATFORM + def lock_shift_log + yield + end + else + def lock_shift_log + retry_limit = 8 + retry_sleep = 0.1 + begin + File.open(@filename, File::WRONLY | File::APPEND) do |lock| + lock.flock(File::LOCK_EX) # inter-process locking. will be unlocked at closing file + if File.identical?(@filename, lock) and File.identical?(lock, @dev) + yield # log shifting + else + # log shifted by another process (i-node before locking and i-node after locking are different) + @dev.close rescue nil + @dev = open_logfile(@filename) + @dev.sync = true + end + end + rescue Errno::ENOENT + # @filename file would not exist right after #rename and before #create_logfile + if retry_limit <= 0 + warn("log rotation inter-process lock failed. #{$!}") + else + sleep retry_sleep + retry_limit -= 1 + retry_sleep *= 2 + retry + end + end + rescue + warn("log rotation inter-process lock failed. #{$!}") + end + end + + def shift_log_age + (@shift_age-3).downto(0) do |i| + if FileTest.exist?("#{@filename}.#{i}") + File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}") + end + end + @dev.close rescue nil + File.rename("#{@filename}", "#{@filename}.0") + @dev = create_logfile(@filename) + return true + end + + def shift_log_period(period_end) + suffix = period_end.strftime(@shift_period_suffix) + age_file = "#{@filename}.#{suffix}" + if FileTest.exist?(age_file) + # try to avoid filename crash caused by Timestamp change. + idx = 0 + # .99 can be overridden; avoid too much file search with 'loop do' + while idx < 100 + idx += 1 + age_file = "#{@filename}.#{suffix}.#{idx}" + break unless FileTest.exist?(age_file) + end + end + @dev.close rescue nil + File.rename("#{@filename}", age_file) + @dev = create_logfile(@filename) + return true + end + end +end diff --git a/lib/logger/period.rb b/lib/logger/period.rb new file mode 100644 index 0000000000..bb8cffc2f8 --- /dev/null +++ b/lib/logger/period.rb @@ -0,0 +1,41 @@ +class Logger + module Period + module_function + + SiD = 24 * 60 * 60 + + def next_rotate_time(now, shift_age) + case shift_age + when 'daily' + t = Time.mktime(now.year, now.month, now.mday) + SiD + when 'weekly' + t = Time.mktime(now.year, now.month, now.mday) + SiD * (7 - now.wday) + when 'monthly' + t = Time.mktime(now.year, now.month, 1) + SiD * 32 + return Time.mktime(t.year, t.month, 1) + else + return now + end + if t.hour.nonzero? or t.min.nonzero? or t.sec.nonzero? + hour = t.hour + t = Time.mktime(t.year, t.month, t.mday) + t += SiD if hour > 12 + end + t + end + + def previous_period_end(now, shift_age) + case shift_age + when 'daily' + t = Time.mktime(now.year, now.month, now.mday) - SiD / 2 + when 'weekly' + t = Time.mktime(now.year, now.month, now.mday) - (SiD * now.wday + SiD / 2) + when 'monthly' + t = Time.mktime(now.year, now.month, 1) - SiD / 2 + else + return now + end + Time.mktime(t.year, t.month, t.mday, 23, 59, 59) + end + end +end diff --git a/lib/logger/severity.rb b/lib/logger/severity.rb new file mode 100644 index 0000000000..c8179cb017 --- /dev/null +++ b/lib/logger/severity.rb @@ -0,0 +1,17 @@ +class Logger + # Logging severity. + module Severity + # Low-level information, mostly for developers. + DEBUG = 0 + # Generic (useful) information about system operation. + INFO = 1 + # A warning. + WARN = 2 + # A handleable error condition. + ERROR = 3 + # An unhandleable error that results in a program crash. + FATAL = 4 + # An unknown message that should always be logged. + UNKNOWN = 5 + end +end diff --git a/lib/logger/version.rb b/lib/logger/version.rb new file mode 100644 index 0000000000..af122736c3 --- /dev/null +++ b/lib/logger/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class Logger + VERSION = "1.3.0" +end |