1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* lib/logger.rb: imported upstream version (logger/1.2.7) see #2238.

* do not raise an exception even if log writing failed.
          * do not raise ShiftingError if an aged file already exists.
            (no ShiftingError will be raised from 1.2.7, just warn() instead)
        * test/logger/test_logger.rb: ditto.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25413 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nahi 2009-10-20 15:08:38 +00:00
parent 9eebf6bf48
commit 8906bf2c1b
3 changed files with 205 additions and 55 deletions

View file

@ -1,3 +1,11 @@
水 10月 21 00:17:28 2009 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
* lib/logger.rb: imported upstream version (logger/1.2.7) see #2238.
* do not raise an exception even if log writing failed.
* do not raise ShiftingError if an aged file already exists.
(no ShiftingError will be raised from 1.2.7, just warn() instead)
* test/logger/test_logger.rb: ditto.
Tue Oct 20 22:29:06 2009 Keiju Ishitsuka <keiju@ruby-lang.org> Tue Oct 20 22:29:06 2009 Keiju Ishitsuka <keiju@ruby-lang.org>
* lib/matrix.rb: Bug fix. See detail [ruby-core:23598]. * lib/matrix.rb: Bug fix. See detail [ruby-core:23598].

View file

@ -1,11 +1,5 @@
# logger.rb - simple logging utility # logger.rb - simple logging utility
# Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>. # Copyright (C) 2000-2003, 2005, 2008 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
require 'monitor'
# = logger.rb
#
# Simple logging utility.
# #
# Author:: NAKAMURA, Hiroshi <nakahiro@sarion.co.jp> # Author:: NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
# Documentation:: NAKAMURA, Hiroshi and Gavin Sinclair # Documentation:: NAKAMURA, Hiroshi and Gavin Sinclair
@ -15,10 +9,11 @@ require 'monitor'
# Revision:: $Id$ # Revision:: $Id$
# #
# See Logger for documentation. # See Logger for documentation.
#
# require 'monitor'
# == Description # == Description
# #
# The Logger class provides a simple but sophisticated logging utility that # The Logger class provides a simple but sophisticated logging utility that
@ -180,7 +175,7 @@ require 'monitor'
class Logger class Logger
VERSION = "1.2.6" VERSION = "1.2.7"
id, name, rev = %w$Id$ id, name, rev = %w$Id$
if name if name
name = name.chomp(",v") name = name.chomp(",v")
@ -191,7 +186,7 @@ class Logger
ProgName = "#{name}/#{rev}" ProgName = "#{name}/#{rev}"
class Error < RuntimeError; end class Error < RuntimeError; end
class ShiftingError < Error; end class ShiftingError < Error; end # not used after 1.2.7. just for compat.
# Logging severity. # Logging severity.
module Severity module Severity
@ -334,10 +329,10 @@ class Logger
progname ||= @progname progname ||= @progname
if message.nil? if message.nil?
if block_given? if block_given?
message = yield message = yield
else else
message = progname message = progname
progname = @progname progname = @progname
end end
end end
@logdev.write( @logdev.write(
@ -499,32 +494,44 @@ private
@dev = @filename = @shift_age = @shift_size = nil @dev = @filename = @shift_age = @shift_size = nil
@mutex = LogDeviceMutex.new @mutex = LogDeviceMutex.new
if log.respond_to?(:write) and log.respond_to?(:close) if log.respond_to?(:write) and log.respond_to?(:close)
@dev = log @dev = log
else else
@dev = open_logfile(log) @dev = open_logfile(log)
@dev.sync = true @dev.sync = true
@filename = log @filename = log
@shift_age = opt[:shift_age] || 7 @shift_age = opt[:shift_age] || 7
@shift_size = opt[:shift_size] || 1048576 @shift_size = opt[:shift_size] || 1048576
end end
end end
def write(message) def write(message)
@mutex.synchronize do begin
if @shift_age and @dev.respond_to?(:stat) @mutex.synchronize do
if @shift_age and @dev.respond_to?(:stat)
begin
check_shift_log
rescue
warn("log shifting failed. #{$!}")
end
end
begin begin
check_shift_log @dev.write(message)
rescue rescue
raise Logger::ShiftingError.new("Shifting failed. #{$!}") warn("log writing failed. #{$!}")
end end
end end
@dev.write(message) rescue Exception => ignored
warn("log writing failed. #{ignored}")
end end
end end
def close def close
@mutex.synchronize do begin
@dev.close @mutex.synchronize do
@dev.close rescue nil
end
rescue Exception => ignored
@dev.close rescue nil
end end
end end
@ -532,9 +539,9 @@ private
def open_logfile(filename) def open_logfile(filename)
if (FileTest.exist?(filename)) if (FileTest.exist?(filename))
open(filename, (File::WRONLY | File::APPEND)) open(filename, (File::WRONLY | File::APPEND))
else else
create_logfile(filename) create_logfile(filename)
end end
end end
@ -547,8 +554,8 @@ private
def add_log_header(file) def add_log_header(file)
file.write( file.write(
"# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName] "# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
) )
end end
SiD = 24 * 60 * 60 SiD = 24 * 60 * 60
@ -561,8 +568,9 @@ private
end end
else else
now = Time.now now = Time.now
if @dev.stat.mtime <= previous_period_end(now) period_end = previous_period_end(now)
shift_log_period(now) if @dev.stat.mtime <= period_end
shift_log_period(period_end)
end end
end end
end end
@ -573,19 +581,26 @@ private
File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}") File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}")
end end
end end
@dev.close @dev.close rescue nil
File.rename("#{@filename}", "#{@filename}.0") File.rename("#{@filename}", "#{@filename}.0")
@dev = create_logfile(@filename) @dev = create_logfile(@filename)
return true return true
end end
def shift_log_period(now) def shift_log_period(period_end)
postfix = previous_period_end(now).strftime("%Y%m%d") # YYYYMMDD postfix = period_end.strftime("%Y%m%d") # YYYYMMDD
age_file = "#{@filename}.#{postfix}" age_file = "#{@filename}.#{postfix}"
if FileTest.exist?(age_file) if FileTest.exist?(age_file)
raise RuntimeError.new("'#{ age_file }' already exists.") # try to avoid filename crash caused by Timestamp change.
idx = 0
# .99 can be overriden; avoid too much file search with 'loop do'
while idx < 100
idx += 1
age_file = "#{@filename}.#{postfix}.#{idx}"
break unless FileTest.exist?(age_file)
end
end end
@dev.close @dev.close rescue nil
File.rename("#{@filename}", age_file) File.rename("#{@filename}", age_file)
@dev = create_logfile(@filename) @dev = create_logfile(@filename)
return true return true
@ -672,12 +687,12 @@ private
def start def start
status = -1 status = -1
begin begin
log(INFO, "Start of #{ @appname }.") log(INFO, "Start of #{ @appname }.")
status = run status = run
rescue rescue
log(FATAL, "Detected an exception. Stopping ... #{$!} (#{$!.class})\n" << $@.join("\n")) log(FATAL, "Detected an exception. Stopping ... #{$!} (#{$!.class})\n" << $@.join("\n"))
ensure ensure
log(INFO, "End of #{ @appname }. (status: #{ status.to_s })") log(INFO, "End of #{ @appname }. (status: #{ status.to_s })")
end end
status status
end end
@ -692,6 +707,8 @@ private
# #
def logger=(logger) def logger=(logger)
@log = logger @log = logger
@log.progname = @appname
@log.level = @level
end end
# #
@ -726,6 +743,7 @@ private
private private
def run def run
# TODO: should be an NotImplementedError
raise RuntimeError.new('Method run must be defined in the derived class.') raise RuntimeError.new('Method run must be defined in the derived class.')
end end
end end

