我有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输出进入日志?
答案 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的封装,我认为这是有原因的。