Перенаправить stderr в экземпляр Logger - PullRequest
9 голосов
/ 09 марта 2012

У меня есть Logger экземпляр:

require 'logger'
logger = Logger.new( 'foo.log', 'weekly' )

Я хочу перенаправить ошибки времени выполнения (вывод stderr) в журнал.Я нашел эту ветку форума , в которой есть совет:

new_fd = logger.get_logger_file_descriptor
$stderr.reopen new_fd

Однако у Logger нет метода экземпляра get_logger_file_descriptor, и я не могу найти какие-либо открытые методы получения доступа кустройство журнала или файл.

Как я могу заставить все выходные данные $ stderr попадать в журнал?

Ответы [ 2 ]

17 голосов
/ 10 марта 2012

Если вы создаете регистратор самостоятельно, вы можете сначала создать объект File, а затем использовать его для создания регистратора и присвоить ему $stderr:

log_file = File.new('foo.log', 'a')
logger = Logger.new(log_file, 'weekly')
$stderr = log_file   #usually no need to use reopen

Обратите внимание, что это приведет к тому, что вывод журнала будет смешан с выводом $stderr, что может вызвать проблемы, если вы анализируете файл журнала, ожидая, что он будет в определенном формате (это произойдет с вашим решением тоже).

Если у вас нет базового файла, а просто получите logger откуда-то еще, это немного сложнее. Требуется IO подобный объект, который может быть назначен на $stderr и передает все записанное в него регистратору. Класс IO в Ruby, к сожалению, довольно тесно связан с базовой системой ввода-вывода (файловые дескрипторы и т. П.), И нет общего интерфейса, который можно использовать для создания потоков ввода и вывода. (StringIO является заметным исключением).

Однако большинство, если не все, методы вывода в IO в конечном итоге проходят через #write, поэтому, переопределив этот единственный метод, вы можете приблизиться к тому, что вам нужно:

class IOToLog < IO

  def initialize(logger)
    @logger = logger
  end

  def write(string)
    #assume anything written to stderr is an error
    @logger.error(message)
  end

end

logger = get_logger_from_somewhere

$stderr = IOToLog.new(logger)

Теперь все, что записано в $stderr, в конечном итоге попадет в файл журнала. Форматирование, однако, будет немного странным. Каждый раз, когда любой из методов записи вызывает #write, в лог-файл будет добавлена ​​новая запись. Например, #puts, вызываемый с помощью массива, будет вызывать #write для каждой записи массива, и снова с символом новой строки между каждой записью, что приведет к 2n - 1 записям журнала, n - 1 из которых будет пустым.

Вы можете сделать переопределенный метод #write более сложным, чтобы справиться с этим, возможно, используя внутренний буфер, и вызывать регистратор только тогда, когда считаете, что у вас есть полное сообщение. В качестве альтернативы вы можете переопределить отдельные методы для записи в сам логгер. Если вы это сделаете, класс IOToLog не обязательно будет наследоваться от IO.

Ваше лучшее решение будет зависеть от того, как вы хотите, чтобы стандартный вывод ошибок отображался в лог-файле, как ваша программа использует $stderr, и сколько работы вы хотите выполнить, реализуя методы из IO.

2 голосов
/ 09 марта 2012

Лучшее, что я смог придумать, - это хакер:

$stderr.reopen logger.instance_variable_get(:@logdev).dev

Это работает, но, конечно, нарушает инкапсуляцию Logger, которая, как я полагаю, была там по причине.

...