将stderr重定向到Logger实例

时间:2012-03-09 15:58:37

标签: ruby stderr

我有Logger个实例:

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

我想将运行时错误(stderr输出)重定向到日志中。我发现this forum thread有建议:

new_fd = logger.get_logger_file_descriptor
$stderr.reopen new_fd

但是,Logger没有实例方法get_logger_file_descriptor,也无法找到任何公开的方法来获取对日志设备或文件的访问权。

如何让所有$ stderr输出进入日志?

2 个答案:

答案 0 :(得分:17)

如果您自己创建记录器,可以先创建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并将写入它的任何内容传递给记录器。遗憾的是,Ruby中的IO类与底层的i / o系统(文件描述符等)紧密相关,并且没有可用于创建输入和输出流的通用接口。 (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实施方法的工作量。< / p>

答案 1 :(得分:2)

我能想出的最好的就是这种无处不在的黑客攻击:

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

它有效,但当然打破了Logger的封装,我认为这是有原因的。