class Logger

Logger 类提供了一个简单但功能强大的日志实用程序,你可以使用它为你的程序创建一个或多个事件日志。每个这样的日志都包含一个按时间顺序排列的条目序列,记录了程序的活动。

关于示例

此页面上的所有示例都假设已经 require 了 Logger

require 'logger'

概要

使用 Logger.new 创建日志

# Single log file.
logger = Logger.new('t.log')
# Size-based rotated logging: 3 10-megabyte files.
logger = Logger.new('t.log', 3, 10485760)
# Period-based rotated logging: daily (also allowed: 'weekly', 'monthly').
logger = Logger.new('t.log', 'daily')
# Log to an IO stream.
logger = Logger.new($stdout)

使用 Logger#add 添加条目(级别,消息)

logger.add(Logger::DEBUG, 'Maximal debugging info')
logger.add(Logger::INFO, 'Non-error information')
logger.add(Logger::WARN, 'Non-error warning')
logger.add(Logger::ERROR, 'Non-fatal error')
logger.add(Logger::FATAL, 'Fatal error')
logger.add(Logger::UNKNOWN, 'Most severe')

使用 Logger#close 关闭日志

logger.close

条目

你可以使用方法 Logger#add 添加条目

logger.add(Logger::DEBUG, 'Maximal debugging info')
logger.add(Logger::INFO, 'Non-error information')
logger.add(Logger::WARN, 'Non-error warning')
logger.add(Logger::ERROR, 'Non-fatal error')
logger.add(Logger::FATAL, 'Fatal error')
logger.add(Logger::UNKNOWN, 'Most severe')

这些简写方法也添加条目

logger.debug('Maximal debugging info')
logger.info('Non-error information')
logger.warn('Non-error warning')
logger.error('Non-fatal error')
logger.fatal('Fatal error')
logger.unknown('Most severe')

当你调用这些方法中的任何一个时,条目可能会或可能不会写入日志,具体取决于条目的严重程度和日志级别;请参阅日志级别

一个条目始终具有

  • 严重程度(add 的必需参数)。

  • 自动创建的时间戳。

并且还可能具有

  • 消息。

  • 程序名称。

示例

logger = Logger.new($stdout)
logger.add(Logger::INFO, 'My message.', 'mung')
# => I, [2022-05-07T17:21:46.536234 #20536]  INFO -- mung: My message.

条目的默认格式是

"%s, [%s #%d] %5s -- %s: %s\n"

其中要格式化的值是

  • 严重程度(一个字母)。

  • 时间戳。

  • 进程 ID。

  • 严重程度(单词)。

  • 程序名称。

  • 消息。

你可以使用不同的条目格式,方法是

  • 设置自定义格式 proc(影响后续条目);请参阅 formatter=

  • 使用块调用上述任何方法(仅影响一个条目)。这样做有两个好处

    • 上下文:该块可以评估整个程序上下文并创建依赖于上下文的消息。

    • 性能:除非日志级别允许实际写入条目,否则不会评估该块

      logger.error { my_slow_message_generator }
      

      将此与字符串形式进行对比,字符串形式始终会进行评估,无论日志级别如何

      logger.error("#{my_slow_message_generator}")
      

严重程度

日志条目的严重程度有两个影响

  • 确定是否选择将条目包含在日志中;请参阅日志级别

  • 向任何日志读取器(无论是人还是程序)指示条目的相对重要性。

时间戳

日志条目的时间戳是在创建条目时自动生成的。

记录的时间戳由方法 Time#strftime 使用此格式字符串格式化

'%Y-%m-%dT%H:%M:%S.%6N'

示例

logger = Logger.new($stdout)
logger.add(Logger::INFO)
# => I, [2022-05-07T17:04:32.318331 #20536]  INFO -- : nil

你可以使用方法 datetime_format= 设置不同的格式。

消息

消息是条目方法的可选参数

logger = Logger.new($stdout)
logger.add(Logger::INFO, 'My message')
# => I, [2022-05-07T18:15:37.647581 #20536]  INFO -- : My message

对于默认的条目格式化程序 Logger::Formatter,消息对象可以是

  • 字符串:按原样使用。

  • 异常:使用 message.message

  • 任何其他内容:使用 message.inspect

注意Logger::Formatter 不会转义或清理传递给它的消息。开发人员应注意,恶意数据(用户输入)可能位于消息中,并且应显式转义不受信任的数据。

你可以使用自定义格式化程序来转义消息数据;请参阅 formatter= 的示例。

程序名称

