2012-12-17 22 views
5

Piszę narzędzie badawcze, a ostatnio przełączyłem się z użycia instrukcji "print" na użycie funkcji rejestratora wbudowanej w Pythonie. To, jak uzasadniłem, pozwoliłoby mi dać użytkownikowi opcję zrzucenia danych wyjściowych do pliku, oprócz zrzucenia go na ekran.Logowanie z zewnętrznej aplikacji

Jak dotąd tak dobrze. Część mojego kodu, która jest w Pythonie, używa "logger.info" i "logger.error" do zrzutu zarówno do ekranu, jak i do pliku. "logger" to moduł rejestrujący całego modułu. Ta część działa jak urok.

Jednak w kilku punktach używam "subprocess.call" do uruchamiania pliku wykonywalnego przez powłokę. Tak, przez cały kod, mam linie takie jak

proc = subprocess.call(command) 

Wyjście tego polecenia będzie drukować na ekranie, jak zawsze, ale nie byłoby zrzucić do pliku podanego przez użytkownika.

Możliwym rozwiązaniem byłoby otwarcie rury do pliku:

proc = subprocess.call(command, stdout=f, stderr=subprocess.OUTPUT) 

Ale to tylko zrzucić do pliku, a nie na ekranie.

Zasadniczo moje pytanie sprowadza się do tego: czy istnieje sposób, w jaki mogę wykorzystać istniejący program rejestrujący, bez konieczności konstruowania innego programu obsługi dla plików specjalnie dla podprocesu. (Być może poprzez przekierowanie wyjścia do rejestratora?) Czy jest to niemożliwe, biorąc pod uwagę obecną konfigurację? Jeśli to drugie, jak mogę poprawić konfigurację?

(Oh, również, że byłoby wspaniale, gdyby było rejestrowanie w „czasie rzeczywistym”, tak, że wiadomości z pliku wykonywalnego są rejestrowane, jak są one odbierane.)

Dzięki za wszelką pomoc! :)

+1

Lennart Regebro [Klasa StreamLogger] (http://stackoverflow.com/a/4838875/190597) będzie dobrze działać w twojej sytuacji. – unutbu

+0

Dzięki za referencję! Działa to doskonale w moim przypadku. –

Odpowiedz

3

Zamiast pipetowania stdout do pliku, można przepuścić go do PIPE, a następnie odczytać z tego PIPE i napisać do rejestratora. Coś takiego:

proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.OUTPUT) 
for line in proc.stdout: 
    logging.info(line) 

Istnieje jednak jeszcze prostsza odpowiedź: trzeba użyć obiektu plikopodobny z uchwytu pliku, ale można utworzyć na szczycie rury, która przechodzi każdą linię do logging. Możesz napisać ten obiekt sam, ale, jak mówi @unutbu, ktoś już zrobił to w this question. Więc:

with StreamLogger(logging.INFO) as out: 
    proc = subprocess.call(command, stdout=out, stderr=subprocess.OUTPUT) 

Oczywiście można również tymczasowo owinąć stdout zapisu rejestratora i po prostu przekazać wyjście poprzez, na przykład, za pomocą this confusingly identically-named class:

with StreamLogger('stdout'): 
    proc = subprocess.call(command, stderr=subprocess.OUTPUT) 
+0

Dzięki za odniesienie do kodu w ostatnim linku. To była interesująca lektura. –

3

unutbu's comment jest dobre; powinieneś rzucić okiem na Lennart's answer.

Co potrzebne jest coś funkcjonalności tee, ale moduł subprocess działa na poziomie uchwytów OS, co oznacza, że ​​dane zapisywane przez podproces nie mogą być postrzegane przez kodzie Pythona, powiedzmy przez jakiś File- jak obiekt, który piszesz, który rejestruje i drukuje to, co do niego napisano.

Oprócz użycia odpowiedzi Lennarta, możesz zrobić takie rzeczy, korzystając z biblioteki innej firmy, takiej jak sarge (ujawnienie: jestem jej opiekunem). Działa na więcej niż logowanie.Załóżmy, że mamy program, który generuje dane wyjściowe, takie jak:

# echotest.py 
import time 
for i in range(10): 
    print('Message %d' % (i + 1)) 

i chcesz uchwycić go w skrypcie logowania go i wydrukować go na ekranie:

#subptest.py 
from sarge import capture_stdout 
import logging 
import sys 

logging.basicConfig(filename='subptest.log', filemode='w', 
        level=logging.INFO) 

p = capture_stdout('python echotest.py', async=True) 
while True: 
    line = p.stdout.readline() 
    line = line.strip() 
    # depending on how the child process generates output, 
    # sometimes you won't see anything for a bit. Hence only print and log 
    # if you get something 
    if line: 
     print(line) 
     logging.info(line) 

    # Check to see when we can stop - after the child is done. 
    # The return code will be set to the value of the child's exit code, 
    # so it won't be None any more. 

    rc = p.commands[0].process.poll() 
    # if no more output and subprocess is done, break 
    if not line and rc is not None: 
     break 

Jeśli uruchomisz powyższy skrypt , Ci wydrukowany do konsoli:

$ python subptest.py 
Message 1 
Message 2 
Message 3 
Message 4 
Message 5 
Message 6 
Message 7 
Message 8 
Message 9 
Message 10 

A kiedy sprawdzić plik dziennika, widzimy:

$ cat subptest.log 
INFO:root:Message 1 
INFO:root:Message 2 
INFO:root:Message 3 
INFO:root:Message 4 
INFO:root:Message 5 
INFO:root:Message 6 
INFO:root:Message 7 
INFO:root:Message 8 
INFO:root:Message 9 
INFO:root:Message 10 
+0

Dzięki za napiwek! :) –

Powiązane problemy