mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
[ruby/logger] split logger classes/modules into separate files
https://github.com/ruby/logger/commit/f10ce9fff2
This commit is contained in:
parent
136196785b
commit
bbe157f340
8 changed files with 308 additions and 294 deletions
|
@ -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|
|
||||
|
|
295
lib/logger.rb
295
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
|
||||
|
|
7
lib/logger/errors.rb
Normal file
7
lib/logger/errors.rb
Normal file
|
@ -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
|
35
lib/logger/formatter.rb
Normal file
35
lib/logger/formatter.rb
Normal file
|
@ -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
|
197
lib/logger/log_device.rb
Normal file
197
lib/logger/log_device.rb
Normal file
|
@ -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
|
41
lib/logger/period.rb
Normal file
41
lib/logger/period.rb
Normal file
|
@ -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
|
17
lib/logger/severity.rb
Normal file
17
lib/logger/severity.rb
Normal file
|
@ -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
|
5
lib/logger/version.rb
Normal file
5
lib/logger/version.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Logger
|
||||
VERSION = "1.3.0"
|
||||
end
|
Loading…
Reference in a new issue