程序名称是条目方法的可选参数

logger = Logger.new($stdout)
logger.add(Logger::INFO, 'My message', 'mung')
# => I, [2022-05-07T18:17:38.084716 #20536]  INFO -- mung: My message

可以在调用 Logger.new 时,通过可选关键字参数 progname 设置新日志记录器的默认程序名称

logger = Logger.new('t.log', progname: 'mung')

可以通过调用方法 progname= 设置现有日志记录器的默认程序名称

logger.progname = 'mung'

可以使用方法 progname 检索当前程序名称

logger.progname # => "mung"

日志级别

日志级别设置基于条目的严重程度确定是否实际将条目写入日志。

以下是定义的严重程度(从最不严重到最严重)

logger = Logger.new($stdout)
logger.add(Logger::DEBUG, 'Maximal debugging info')
# => D, [2022-05-07T17:57:41.776220 #20536] DEBUG -- : Maximal debugging info
logger.add(Logger::INFO, 'Non-error information')
# => I, [2022-05-07T17:59:14.349167 #20536]  INFO -- : Non-error information
logger.add(Logger::WARN, 'Non-error warning')
# => W, [2022-05-07T18:00:45.337538 #20536]  WARN -- : Non-error warning
logger.add(Logger::ERROR, 'Non-fatal error')
# => E, [2022-05-07T18:02:41.592912 #20536] ERROR -- : Non-fatal error
logger.add(Logger::FATAL, 'Fatal error')
# => F, [2022-05-07T18:05:24.703931 #20536] FATAL -- : Fatal error
logger.add(Logger::UNKNOWN, 'Most severe')
# => A, [2022-05-07T18:07:54.657491 #20536]   ANY -- : Most severe

默认的初始级别设置为 Logger::DEBUG,即最低级别,这意味着将写入所有条目,无论严重程度如何

logger = Logger.new($stdout)
logger.level # => 0
logger.add(0, "My message")
# => D, [2022-05-11T15:10:59.773668 #20536] DEBUG -- : My message

你可以在新的日志记录器中使用关键字参数 level 以及适当的值指定不同的设置

logger = Logger.new($stdout, level: Logger::ERROR)
logger = Logger.new($stdout, level: 'error')
logger = Logger.new($stdout, level: :error)
logger.level # => 3

使用此级别,将写入严重程度为 Logger::ERROR 及更高的条目,而不会写入严重程度较低的条目

logger = Logger.new($stdout, level: Logger::ERROR)
logger.add(3)
# => E, [2022-05-11T15:17:20.933362 #20536] ERROR -- : nil
logger.add(2) # Silent.

可以使用方法 level= 设置现有日志记录器的日志级别

logger.level = Logger::ERROR

这些简写方法也设置级别

logger.debug! # => 0
logger.info!  # => 1
logger.warn!  # => 2
logger.error! # => 3
logger.fatal! # => 4

可以使用方法 level 检索日志级别。

logger.level = Logger::ERROR
logger.level # => 3

这些方法返回是否要写入给定的级别

logger.level = Logger::ERROR
logger.debug? # => false
logger.info?  # => false
logger.warn?  # => false
logger.error? # => true
logger.fatal? # => true

日志文件轮转

默认情况下,日志文件是一个无限增长的单个文件(直到显式关闭);没有文件轮转。

为了使日志文件保持在可管理的大小,你可以使用日志 文件 轮转,它使用多个日志文件

  • 每个日志文件都有不重叠时间间隔的条目。

  • 只有最新的日志文件是打开和活动的;其他文件是关闭和非活动的。

基于大小的轮转

对于基于大小的日志文件轮转,请使用以下参数调用 Logger.new

  • 参数 logdev 作为文件路径。

  • 参数 shift_age 为正整数:轮转中要使用的日志文件数。

  • 参数 shift_size 为正整数:每个日志文件的最大大小(以字节为单位);默认为 1048576(1 兆字节)。

示例

logger = Logger.new('t.log', 3)           # Three 1-megabyte files.
logger = Logger.new('t.log', 5, 10485760) # Five 10-megabyte files.

对于这些示例,假设

logger = Logger.new('t.log', 3)

日志记录在新日志文件 t.log 中开始;当新条目导致其大小超过 shift_size 时,该日志文件将“满”并准备好进行轮转。

t.log 首次满时

  • t.log 被关闭并重命名为 t.log.0

  • 将打开一个新文件 t.log

t.log 第二次满时

  • +t.log.0 被重命名为 t.log.1

  • t.log 被关闭并重命名为 t.log.0

  • 将打开一个新文件 t.log

