2012-03-09 47 views
8

我有一個Logger實例:重定向錯誤輸出到記錄器實例

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

我想運行時錯誤(錯誤輸出)重定向到日誌爲好。我發現this forum thread其中有建議:

new_fd = logger.get_logger_file_descriptor 
$stderr.reopen new_fd 

然而,記錄器沒有一個實例方法get_logger_file_descriptor,也可以找到身邊獲得訪問日誌設備或文件中的任何公開的方法。

如何導致所有$ stderr輸出進入日誌?

回答

15

如果你自己創造的記錄,你可以先創建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執行方法需要執行多少工作。

+0

很好的答案,謝謝!在我的情況下,我正在創建記錄器,以便您的第一個解決方案乾淨而優雅。我特別喜歡你已經包含了另一種情況的解決方法。 (雖然在這種情況下,我可能會主張我直接進入並直接抓取文件。) – Phrogz 2012-03-09 23:24:55

+0

第二種方法在jruby上不適用於我。我得到以下錯誤:(Errno :: EBADF)錯誤的文件描述符 – 2013-01-02 13:31:29

+0

應該有'@ logger.error(string)'我認爲 – lojza 2017-06-21 12:51:41

2

我已經能夠拿出最好的是該河段全能破解:

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

它的工作原理,但當然打破記錄儀,我認爲是有原因的封裝。