View file

@ -6,9 +6,9 @@ require 'tempfile'
class TestLoggerSeverity < Test::Unit::TestCase class TestLoggerSeverity < Test::Unit::TestCase
def test_enum def test_enum
logger_levels = Logger.constants logger_levels = Logger.constants
levels = [:WARN, :UNKNOWN, :INFO, :FATAL, :DEBUG, :ERROR] levels = ["WARN", "UNKNOWN", "INFO", "FATAL", "DEBUG", "ERROR"]
Logger::Severity.constants.each do |level| Logger::Severity.constants.each do |level|
assert(levels.include?(level)) assert(levels.include?(level.to_s))
assert(logger_levels.include?(level)) assert(logger_levels.include?(level))
end end
assert_equal(levels.size, Logger::Severity.constants.size) assert_equal(levels.size, Logger::Severity.constants.size)
@ -21,16 +21,19 @@ class TestLogger < Test::Unit::TestCase
def setup def setup
@logger = Logger.new(nil) @logger = Logger.new(nil)
@filename = __FILE__ + ".#{$$}"
end end
def test_const_progname def teardown
assert %r!\Alogger\.rb/\S+\z! === Logger::ProgName unless $DEBUG
File.unlink(@filename) if File.exist?(@filename)
end
end end
class Log class Log
attr_reader :label, :datetime, :pid, :severity, :progname, :msg attr_reader :label, :datetime, :pid, :severity, :progname, :msg
def initialize(line) def initialize(line)
/\A(\w+), \[([^#]*)#(\d+)\]\s+(\w+) -- (\w*): ([\x0-\xff]*)/n =~ line /\A(\w+), \[([^#]*)#(\d+)\]\s+(\w+) -- (\w*): ([\x0-\xff]*)/ =~ line
@label, @datetime, @pid, @severity, @progname, @msg = $1, $2, $3, $4, $5, $6 @label, @datetime, @pid, @severity, @progname, @msg = $1, $2, $3, $4, $5, $6
end end
end end
@ -262,28 +265,56 @@ class TestLogger < Test::Unit::TestCase
end end
class TestLogDevice < Test::Unit::TestCase class TestLogDevice < Test::Unit::TestCase
def d(log) class LogExcnRaiser
Logger::LogDevice.new(log) def write(*arg)
raise 'disk is full'
end
def close
end
def stat
Object.new
end
end
def setup
@filename = __FILE__ + ".#{$$}"
end
def teardown
unless $DEBUG
File.unlink(@filename) if File.exist?(@filename)
end
end
def d(log, opt = {})
Logger::LogDevice.new(log, opt)
end end
def test_initialize def test_initialize
logdev = d(STDERR) logdev = d(STDERR)
assert_equal(STDERR, logdev.dev) assert_equal(STDERR, logdev.dev)
assert_nil(logdev.filename) assert_nil(logdev.filename)
assert_raise(TypeError) do assert_raises(TypeError) do
d(nil) d(nil)
end end
# #
filename = __FILE__ + ".#{$$}" logdev = d(@filename)
begin begin
logdev = d(filename) assert(File.exist?(@filename))
assert(File.exist?(filename))
assert(logdev.dev.sync) assert(logdev.dev.sync)
assert_equal(filename, logdev.filename) assert_equal(@filename, logdev.filename)
logdev.write('hello')
ensure ensure
logdev.close logdev.close
File.unlink(filename)
end end
# create logfile whitch is already exist.
logdev = d(@filename)
logdev.write('world')
logfile = File.read(@filename)
assert_equal(2, logfile.split(/\n/).size)
assert_match(/^helloworld$/, logfile)
end end
def test_write def test_write
@ -295,6 +326,15 @@ class TestLogDevice < Test::Unit::TestCase
msg = r.read msg = r.read
r.close r.close
assert_equal("msg2\n\n", msg) assert_equal("msg2\n\n", msg)
#
logdev = d(LogExcnRaiser.new)
begin
assert_nothing_raised do
logdev.write('hello')
end
ensure
logdev.close
end
end end
def test_close def test_close
@ -377,4 +417,88 @@ class TestLogDevice < Test::Unit::TestCase
File.unlink(logfile1) File.unlink(logfile1)
File.unlink(logfile2) File.unlink(logfile2)
end end
def test_shifting_age_variants
logger = Logger.new(@filename, 'daily')
logger.info('daily')
logger.close
logger = Logger.new(@filename, 'weekly')
logger.info('weekly')
logger.close
logger = Logger.new(@filename, 'monthly')
logger.info('monthly')
logger.close
end
def test_shifting_age
# shift_age other than 'daily', 'weekly', and 'monthly' means 'everytime'
yyyymmdd = Time.now.strftime("%Y%m%d")
filename1 = @filename + ".#{yyyymmdd}"
filename2 = @filename + ".#{yyyymmdd}.1"
filename3 = @filename + ".#{yyyymmdd}.2"
begin
logger = Logger.new(@filename, 'now')
assert(File.exist?(@filename))
assert(!File.exist?(filename1))
assert(!File.exist?(filename2))
assert(!File.exist?(filename3))
logger.info("0" * 15)
assert(File.exist?(@filename))
assert(File.exist?(filename1))
assert(!File.exist?(filename2))
assert(!File.exist?(filename3))
logger.warn("0" * 15)
assert(File.exist?(@filename))
assert(File.exist?(filename1))
assert(File.exist?(filename2))
assert(!File.exist?(filename3))
logger.error("0" * 15)
assert(File.exist?(@filename))
assert(File.exist?(filename1))
assert(File.exist?(filename2))
assert(File.exist?(filename3))
ensure
[filename1, filename2, filename3].each do |filename|
File.unlink(filename) if File.exist?(filename)
end
end
end
end
class TestLoggerApplication < Test::Unit::TestCase
def setup
@app = Logger::Application.new('appname')
@filename = __FILE__ + ".#{$$}"
end
def teardown
unless $DEBUG
File.unlink(@filename) if File.exist?(@filename)
end
end
def test_initialize
app = Logger::Application.new('appname')
assert_equal('appname', app.appname)
end
def test_start
@app.set_log(@filename)
@app.level = Logger::UNKNOWN
@app.start # logs FATAL log
assert_equal(1, File.read(@filename).split(/\n/).size)
end
def test_logger
@app.level = Logger::WARN
@app.set_log(@filename)
assert_equal(Logger::WARN, @app.logger.level)
@app.logger = logger = Logger.new(STDOUT)
assert_equal(logger, @app.logger)
assert_equal(Logger::WARN, @app.logger.level)
@app.log = @filename
assert(logger != @app.logger)
assert_equal(Logger::WARN, @app.logger.level)
end
end end