класс Logger
Класс Logger предоставляет простую, но многофункциональную утилиту ведения журналов, которую можно использовать для создания одного или нескольких журналов событий для программы. Каждый такой журнал содержит хронологическую последовательность записей, отражающих работу программы.
О примерах
Во всех примерах на этой странице предполагается, что 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"
где форматируемые значения — это:
-
Уровень серьёзности (одна буква).
-
Временная метка.
-
Идентификатор процесса.
-
Уровень серьёзности (словом).
-
Имя программы.
-
Сообщение.
Можно использовать другой формат записи:
-
Задав пользовательский 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
Ротация File журнала
По умолчанию файл журнала — это один файл, который растёт бесконечно (пока его явно не закроют); ротация файлов не выполняется.
Чтобы файлы журнала не превышали удобный для работы размер, можно использовать ротацию файлов журнала, при которой применяются несколько файлов журнала:
-
Каждый файл журнала содержит записи за непересекающийся временной интервал.
-
Открыт и активен только самый новый файл журнала; остальные закрыты и неактивны.
Ротация по размеру
Для ротации файлов журнала по размеру вызовите 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
Атрибуты
Задает или возвращает proc форматирования записи журнала.
Если formatter — nil, журнал использует Logger::Formatter.
Если formatter — 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\\\""
Имя программы, включаемое в сообщения журнала.
Открытые методы класса
# File lib/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 Если передан единственный аргумент logdev, возвращает новый объект logger со всеми параметрами по умолчанию:
Logger.new('t.log') # => #<Logger:0x000001e685dc6ac8>
Аргумент logdev должен иметь одно из следующих значений:
-
Путь к файлу в виде строки: записи будут добавляться в файл по этому пути; если файл уже существует, новые записи добавляются в его конец.
-
Поток
IO(обычно +$stdout+, +$stderr+ или открытый файл): записи будут выводиться в указанный поток. -
nilилиFile::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: задает, записывает ли logger данные в двоичном режиме; значение по умолчанию —false. -
shift_period_suffix: задает формат суффикса имени файла для периодической ротации файлов журнала; значение по умолчанию —'%Y%m%d'. См. раздел Периодическая ротация. -
reraise_write_errors: массив классов исключений, которые будут повторно вызваны при ошибке записи в устройство журнала. По умолчанию все возникающие исключения подавляются.
Открытые методы экземпляра
# File lib/logger.rb, line 689 def <<(msg) @logdev&.write(msg) end
Записывает заданное msg в журнал без форматирования; возвращает количество записанных символов или nil, если устройство журнала отсутствует:
logger = Logger.new($stdout) logger << 'My message.' # => 10
Вывод:
My message.
# File lib/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 Создает запись журнала, которая может быть записана или не записана в зависимости от уровня серьезности записи и уровня журнала. Подробнее см. разделы Уровень журнала и Записи.
Примеры:
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 lib/logger.rb, line 736 def close @logdev&.close end
Закрывает logger; возвращает nil:
logger = Logger.new('t.log')
logger.close # => nil
logger.info('foo') # Prints "log writing failed. closed stream"
См. также: Logger#reopen.
# File lib/logger.rb, line 438 def datetime_format @default_formatter.datetime_format end
Возвращает формат даты и времени; см. datetime_format=.
# File lib/logger.rb, line 432 def datetime_format=(datetime_format) @default_formatter.datetime_format = datetime_format end
Задает формат даты и времени.
Аргумент datetime_format должен иметь одно из следующих значений:
-
Строка, подходящая для использования в качестве формата метода
Time#strftime. -
nil: logger использует'%Y-%m-%dT%H:%M:%S.%6N'.
# File lib/logger.rb, line 695 def debug(progname = nil, &block) add(DEBUG, nil, progname, &block) end
Эквивалентен вызову add с уровнем серьезности Logger::DEBUG.
# File lib/logger.rb, line 487 def debug!; self.level = DEBUG; end
Устанавливает уровень журнала Logger::DEBUG. См. раздел Уровень журнала.
# File lib/logger.rb, line 482 def debug?; level <= DEBUG; end
Возвращает true, если уровень журнала разрешает запись записей с уровнем серьезности Logger::DEBUG, и false в противном случае. См. раздел Уровень журнала.
# File lib/logger.rb, line 713 def error(progname = nil, &block) add(ERROR, nil, progname, &block) end
Эквивалентен вызову add с уровнем серьезности Logger::ERROR.
# File lib/logger.rb, line 520 def error!; self.level = ERROR; end
Устанавливает уровень журнала Logger::ERROR. См. раздел Уровень журнала.
# File lib/logger.rb, line 515 def error?; level <= ERROR; end
Возвращает true, если уровень журнала разрешает запись записей с уровнем серьезности Logger::ERROR, и false в противном случае. См. раздел Уровень журнала.
# File lib/logger.rb, line 719 def fatal(progname = nil, &block) add(FATAL, nil, progname, &block) end
Эквивалентен вызову add с уровнем серьезности Logger::FATAL.
# File lib/logger.rb, line 531 def fatal!; self.level = FATAL; end
Устанавливает уровень журнала Logger::FATAL. См. раздел Уровень журнала.
# File lib/logger.rb, line 526 def fatal?; level <= FATAL; end
Возвращает true, если уровень журнала разрешает запись записей с уровнем серьезности Logger::FATAL, и false в противном случае. См. раздел Уровень журнала.
# File lib/logger.rb, line 701 def info(progname = nil, &block) add(INFO, nil, progname, &block) end
Эквивалентен вызову add с уровнем серьезности Logger::INFO.
# File lib/logger.rb, line 498 def info!; self.level = INFO; end
Устанавливает уровень журнала Logger::INFO. См. раздел Уровень журнала.
# File lib/logger.rb, line 493 def info?; level <= INFO; end
Возвращает true, если уровень журнала разрешает запись записей с уровнем серьезности Logger::INFO, и false в противном случае. См. раздел Уровень журнала.
# File lib/logger.rb, line 383 def level level_override[level_key] || @level end
Порог уровня серьезности журнала (например, Logger::INFO).
# File lib/logger.rb, line 399 def level=(severity) @level = Severity.coerce(severity) end
Задает уровень журнала; возвращает 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 lib/logger.rb, line 624 def reopen(logdev = nil) @logdev&.reopen(logdev) self end
Задает выходной поток logger:
-
Если
logdev—nil, повторно открывает текущий выходной поток. -
Если
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 lib/logger.rb, line 725 def unknown(progname = nil, &block) add(UNKNOWN, nil, progname, &block) end
Эквивалентен вызову add с уровнем серьезности Logger::UNKNOWN.
# File lib/logger.rb, line 707 def warn(progname = nil, &block) add(WARN, nil, progname, &block) end
Эквивалентен вызову add с уровнем серьезности Logger::WARN.
# File lib/logger.rb, line 509 def warn!; self.level = WARN; end
Устанавливает уровень журнала Logger::WARN. См. раздел Уровень журнала.
# File lib/logger.rb, line 504 def warn?; level <= WARN; end
Возвращает true, если уровень журнала разрешает запись записей с уровнем серьезности Logger::WARN, и false в противном случае. См. раздел Уровень журнала.
# File lib/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 Изменяет уровень журнала на время выполнения блока только для текущего Fiber
logger.with_level(:debug) do
logger.debug { "Hello" }
end
Закрытые методы экземпляра
# File lib/logger.rb, line 758 def format_message(severity, datetime, progname, msg) (@formatter || @default_formatter).call(severity, datetime, progname, msg) end
# File lib/logger.rb, line 745 def format_severity(severity) SEV_LABEL[severity] || 'ANY' end
# File lib/logger.rb, line 754 def level_key Fiber.current end
# File lib/logger.rb, line 750
def level_override
@level_override ||= {}
end Обеспечивает наличие этой переменной экземпляра, даже если подклассы не вызывают конструктор суперкласса.
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.