159 votes

Comment dupliquer sys.stdout dans un fichier journal en python ?

Edit: étant donné qu'il semble qu'il n'y a pas de solution, ou je suis en train de faire quelque chose de manière non-standard que personne ne le sait - je vais revoir ma question, également, de se poser: Quelle est la meilleure façon d'effectuer la journalisation quand un python application est de faire beaucoup d'appels système?

Mon application a deux modes. En mode interactif, je veux toutes les sorties pour aller à l'écran ainsi que dans un fichier journal, y compris la sortie de tous les appels système. En mode démon, toutes les sorties se passe dans le journal. Mode démon fonctionne très bien en utilisant le système d'exploitation.dup2(). Je ne peux pas trouver un moyen de "tee" toutes les sorties à un journal en mode interactif, sans modification de chaque appel système.


En d'autres termes, je veux la fonctionnalité de la ligne de commande 'tee' pour toute sortie générée par un python application, y compris le système d'appel de sortie.

Pour clarifier:

Pour rediriger toutes les sorties que je fais quelque chose comme ça, et il fonctionne très bien:

# open our log file
so = se = open("%s.log" % self.name, 'w', 0)

# re-open stdout without buffering
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

# redirect stdout and stderr to the log file opened above
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())

La bonne chose à ce sujet est qu'il ne nécessite aucune spéciale d'impression des appels à partir du reste du code. Le code s'exécute également de certaines commandes shell, donc c'est agréable de ne pas avoir à traiter avec chacun de leur production individuelle.

Simplement, je veux faire la même chose, sauf en double au lieu de redirection.

À première vue, je pensais qu'il suffit d'inverser le dup2 de travail. Pourquoi n'est-ce pas? Voici mon test:

import os, sys

### my broken solution:
so = se = open("a.log", 'w', 0)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

os.dup2(sys.stdout.fileno(), so.fileno())
os.dup2(sys.stderr.fileno(), se.fileno())
###

print "kljhf sdf"

os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)

Le fichier ".journal" doit être identique à ce qui était affiché sur l'écran.

146voto

John T Points 14067

J’ai eu ce même problème auparavant et trouvé cet extrait très utile :

de : http://mail.python.org/pipermail/python-list/2007-May/438106.html

80voto

Triptych Points 70247

L' print déclaration d'appel de l' write() méthode de n'importe quel objet que vous attribuez à sys.la sortie standard stdout.

Je voudrais faire tourner une petite classe pour écrire à deux endroits à la fois...

import sys

class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout
        self.log = open("log.dat", "a")

    def write(self, message):
        self.terminal.write(message)
        self.log.write(message)  

sys.stdout = Logger()

Maintenant l' print déclaration d'écho à l'écran et de les ajouter à votre fichier de log:

# prints "1 2" to <stdout> AND log.dat
print "%d %d" % (1,2)

C'est évidemment rapide et sale. Quelques remarques:

  • Vous avez probablement devrait parametize le nom du fichier journal.
  • Vous devriez probablement revenir sys.stdout pour <stdout> si vous ne pas se connecter pendant la durée du programme.
  • Vous souhaitez peut-être la possibilité d'écrire à plusieurs fichiers à la fois, ou de gérer les différents niveaux de journal, etc.

Ce sont tous assez simple que je suis à l'aise en leur laissant que des exercices pour le lecteur. L'idée clé ici est que l' print seulement appelle un "fichier-comme objet" qui vous est assigné sys.stdout.

65voto

Alex Lebedev Points 4273

Ce que vous voulez vraiment est `` module de bibliothèque standard. Créer un journal et fixer les deux gestionnaires, on serait écrit dans un fichier et l’autre à stdout ou stderr.

Consultez journalisation vers plusieurs destinations pour plus de détails

62voto

Jacob Gabrielson Points 8800

Puisque vous êtes à l'aise à la ponte d'un processus externe à partir de votre code, vous pouvez utiliser tee lui-même. Je ne sais pas du tout appels système Unix qui font exactement ce qu' tee .

import subprocess, os, sys

# Unbuffer output
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

tee = subprocess.Popen(["tee", "log.txt"], stdin=subprocess.PIPE)
os.dup2(tee.stdin.fileno(), sys.stdout.fileno())
os.dup2(tee.stdin.fileno(), sys.stderr.fileno())

print "\nstdout"
print >>sys.stderr, "stderr"
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)

Vous pouvez également émuler tee en utilisant le multitraitement package (ou de l'utilisation de traitement si vous êtes à l'aide de Python 2.5 ou version antérieure).

8voto

Atlas1j Points 642

(Ah, juste relire votre question et voir que cela ne s’applique tout à fait.)

Voici un exemple de programme que fait utilise python module de journalisation. Ce module de journalisation dans toutes les versions depuis 2.3. Dans cet exemple la journalisation est configurable par les options de ligne de commande.

En mode tout à fait qu'il ouvre une session uniquement sur un fichier, en mode normal il ouvre une session sur un fichier et la console.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X