2012-12-17 9 views
5

Ich schreibe ein Recherche-Tool, und ich habe kürzlich von der Verwendung von "print" -Anweisungen auf die Verwendung der in Python integrierten Protokollfunktionalität umgestellt. Dies, so argumentierte ich, würde es mir erlauben, dem Benutzer die Option zu geben, die Ausgabe in eine Datei zu speichern und sie auf dem Bildschirm auszugeben.Protokollierung von einer externen Anwendung

So weit so gut. Der Teil meines Codes, der sich in Python befindet, verwendet "logger.info" und "logger.error", um sowohl auf den Bildschirm als auch auf eine Datei zu schreiben. "Logger" ist der modulweite Logger. Dieser Teil wirkt wie ein Zauber.

Allerdings verwende ich "subprocess.call" an mehreren Stellen, um eine ausführbare Datei durch die Shell auszuführen. Also, über den gesamten Code, habe ich Linien wie

proc = subprocess.call(command) 

Die Ausgabe dieses Befehls auf dem Bildschirm gedruckt werden würde, wie immer, aber es würde Dump nicht auf die Datei, die der Benutzer angegeben.

Eine mögliche Option wäre ein Rohr in die Datei zu öffnen:

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

Aber das würde Dump nur auf die Datei und nicht auf den Bildschirm.

Grundsätzlich läuft meine Frage darauf hinaus: Gibt es eine Möglichkeit, die ich meine vorhandene Protokollfunktion nutzen kann, ohne einen anderen Handler für Dateien speziell für subprocess.call konstruieren zu müssen? (Vielleicht durch Umleiten der Ausgabe an den Logger?) Oder ist das angesichts der aktuellen Konfiguration unmöglich? Wenn letzteres, wie kann ich das Setup verbessern?

(Oh, auch wäre es toll, wenn die Protokollierung in ‚Echtzeit‘ ist, so dass Nachrichten von der ausführbaren Datei protokolliert werden, wie sie empfangen werden.)

Vielen Dank für jede Hilfe! :)

+1

Lennart Regebros [StreamLogger-Klasse] (http:// Stackoverflow.com/a/4838875/190597) wird in Ihrer Situation gut funktionieren. – unutbu

+0

Danke für die Referenz! Das funktioniert gut für meinen Fall. –

Antwort

3

Anstatt stdout zu einer Datei zu leiten, können Sie es zu einem PIPE leiten, und dann von diesem PIPE lesen und zum Logger schreiben. Etwas wie folgt aus:

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

Allerdings gibt es eine noch einfachere Antwort: Sie haben eine dateiähnliche Objekt mit einem Datei-Handle zu verwenden, aber Sie können eine auf der Rohrleitungen erstellen, die jede Zeile in logging passiert. Du könntest dieses Objekt selbst schreiben, aber, wie @unutbu sagt, jemand hat es bereits in this question getan. Also:

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

Natürlich können Sie auch vorübergehend stdout wickeln an den Logger zu schreiben und nur die Ausgabe durchlaufen, beispielsweise unter Verwendung this confusingly identically-named class:

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

Danke für den Hinweis auf den Code im letzten Link. Es war eine interessante Lektüre. –

3

unutbu's comment ist gut; Sie sollten einen Blick auf Lennart's answer werfen.

Was brauchen Sie so etwas wie die Funktionalität von tee ist, aber das subprocess Modul arbeitet auf der Ebene der OS Griffe, was bedeutet, dass durch die subprocess geschriebenen Daten können nicht von Ihrem Python-Code zu sehen, sagen von einigen File- Wie Objekt schreibst du, welche Logs und Drucke was geschrieben wird.

Ebenso wie Lennart die Antwort verwenden, können Sie diese Art von Sache mit einer Drittanbieter-Bibliothek wie sarge (Offenlegung: Ich bin sein Betreuer). Es funktioniert für mehr als die Protokollierung.Angenommen, Sie haben ein Programm, das ausgegeben, wie erzeugt:

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

und Sie erfassen sie in Ihrem Skript und es auf Bildschirm zu drucken, melden Sie es:

#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 

Wenn Sie Skript ausführen, um das oben , erhalten Sie auf der Konsole ausgegeben:

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

Und wenn wir die Protokolldatei überprüfen, sehen wir:

$ 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

Danke für den Tipp! :) –

Verwandte Themen