每次后续 t.log 文件满时,都会轮转日志文件

  • t.log.1 被删除。

  • +t.log.0 被重命名为 t.log.1

  • t.log 被关闭并重命名为 t.log.0

  • 将打开一个新文件 t.log

定期轮转

对于定期轮转,请使用以下参数调用 Logger.new

  • 参数 logdev 作为文件路径。

  • 参数 shift_age 作为字符串周期指示符。

示例

logger = Logger.new('t.log', 'daily')   # Rotate log files daily.
logger = Logger.new('t.log', 'weekly')  # Rotate log files weekly.
logger = Logger.new('t.log', 'monthly') # Rotate log files monthly.

示例

logger = Logger.new('t.log', 'daily')

当给定的周期过期时

  • 基本日志文件 t.log 被关闭,并使用基于日期的后缀(例如 t.log.20220509)重命名。

  • 将打开一个新的日志文件 t.log

  • 不删除任何内容。

后缀的默认格式为 '%Y%m%d',这将生成类似于上面的后缀。你可以使用创建时选项 shift_period_suffix 设置不同的格式;请参阅 Time#strftime 的详细信息和建议。

常量

ProgName
SEV_LABEL

日志记录的严重程度标签(最多 5 个字符)。

VERSION

属性

formatter[RW]

设置或检索日志记录器条目格式化程序 proc。

formatternil 时,日志记录器使用 Logger::Formatter

formatter 为 proc 时,新条目由 proc 格式化,该 proc 使用四个参数调用

  • severity:条目的严重程度。

  • time:表示条目时间戳的 Time 对象。

  • progname:条目的程序名称。

  • msg:条目的消息(字符串或可转换为字符串的对象)。

该 proc 应返回包含格式化条目的字符串。

此自定义格式化程序使用 String#dump 来转义消息字符串

logger = Logger.new($stdout, progname: 'mung')
original_formatter = logger.formatter || Logger::Formatter.new
logger.formatter = proc { |severity, time, progname, msg|
  original_formatter.call(severity, time, progname, msg.dump)
}
logger.add(Logger::INFO, "hello \n ''")
logger.add(Logger::INFO, "\f\x00\xff\\\"")

输出

