# logger.rb - simple logging utility # Copyright (C) 2000-2003, 2005, 2008, 2011 NAKAMURA, Hiroshi . # # Documentation:: NAKAMURA, Hiroshi and Gavin Sinclair # License:: # You can redistribute it and/or modify it under the same terms of Ruby's # license; either the dual license version in 2003, or any later version. # Revision:: $Id: logger.rb 31641 2011-05-19 00:07:25Z nobu $ # # A simple system for logging messages. See Logger for more documentation. require 'monitor' require 'fileutils' # == Description # # The Logger class provides a simple but sophisticated logging utility that # you can use to output messages. # # The messages have associated levels, such as +INFO+ or +ERROR+ that indicate # their importance. You can then give the Logger a level, and only messages # at that level of higher will be printed. # # The levels are: # # +FATAL+:: an unhandleable error that results in a program crash # +ERROR+:: a handleable error condition # +WARN+:: a warning # +INFO+:: generic (useful) information about system operation # +DEBUG+:: low-level information for developers # # For instance, in a production system, you may have your Logger set to # +INFO+ or even +WARN+ # When you are developing the system, however, you probably # want to know about the program's internal state, and would set the Logger to # +DEBUG+. # # *Note*: Logger does not escape or sanitize any messages passed to it. # Developers should be aware of when potentially malicious data (user-input) # is passed to Logger, and manually escape the untrusted data: # # logger.info("User-input: #{input.dump}") # logger.info("User-input: %p" % input) # # You can use #formatter= for escaping all data. # # original_formatter = Logger::Formatter.new # logger.formatter = proc { |severity, datetime, progname, msg| # original_formatter.call(severity, datetime, progname, msg.dump) # } # logger.info(input) # # === Example # # This creates a logger to the standard output stream, with a level of +WARN+ # # log = Logger.new(STDOUT) # log.level = Logger::WARN # # log.debug("Created logger") # log.info("Program started") # log.warn("Nothing to do!") # # begin # File.each_line(path) do |line| # unless line =~ /^(\w+) = (.*)$/ # log.error("Line in wrong format: #{line}") # end # end # rescue => err # log.fatal("Caught exception; exiting") # log.fatal(err) # end # # Because the Logger's level is set to +WARN+, only the warning, error, and # fatal messages are recorded. The debug and info messages are silently # discarded. # # === Features # # There are several interesting features that Logger provides, like # auto-rolling of log files, setting the format of log messages, and # specifying a program name in conjunction with the message. The next section # shows you how to achieve these things. # # # == HOWTOs # # === How to create a logger # # The options below give you various choices, in more or less increasing # complexity. # # 1. Create a logger which logs messages to STDERR/STDOUT. # # logger = Logger.new(STDERR) # logger = Logger.new(STDOUT) # # 2. Create a logger for the file which has the specified name. # # logger = Logger.new('logfile.log') # # 3. Create a logger for the specified file. # # file = File.open('foo.log', File::WRONLY | File::APPEND) # # To create new (and to remove old) logfile, add File::CREAT like; # # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT) # logger = Logger.new(file) # # 4. Create a logger which ages logfile once it reaches a certain size. Leave # 10 "old log files" and each file is about 1,024,000 bytes. # # logger = Logger.new('foo.log', 10, 1024000) # # 5. Create a logger which ages logfile daily/weekly/monthly. # # logger = Logger.new('foo.log', 'daily') # logger = Logger.new('foo.log', 'weekly') # logger = Logger.new('foo.log', 'monthly') # # === How to log a message # # Notice the different methods (+fatal+, +error+, +info+) being used to log # messages of various levels? Other methods in this family are +warn+ and # +debug+. +add+ is used below to log a message of an arbitrary (perhaps # dynamic) level. # # 1. Message in block. # # logger.fatal { "Argument 'foo' not given." } # # 2. Message as a string. # # logger.error "Argument #{ @foo } mismatch." # # 3. With progname. # # logger.info('initialize') { "Initializing..." } # # 4. With severity. # # logger.add(Logger::FATAL) { 'Fatal error!' } # # The block form allows you to create potentially complex log messages, # but to delay their evaluation until and unless the message is # logged. For example, if we have the following: # # logger.debug { "This is a " + potentially + " expensive operation" } # # If the logger's level is +INFO+ or higher, no debug messages will be logged, # and the entire block will not even be evaluated. Compare to this: # # logger.debug("This is a " + potentially + " expensive operation") # # Here, the string concatenation is done every time, even if the log # level is not set to show the debug message. # # === How to close a logger # # logger.close # # === Setting severity threshold # # 1. Original interface. # # logger.sev_threshold = Logger::WARN # # 2. Log4r (somewhat) compatible interface. # # logger.level = Logger::INFO # # DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN # # # == Format # # Log messages are rendered in the output stream in a certain format by # default. The default format and a sample are shown below: # # Log format: # SeverityID, [Date Time mSec #pid] SeverityLabel -- ProgName: message # # Log sample: # I, [Wed Mar 03 02:34:24 JST 1999 895701 #19074] INFO -- Main: info. # # You may change the date and time format via #datetime_format= # # logger.datetime_format = "%Y-%m-%d %H:%M:%S" # # e.g. "2004-01-03 00:54:26" # # Or, you may change the overall format with #formatter= method. # # logger.formatter = proc do |severity, datetime, progname, msg| # "#{datetime}: #{msg}\n" # end # # e.g. "Thu Sep 22 08:51:08 GMT+9:00 2005: hello world" # # class Logger # #具体内容请看https://bugs.ruby-lang.org/issues/7303 # # Device used for logging messages. # class LogDevice # def shift_log_period(period_end) # postfix = period_end.strftime("%Y%m%d") # YYYYMMDD # age_file = "#{@filename}.#{postfix}" # 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}.#{postfix}.#{idx}" # break unless FileTest.exist?(age_file) # end # end # # @dev.close rescue nil # # File.rename("#{@filename}", age_file) # # @dev = create_logfile(@filename) # #覆盖原来lib库的方法,将上边三行删除,增加下边两行 # FileUtils.cp(@filename, age_file) # reset_logfile(@dev) # see below for this new method return true # return true # end # #打开原来lib库,新增一个方法 # def reset_logfile(logdev) # logdev.truncate( 0 ) # logdev.sync = true # add_log_header(logdev) # end # end # end