2012-03-09 21 views
8

mam Logger instancję:przekierować stderr do instancji Logger

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

Chcę przekierować błędy runtime (wyjście stderr) w dzienniku, jak również. Znalazłem this forum thread który ma rady:

new_fd = logger.get_logger_file_descriptor 
$stderr.reopen new_fd 

Jednak Rejestrator nie posiada metodę instancji get_logger_file_descriptor, nie mogę znaleźć żadnych odsłoniętych metod całym uzyskania dostępu do urządzenia lub pliku dziennika.

Jak mogę spowodować, że wszystkie dane z $ stderr zostaną umieszczone w dzienniku?

Odpowiedz

15

Jeśli tworzysz rejestratora siebie, można utworzyć obiekt File pierwszy, a następnie użyć go do tworzenia rejestratora i przypisać ją do $stderr:

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

Zauważ, że to spowoduje wyjście dziennika jest mieszany z wynikiem $stderr, co może powodować problemy podczas analizowania pliku dziennika, spodziewając się, że ma on określony format (tak samo będzie z twoim rozwiązaniem).

Jeśli nie masz pliku bazowego, ale po prostu odbierasz logger z innego miejsca, jest to nieco trudniejsze. Potrzebny jest obiekt podobny do IO, który można przypisać do $stderr i przekazuje wszystko, co do niego napisał, do rejestratora. Klasa Ruby w wersji IO jest niestety dość ściśle powiązana z bazowym systemem i/o (deskryptorami plików itp.) I nie ma ogólnego interfejsu, który można wykorzystać do tworzenia strumieni wejściowych i wyjściowych. (StringIO jest godnym uwagi wyjątkiem).

Jednak większość, jeśli nie wszystkie, z metod wyjściowych IO ostatecznie przejść #write, więc nadrzędnymi tę jedną metodę można zbliżyć się do tego, co jesteś po:

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) 

Teraz wszystkie zapisywane w $stderr zakończy się przechodzeniem do pliku dziennika. Formatowanie będzie jednak nieco dziwne. Za każdym razem, gdy któraś z metod zapisu wywoła #write, w pliku logu pojawi się nowy wpis. Na przykład: #puts wywołany z tablicą wywoła #write dla każdego wpisu tablicy, i ponownie znakiem nowej linii między poszczególnymi wpisami, w wyniku czego pojawią się wpisy dziennika 2n - 1, z których n - 1 będzie puste.

Można przesłonić metodę #write bardziej złożoną, aby sobie z tym poradzić, być może za pomocą wewnętrznego bufora, i wywoływać tylko program rejestrujący, gdy myślisz, że masz pełną wiadomość. Alternatywnie można zastąpić poszczególne metody, aby sami zapisali dane do rejestratora. Jeśli to zrobisz, klasa IOToLog niekoniecznie będzie musiała dziedziczyć po IO.

Najlepszym rozwiązaniem będzie zależeć od tego, jak chcesz się standardowe wyjście błędów, aby pojawić się w dzienniku, w jaki sposób program używa $stderr i jak chcesz robić metod wykonawczych z IO dużo pracy.

+0

Świetna odpowiedź, dzięki! W moim przypadku tworzę rejestrator, więc twoje pierwsze rozwiązanie jest czyste i eleganckie. Szczególnie podoba mi się, że uwzględniłeś obejście tego drugiego przypadku. (Choć w takim przypadku mógłbym zalecić mój włamanie do osiągnięcia i pobrania pliku bezpośrednio.) – Phrogz

+0

Druga metoda nie działa dla mnie na jruby. Pojawia się następujący błąd: (Errno :: EBADF) Niepoprawny deskryptor pliku –

+0

Powinien być '@ logger.error (ciąg)' Myślę, że – lojza

2

Najlepszą udało mi się wymyślić jest to zasięg wokół Hack:

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

To działa, ale oczywiście zrywa hermetyzacja Logger które zakładam, że było tam bez powodu.

Powiązane problemy