I, [2022-05-13T13:16:29.637488 #8492]  INFO -- mung: "hello \n ''"
I, [2022-05-13T13:16:29.637610 #8492]  INFO -- mung: "\f\x00\xFF\\\""
progname[RW]

要包含在日志消息中的程序名称。

公共类方法

new(logdev, shift_age = 0, shift_size = 1048576, **options) 单击以切换源代码

使用单个参数 logdev,返回一个具有所有默认选项的新日志记录器

Logger.new('t.log') # => #<Logger:0x000001e685dc6ac8>

参数 logdev 必须是以下之一

  • 字符串文件路径:条目将写入该路径的文件;如果该路径的文件存在,则将追加新条目。

  • IO 流(通常为 +$stdout+、+$stderr+ 或打开的文件):条目将写入给定的流。

  • nilFile::NULL:不写入任何条目。

示例

Logger.new('t.log')
Logger.new($stdout)

关键字选项为

  • level:设置日志级别;默认值为 Logger::DEBUG。请参阅日志级别

    Logger.new('t.log', level: Logger::ERROR)
    
  • progname:设置默认程序名称;默认值为 nil。请参阅程序名称

    Logger.new('t.log', progname: 'mung')
    
  • formatter:设置条目格式化程序;默认值为 nil。请参阅formatter=

  • datetime_format:设置条目时间戳的格式;默认值为 nil。请参阅 datetime_format=

  • binmode:设置日志器是否以二进制模式写入;默认值为 false

  • shift_period_suffix:设置定期日志文件轮换的文件名后缀格式;默认值为 '%Y%m%d'。请参阅 定期轮换

  • reraise_write_errors:一个异常类数组,如果在写入日志设备时发生错误,将重新抛出这些异常。默认情况下,会吞噬所有引发的异常。

# File logger.rb, line 581
def initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG,
               progname: nil, formatter: nil, datetime_format: nil,
               binmode: false, shift_period_suffix: '%Y%m%d',
               reraise_write_errors: [])
  self.level = level
  self.progname = progname
  @default_formatter = Formatter.new
  self.datetime_format = datetime_format
  self.formatter = formatter
  @logdev = nil
  @level_override = {}
  if logdev && logdev != File::NULL
    @logdev = LogDevice.new(logdev, shift_age: shift_age,
      shift_size: shift_size,
      shift_period_suffix: shift_period_suffix,
      binmode: binmode,
      reraise_write_errors: reraise_write_errors)
  end
end

公共实例方法

<<(msg) 点击以切换源代码

将给定的 msg 写入日志,不进行格式化;返回写入的字符数,如果不存在日志设备,则返回 nil

logger = Logger.new($stdout)
logger << 'My message.' # => 10

输出

My message.
# File logger.rb, line 689
def <<(msg)
  @logdev&.write(msg)
end
add(severity, message = nil, progname = nil) { || ... } 点击以切换源代码

创建一个日志条目,该条目可能会或可能不会写入日志,具体取决于条目的严重性和日志级别。有关详细信息,请参阅 日志级别条目

示例

logger = Logger.new($stdout, progname: 'mung')
logger.add(Logger::INFO)
logger.add(Logger::ERROR, 'No good')
logger.add(Logger::ERROR, 'No good', 'gnum')

输出

I, [2022-05-12T16:25:31.469726 #36328]  INFO -- mung: mung
E, [2022-05-12T16:25:55.349414 #36328] ERROR -- mung: No good
E, [2022-05-12T16:26:35.841134 #36328] ERROR -- gnum: No good

这些便捷方法具有隐式的严重性

# File logger.rb, line 656
def add(severity, message = nil, progname = nil)
  severity ||= UNKNOWN
  if @logdev.nil? or severity < level
    return true
  end
  if progname.nil?
    progname = @progname
  end
  if message.nil?
    if block_given?
      message = yield
    else
      message = progname
      progname = @progname
    end
  end
  @logdev.write(
    format_message(format_severity(severity), Time.now, progname, message))
  true
end
也别名为:log
close() 点击以切换源代码

关闭日志器;返回 nil

logger = Logger.new('t.log')
logger.close       # => nil
logger.info('foo') # Prints "log writing failed. closed stream"

相关:Logger#reopen

# File logger.rb, line 736
def close
  @logdev&.close
end
datetime_format() 点击以切换源代码

返回日期时间格式;请参阅 datetime_format=

# File logger.rb, line 438
def datetime_format
  @default_formatter.datetime_format
end
datetime_format=(datetime_format) 点击以切换源代码

设置日期时间格式。

参数 datetime_format 应该是以下之一

  • 一个适合用作方法 Time#strftime 的格式的字符串。

  • nil:日志器使用 '%Y-%m-%dT%H:%M:%S.%6N'

# File logger.rb, line 432
def datetime_format=(datetime_format)
  @default_formatter.datetime_format = datetime_format
end
debug(progname = nil, &block) 点击以切换源代码

等效于使用严重性 Logger::DEBUG 调用 add

# File logger.rb, line 695
def debug(progname = nil, &block)
  add(DEBUG, nil, progname, &block)
end
debug!() 点击以切换源代码

将日志级别设置为 Logger::DEBUG。请参阅 日志级别

# File logger.rb, line 487
def debug!; self.level = DEBUG; end
debug?() 点击以切换源代码

如果日志级别允许写入严重性为 Logger::DEBUG 的条目,则返回 true,否则返回 false。请参阅 日志级别

# File logger.rb, line 482
def debug?; level <= DEBUG; end
error(progname = nil, &block) 点击以切换源代码

等效于使用严重性 Logger::ERROR 调用 add

# File logger.rb, line 713
def error(progname = nil, &block)
  add(ERROR, nil, progname, &block)
end
error!() 点击以切换源代码

将日志级别设置为 Logger::ERROR。请参阅 日志级别

# File logger.rb, line 520
def error!; self.level = ERROR; end
error?() 点击以切换源代码

如果日志级别允许写入严重性为 Logger::ERROR 的条目,则返回 true,否则返回 false。请参阅 日志级别

# File logger.rb, line 515
def error?; level <= ERROR; end
fatal(progname = nil, &block) 点击以切换源代码

等效于使用严重性 Logger::FATAL 调用 add

# File logger.rb, line 719
def fatal(progname = nil, &block)
  add(FATAL, nil, progname, &block)
end
fatal!() 点击以切换源代码

将日志级别设置为 Logger::FATAL。请参阅 日志级别

# File logger.rb, line 531
def fatal!; self.level = FATAL; end
fatal?() 点击以切换源代码

如果日志级别允许写入严重性为 Logger::FATAL 的条目,则返回 true,否则返回 false。请参阅 日志级别

# File logger.rb, line 526
def fatal?; level <= FATAL; end
info(progname = nil, &block) 点击以切换源代码

等效于使用严重性 Logger::INFO 调用 add

# File logger.rb, line 701
def info(progname = nil, &block)
  add(INFO, nil, progname, &block)
end
info!() 点击以切换源代码

将日志级别设置为 Logger::INFO。请参阅 日志级别

# File logger.rb, line 498
def info!; self.level = INFO; end
info?() 点击以切换源代码

如果日志级别允许写入严重性为 Logger::INFO 的条目,则返回 true,否则返回 false。请参阅 日志级别

# File logger.rb, line 493
def info?; level <= INFO; end
level() 点击以切换源代码

日志严重性阈值(例如,Logger::INFO)。

# File logger.rb, line 383
def level
  level_override[level_key] || @level
end
也别名为:sev_threshold
level=(severity) 点击以切换源代码

设置日志级别;返回 severity。请参阅 日志级别

参数 severity 可以是整数、字符串或符号

logger.level = Logger::ERROR # => 3
logger.level = 3             # => 3
logger.level = 'error'       # => "error"
logger.level = :error        # => :error

Logger#sev_threshold=Logger#level= 的别名。

# File logger.rb, line 399
def level=(severity)
  @level = Severity.coerce(severity)
end
也别名为:sev_threshold=
log(severity, message = nil, progname = nil)
别名为:add
reopen(logdev = nil) 点击以切换源代码

设置日志器的输出流

  • 如果 logdevnil,则重新打开当前的输出流。

  • 如果 logdev 是文件路径,则打开指示的文件进行追加。

  • 如果 logdev 是 IO 流(通常是 $stdout$stderr 或打开的 File 对象),则打开该流进行追加。

示例

logger = Logger.new('t.log')
logger.add(Logger::ERROR, 'one')
logger.close
logger.add(Logger::ERROR, 'two') # Prints 'log writing failed. closed stream'
logger.reopen
logger.add(Logger::ERROR, 'three')
logger.close
File.readlines('t.log')
# =>
# ["# Logfile created on 2022-05-12 14:21:19 -0500 by logger.rb/v1.5.0\n",
#  "E, [2022-05-12T14:21:27.596726 #22428] ERROR -- : one\n",
#  "E, [2022-05-12T14:23:05.847241 #22428] ERROR -- : three\n"]
# File logger.rb, line 624
def reopen(logdev = nil)
  @logdev&.reopen(logdev)
  self
end
sev_threshold()
别名为:level
sev_threshold=(severity)
别名为:level=
unknown(progname = nil, &block) 点击以切换源代码

等效于使用严重性 Logger::UNKNOWN 调用 add

# File logger.rb, line 725
def unknown(progname = nil, &block)
  add(UNKNOWN, nil, progname, &block)
end
warn(progname = nil, &block) 点击以切换源代码

等效于使用严重性 Logger::WARN 调用 add

# File logger.rb, line 707
def warn(progname = nil, &block)
  add(WARN, nil, progname, &block)
end
warn!() 点击以切换源代码

将日志级别设置为 Logger::WARN。请参阅 日志级别

# File logger.rb, line 509
def warn!; self.level = WARN; end
warn?() 点击以切换源代码

如果日志级别允许写入严重性为 Logger::WARN 的条目,则返回 true,否则返回 false。请参阅 日志级别

# File logger.rb, line 504
def warn?; level <= WARN; end
with_level(severity) { || ... } 点击以切换源代码

仅在当前 Fiber 的块执行期间调整日志级别

logger.with_level(:debug) do
  logger.debug { "Hello" }
end
# File logger.rb, line 408
def with_level(severity)
  prev, level_override[level_key] = level, Severity.coerce(severity)
  begin
    yield
  ensure
    if prev
      level_override[level_key] = prev
    else
      level_override.delete(level_key)
    end
  end
end

私有实例方法

format_message(severity, datetime, progname, msg) 点击以切换源代码
# File logger.rb, line 758
def format_message(severity, datetime, progname, msg)
  (@formatter || @default_formatter).call(severity, datetime, progname, msg)
end
format_severity(severity) 点击以切换源代码
# File logger.rb, line 745
def format_severity(severity)
  SEV_LABEL[severity] || 'ANY'
end
level_key() 点击以切换源代码
# File logger.rb, line 754
def level_key
  Fiber.current
end
level_override() 点击以切换源代码

即使子类不调用超类构造函数,也保证此 ivar 的存在。

# File logger.rb, line 750
def level_override
  @level_override ||= {}
end