Come duplicare sys.stdout in un file di registro?


149

Modifica: dal momento che sembra che non ci sia soluzione, o sto facendo qualcosa di così non standard che nessuno lo sa, rivedrò la mia domanda per chiedere anche: Qual è il modo migliore per realizzare la registrazione quando un'app Python sta realizzando un molte chiamate di sistema?

La mia app ha due modalità. In modalità interattiva, voglio che tutto l'output vada sullo schermo e su un file di registro, incluso l'output di qualsiasi chiamata di sistema. In modalità demone, tutto l'output passa al registro. La modalità Daemon funziona benissimo os.dup2(). Non riesco a trovare un modo per "collegare" tutti gli output a un registro in modalità interattiva, senza modificare ogni chiamata di sistema.


In altre parole, voglio la funzionalità della riga di comando 'tee' per qualsiasi output generato da un'app Python, incluso l'output delle chiamate di sistema .

Chiarire:

Per reindirizzare tutto l'output faccio qualcosa del genere, e funziona benissimo:

# 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 cosa bella di questo è che non richiede chiamate di stampa speciali dal resto del codice. Il codice esegue anche alcuni comandi della shell, quindi è bello non dover gestire anche ciascuno dei loro output singolarmente.

Semplicemente, voglio fare lo stesso, tranne la duplicazione anziché il reindirizzamento.

A prima vista, ho pensato che semplicemente invertire quello dup2che avrebbe funzionato. Perché no? Ecco il mio 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("foo bar")

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

Il file "a.log" dovrebbe essere identico a quello che è stato visualizzato sullo schermo.


Se guardi la pagina man ( manpagez.com/man/2/dup2 ) il secondo argomento di dup2 è sempre chiuso (se è già aperto). Quindi nella tua "soluzione rotta" si sta chiudendo così e se e quindi riassegnando i loro file a sys.stdout.
Jacob Gabrielson,

1
Ri: la tua modifica: questo non è raro, l'ho fatto un paio di volte (in altre lingue). Mentre Unix consentirà più "alias" per lo stesso handle di file, non "dividerà" un handle di file (copiandolo in più altri). Quindi devi implementare "tee" da solo (o semplicemente usare "tee", vedi la mia risposta rozza).
Jacob Gabrielson,

Penso che la risposta di JohnT sia migliore di quella effettivamente accettata. Potresti voler cambiare la risposta accettata.
Phong

"Sto facendo qualcosa di così non standard" - lo sei davvero, le persone inviano semplicemente i loro log a stderr e gestiscono dalla riga di comando.
Khachik,

Risposte:


55

Dal momento che sei a tuo agio nel generare processi esterni dal tuo codice, potresti usarlo tee. Non conosco nessuna chiamata di sistema Unix che faccia esattamente quello che teefa.

# Note this version was written circa Python 2.6, see below for
# an updated 3.3+-compatible version.
import subprocess, os, sys

# Unbuffer output (this ensures the output is in the correct order)
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)

Puoi anche emulare teeusando il pacchetto multiprocessing (o usare l' elaborazione se stai usando Python 2.5 o precedente).

Aggiornare

Ecco una versione compatibile con Python 3.3 +:

import subprocess, os, sys

tee = subprocess.Popen(["tee", "log.txt"], stdin=subprocess.PIPE)
# Cause tee's stdin to get a copy of our stdin/stdout (as well as that
# of any child processes we spawn)
os.dup2(tee.stdin.fileno(), sys.stdout.fileno())
os.dup2(tee.stdin.fileno(), sys.stderr.fileno())

# The flush flag is needed to guarantee these lines are written before
# the two spawned /bin/ls processes emit any output
print("\nstdout", flush=True)
print("stderr", file=sys.stderr, flush=True)

# These child processes' stdin/stdout are 
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)

28
Bene, questa risposta funziona, quindi la accetterò. Tuttavia, mi fa sentire sporco.
drue

2
Ho appena pubblicato un'implementazione di tee pura (compatibile con py2 / 3) che può essere eseguita su qualsiasi piattaforma e può essere utilizzata anche in diverse configurazioni di registrazione. stackoverflow.com/questions/616645/…
sorin

8
Se Python funziona su una delle mie macchine e la soluzione no, non è una soluzione Pythonic. Downvoted a causa di quello.
Anatoly Techtonik,

2
Secondo questo post la linea sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)non funziona più da Python 3.3 (vedi PEP 3116)
Ken Myers,

1
Ho ricevuto l'errore "sys: 1: ResourceWarning: file non chiuso <_io.BufferedWriter name = 5>", quindi ho dovuto aggiungere tee.stdin.close()alla fine del mio programma. Ottengo anche "ResourceWarning: il sottoprocesso 1842 è ancora in esecuzione" e l'aggiunta sys.stdout.close(); sys.stderr.close()alla fine del programma lo risolve.
Matthieu,

136

Ho avuto lo stesso problema in precedenza e ho trovato questo frammento molto utile:

class Tee(object):
    def __init__(self, name, mode):
        self.file = open(name, mode)
        self.stdout = sys.stdout
        sys.stdout = self
    def __del__(self):
        sys.stdout = self.stdout
        self.file.close()
    def write(self, data):
        self.file.write(data)
        self.stdout.write(data)
    def flush(self):
        self.file.flush()

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


7
+1 per gestire la riassegnazione interna di sys.stdout in modo da poter terminare la registrazione eliminando l'oggetto Tee
Ben Blank

12
Aggiungerei un filo a quello. Ad esempio: 'self.file.flush ()'
Luke Stanley il

4
Non sono d'accordo sul modulo di registrazione. Eccellente per un po 'di armeggiare. La registrazione è troppo grande per questo.
Kobor42,

4
Assicurati di annotare la versione rivista in questo seguito alla discussione collegata nella risposta.
martineau,

4
Non funzionerà. __del__non viene chiamato fino alla fine dell'esecuzione. Vedi stackoverflow.com/questions/6104535/…
Nux

77

L' printistruzione chiamerà il write()metodo di qualsiasi oggetto assegnato a sys.stdout.

Farei girare una piccola classe per scrivere in due posti contemporaneamente ...

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()

Ora la printdichiarazione risuonerà sia sullo schermo che aggiungerà al tuo file di registro:

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

Questo è ovviamente veloce e sporco. Alcune note:

  • Probabilmente dovresti parametizzare il nome del file di registro.
  • Probabilmente dovresti ripristinare sys.stdout <stdout>se non effettuerai la registrazione per la durata del programma.
  • Potresti voler scrivere su più file di registro contemporaneamente o gestire diversi livelli di registro, ecc.

Sono abbastanza semplici da essere a mio agio nel lasciarli come esercizi per il lettore. L'intuizione chiave qui è che printchiama semplicemente un "oggetto simile a un file" a cui è assegnato sys.stdout.


Esattamente quello che stavo per pubblicare, praticamente. +1 quando si risolve il problema con write non avendo un argomento autonomo. Inoltre, sarebbe meglio progettare avere il file su cui scrivere per passare. Diavolo, potrebbe anche essere un design migliore avere stdout passato.
Devin Jeanpierre

@Devin, sì, questo è stato rapido e sporco, prenderò alcune note per possibili miglioramenti iniziali.
Trittico

7
Ho selezionato questa risposta troppo presto. Funziona alla grande per "stampa", ma non tanto per l'output di comandi esterni.
drue

2
La classe Logger dovrebbe anche definire un metodo flush () come "def flush (): self.terminal.flush (); self.log.flush ()"
blokeley

5
Tu dici The print statement will call the write() method of any object you assign to sys.stdout. E che dire delle altre funzioni che inviano i dati a stdout non usando print. Ad esempio, se creo un processo usando il subprocess.callsuo output va alla console ma non al log.datfile ... c'è un modo per risolverlo?
jpo38

64

Quello che vuoi veramente è il loggingmodulo dalla libreria standard. Crea un logger e collega due gestori, uno scriverà su un file e l'altro su stdout o stderr.

Vedere Registrazione in più destinazioni per i dettagli


9
Il modulo di registrazione non registra eccezioni e altri output importanti su stdout, che può essere utile quando si analizzano i log sul server di build (ad esempio).
Anatoly Techtonik,

2
loggingIl modulo non reindirizzerà l'output da chiamate di sistema comeos.write(1, b'stdout')
jfs

17

Ecco un'altra soluzione, che è più generale delle altre: supporta la suddivisione dell'output (scritto su sys.stdout) in qualsiasi numero di oggetti simili a file. Non è richiesto che __stdout__sia incluso.

import sys

class multifile(object):
    def __init__(self, files):
        self._files = files
    def __getattr__(self, attr, *args):
        return self._wrap(attr, *args)
    def _wrap(self, attr, *args):
        def g(*a, **kw):
            for f in self._files:
                res = getattr(f, attr, *args)(*a, **kw)
            return res
        return g

# for a tee-like behavior, use like this:
sys.stdout = multifile([ sys.stdout, open('myfile.txt', 'w') ])

# all these forms work:
print 'abc'
print >>sys.stdout, 'line2'
sys.stdout.write('line3\n')

NOTA: questa è una prova di concetto. L'implementazione qui non è completa, poiché avvolge solo i metodi degli oggetti simili a file (ad esempio write), tralasciando membri / proprietà / setattr, ecc. Tuttavia, è probabilmente abbastanza buono per la maggior parte delle persone così com'è attualmente.

Quello che mi piace a questo proposito, a parte la sua generalità, è che è pulito, nel senso non fa tutte le chiamate dirette a write, flush, os.dup2, etc.


3
Avrei init prendere * file non file, ma per il resto sì, questo. Nessuna delle altre soluzioni isola la funzionalità "tee" senza cercare di risolvere altri problemi. Se si desidera inserire un prefisso su tutto ciò che viene emesso, è possibile racchiudere questa classe in una classe di scrittura prefisso. (Se vuoi mettere un prefisso su un solo flusso, avvolgi un flusso e lo passi a questa classe.) Questo ha anche il vantaggio che multifile ([]) crea un file che ignora tutto (come open ('/ dev /nullo')).
Ben

Perché _wrapqui? Non potresti copiare il codice lì dentro __getattr__e funziona allo stesso modo?
Timotree,

@Ben in realtà multifile([])crea un file che genera un UnboundLocalErrorogni volta che chiami uno dei suoi metodi. ( resviene restituito senza essere stato assegnato)
Timotree

13

Come descritto altrove, forse la soluzione migliore è utilizzare direttamente il modulo di registrazione:

import logging

logging.basicConfig(level=logging.DEBUG, filename='mylog.log')
logging.info('this should to write to the log file')

Tuttavia, ci sono alcune (rare) occasioni in cui vuoi davvero reindirizzare stdout. Ho avuto questa situazione quando stavo estendendo il comando di server di Django che utilizza print: non volevo hackerare il sorgente di django ma avevo bisogno delle istruzioni di stampa per andare su un file.

Questo è un modo per reindirizzare stdout e stderr lontano dalla shell usando il modulo di registrazione:

import logging, sys

class LogFile(object):
    """File-like object to log text using the `logging` module."""

    def __init__(self, name=None):
        self.logger = logging.getLogger(name)

    def write(self, msg, level=logging.INFO):
        self.logger.log(level, msg)

    def flush(self):
        for handler in self.logger.handlers:
            handler.flush()

logging.basicConfig(level=logging.DEBUG, filename='mylog.log')

# Redirect stdout and stderr
sys.stdout = LogFile('stdout')
sys.stderr = LogFile('stderr')

print 'this should to write to the log file'

È consigliabile utilizzare questa implementazione LogFile solo se non è possibile utilizzare direttamente il modulo di registrazione.


11

Ho scritto tee()un'implementazione in Python che dovrebbe funzionare nella maggior parte dei casi e funziona anche su Windows.

https://github.com/pycontribs/tendo

Inoltre, puoi usarlo in combinazione con il loggingmodulo di Python se vuoi.


Hmm - quel link non funziona più - in qualsiasi altro luogo può essere trovato?
Danny Staple,

1
wow, il tuo pacchetto oscilla, soprattutto se sai quanto è ingombrante la cultura della console di Windows ma non ti sei arreso per farlo funzionare!
n611x007

8

(Ah, rileggi la tua domanda e vedi che questo non si applica del tutto.)

Ecco un programma di esempio che utilizza il modulo di registrazione di Python . Questo modulo di registrazione è presente in tutte le versioni dalla 2.3. In questo esempio la registrazione è configurabile dalle opzioni della riga di comando.

In modalità tranquilla accederà solo a un file, in modalità normale accederà sia a un file che alla console.

import os
import sys
import logging
from optparse import OptionParser

def initialize_logging(options):
    """ Log information based upon users options"""

    logger = logging.getLogger('project')
    formatter = logging.Formatter('%(asctime)s %(levelname)s\t%(message)s')
    level = logging.__dict__.get(options.loglevel.upper(),logging.DEBUG)
    logger.setLevel(level)

    # Output logging information to screen
    if not options.quiet:
        hdlr = logging.StreamHandler(sys.stderr)
        hdlr.setFormatter(formatter)
        logger.addHandler(hdlr)

    # Output logging information to file
    logfile = os.path.join(options.logdir, "project.log")
    if options.clean and os.path.isfile(logfile):
        os.remove(logfile)
    hdlr2 = logging.FileHandler(logfile)
    hdlr2.setFormatter(formatter)
    logger.addHandler(hdlr2)

    return logger

def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    # Setup command line options
    parser = OptionParser("usage: %prog [options]")
    parser.add_option("-l", "--logdir", dest="logdir", default=".", help="log DIRECTORY (default ./)")
    parser.add_option("-v", "--loglevel", dest="loglevel", default="debug", help="logging level (debug, info, error)")
    parser.add_option("-q", "--quiet", action="store_true", dest="quiet", help="do not log to console")
    parser.add_option("-c", "--clean", dest="clean", action="store_true", default=False, help="remove old log file")

    # Process command line options
    (options, args) = parser.parse_args(argv)

    # Setup logger format and output locations
    logger = initialize_logging(options)

    # Examples
    logger.error("This is an error message.")
    logger.info("This is an info message.")
    logger.debug("This is a debug message.")

if __name__ == "__main__":
    sys.exit(main())

Buona risposta. Ho visto alcuni modi davvero contorti di replicare il logging sulla console, ma creare uno StreamHandler con stderr è stata la risposta che stavo cercando :)
Meatvest

Il codice è carino, non risponde alla domanda: questo genera il log in un file e stderr, la domanda originale era chiedere di duplicare lo stderr in un file di log.
emem

8

Per completare la risposta di John T: https://stackoverflow.com/a/616686/395687

Ho aggiunto __enter__e __exit__metodi per usarlo come gestore del contesto con la withparola chiave, che fornisce questo codice

class Tee(object):
    def __init__(self, name, mode):
        self.file = open(name, mode)
        self.stdout = sys.stdout
        sys.stdout = self

    def __del__(self):
        sys.stdout = self.stdout
        self.file.close()

    def write(self, data):
        self.file.write(data)
        self.stdout.write(data)

    def __enter__(self):
        pass

    def __exit__(self, _type, _value, _traceback):
        pass

Può quindi essere usato come

with Tee('outfile.log', 'w'):
    print('I am written to both stdout and outfile.log')

1
__del____exit__
Sposterei

1
In effetti, penso che usare __del__sia una cattiva idea. Dovrebbe essere spostato in una funzione di "chiusura" che viene richiamata __exit__.
cladmi,

7

So che a questa domanda è stata data una risposta ripetuta, ma per questo ho preso la risposta principale dalla risposta di John T e l'ho modificata in modo che contenga il colore suggerito e abbia seguito la sua versione rivista. Ho anche aggiunto enter e exit come menzionato nella risposta di cladmi per l'uso con l'istruzione with. Inoltre, la documentazione menziona lo scaricamento dei file usando os.fsync()quindi ho aggiunto anche quello. Non so se ne hai davvero bisogno, ma è lì.

import sys, os

class Logger(object):
    "Lumberjack class - duplicates sys.stdout to a log file and it's okay"
    #source: https://stackoverflow.com/q/616645
    def __init__(self, filename="Red.Wood", mode="a", buff=0):
        self.stdout = sys.stdout
        self.file = open(filename, mode, buff)
        sys.stdout = self

    def __del__(self):
        self.close()

    def __enter__(self):
        pass

    def __exit__(self, *args):
        self.close()

    def write(self, message):
        self.stdout.write(message)
        self.file.write(message)

    def flush(self):
        self.stdout.flush()
        self.file.flush()
        os.fsync(self.file.fileno())

    def close(self):
        if self.stdout != None:
            sys.stdout = self.stdout
            self.stdout = None

        if self.file != None:
            self.file.close()
            self.file = None

Puoi quindi usarlo

with Logger('My_best_girlie_by_my.side'):
    print("we'd sing sing sing")

o

Log=Logger('Sleeps_all.night')
print('works all day')
Log.close()

Molti Thnaks @Status hai risolto la mia domanda ( stackoverflow.com/questions/39143417/… ). Metterò un link alla tua soluzione.
Mohammad ElNesr,

1
@MohammadElNesr Ho appena realizzato un problema con il codice quando viene utilizzato con un'istruzione with. L'ho risolto e ora si chiude correttamente alla fine di un blocco con.
Stato

1
Questo ha funzionato benissimo per me, era necessario solo cambiare modalità mode="ab"e writefunzioneself.file.write(message.encode("utf-8"))
enwetws

4

un'altra soluzione che utilizza il modulo di registrazione:

import logging
import sys

log = logging.getLogger('stdxxx')

class StreamLogger(object):

    def __init__(self, stream, prefix=''):
        self.stream = stream
        self.prefix = prefix
        self.data = ''

    def write(self, data):
        self.stream.write(data)
        self.stream.flush()

        self.data += data
        tmp = str(self.data)
        if '\x0a' in tmp or '\x0d' in tmp:
            tmp = tmp.rstrip('\x0a\x0d')
            log.info('%s%s' % (self.prefix, tmp))
            self.data = ''


logging.basicConfig(level=logging.INFO,
                    filename='text.log',
                    filemode='a')

sys.stdout = StreamLogger(sys.stdout, '[stdout] ')

print 'test for stdout'

3

Nessuna delle risposte sopra sembra davvero rispondere al problema posto. So che questo è un vecchio thread, ma penso che questo problema sia molto più semplice di quanto tutti lo stiano facendo:

class tee_err(object):

 def __init__(self):
    self.errout = sys.stderr

    sys.stderr = self

    self.log = 'logfile.log'
    log = open(self.log,'w')
    log.close()

 def write(self, line):

    log = open(self.log,'a')
    log.write(line)
    log.close()   

    self.errout.write(line)

Ora questo ripeterà tutto al normale gestore sys.stderr e al tuo file. Crea un'altra classe tee_outper sys.stdout.


2
Una risposta simile e migliore è stata pubblicata più di due anni prima di questa: stackoverflow.com/a/616686 . Il tuo metodo è molto costoso: ogni chiamata a tee=tee_err();tee.write('');tee.write('');...opens + chiude un file per ciascuno write. Vedere stackoverflow.com/q/4867468 e stackoverflow.com/q/164053 per argomenti contrari a questa pratica.
Rob W, il

3

Secondo una richiesta di @ user5359531 nei commenti sotto la risposta di @John T , ecco una copia del post di riferimento alla versione rivista della discussione collegata in quella risposta:

Issue of redirecting the stdout to both file and screen
Gabriel Genellina gagsl-py2 at yahoo.com.ar
Mon May 28 12:45:51 CEST 2007

    Previous message: Issue of redirecting the stdout to both file and screen
    Next message: Formal interfaces with Python
    Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]

En Mon, 28 May 2007 06:17:39 -0300, 人言落日是天涯,望极天涯不见家
<kelvin.you at gmail.com> escribió:

> I wanna print the log to both the screen and file, so I simulatered a
> 'tee'
>
> class Tee(file):
>
>     def __init__(self, name, mode):
>         file.__init__(self, name, mode)
>         self.stdout = sys.stdout
>         sys.stdout = self
>
>     def __del__(self):
>         sys.stdout = self.stdout
>         self.close()
>
>     def write(self, data):
>         file.write(self, data)
>         self.stdout.write(data)
>
> Tee('logfile', 'w')
> print >>sys.stdout, 'abcdefg'
>
> I found that it only output to the file, nothing to screen. Why?
> It seems the 'write' function was not called when I *print* something.

You create a Tee instance and it is immediately garbage collected. I'd
restore sys.stdout on Tee.close, not __del__ (you forgot to call the
inherited __del__ method, btw).
Mmm, doesn't work. I think there is an optimization somewhere: if it looks
like a real file object, it uses the original file write method, not yours.
The trick would be to use an object that does NOT inherit from file:

import sys
class TeeNoFile(object):
     def __init__(self, name, mode):
         self.file = open(name, mode)
         self.stdout = sys.stdout
         sys.stdout = self
     def close(self):
         if self.stdout is not None:
             sys.stdout = self.stdout
             self.stdout = None
         if self.file is not None:
             self.file.close()
             self.file = None
     def write(self, data):
         self.file.write(data)
         self.stdout.write(data)
     def flush(self):
         self.file.flush()
         self.stdout.flush()
     def __del__(self):
         self.close()

tee=TeeNoFile('logfile', 'w')
print 'abcdefg'
print 'another line'
tee.close()
print 'screen only'
del tee # should do nothing

--
Gabriel Genellina

1

Sto scrivendo uno script per eseguire script cmd-line. (Perché in alcuni casi, non esiste un sostituto valido per un comando Linux - come il caso di rsync.)

Quello che volevo davvero era usare il meccanismo di registrazione predefinito di Python in ogni caso in cui fosse possibile farlo, ma comunque catturare qualsiasi errore quando qualcosa andava storto che non era previsto.

Questo codice sembra fare il trucco. Potrebbe non essere particolarmente elegante o efficiente (anche se non usa string + = string, quindi almeno non ha quel particolare potenziale collo di bottiglia). Lo sto pubblicando nel caso in cui dia a qualcun altro qualche idea utile.

import logging
import os, sys
import datetime

# Get name of module, use as application name
try:
  ME=os.path.split(__file__)[-1].split('.')[0]
except:
  ME='pyExec_'

LOG_IDENTIFIER="uuu___( o O )___uuu "
LOG_IDR_LENGTH=len(LOG_IDENTIFIER)

class PyExec(object):

  # Use this to capture all possible error / output to log
  class SuperTee(object):
      # Original reference: http://mail.python.org/pipermail/python-list/2007-May/442737.html
      def __init__(self, name, mode):
          self.fl = open(name, mode)
          self.fl.write('\n')
          self.stdout = sys.stdout
          self.stdout.write('\n')
          self.stderr = sys.stderr

          sys.stdout = self
          sys.stderr = self

      def __del__(self):
          self.fl.write('\n')
          self.fl.flush()
          sys.stderr = self.stderr
          sys.stdout = self.stdout
          self.fl.close()

      def write(self, data):
          # If the data to write includes the log identifier prefix, then it is already formatted
          if data[0:LOG_IDR_LENGTH]==LOG_IDENTIFIER:
            self.fl.write("%s\n" % data[LOG_IDR_LENGTH:])
            self.stdout.write(data[LOG_IDR_LENGTH:])

          # Otherwise, we can give it a timestamp
          else:

            timestamp=str(datetime.datetime.now())
            if 'Traceback' == data[0:9]:
              data='%s: %s' % (timestamp, data)
              self.fl.write(data)
            else:
              self.fl.write(data)

            self.stdout.write(data)


  def __init__(self, aName, aCmd, logFileName='', outFileName=''):

    # Using name for 'logger' (context?), which is separate from the module or the function
    baseFormatter=logging.Formatter("%(asctime)s \t %(levelname)s \t %(name)s:%(module)s:%(lineno)d \t %(message)s")
    errorFormatter=logging.Formatter(LOG_IDENTIFIER + "%(asctime)s \t %(levelname)s \t %(name)s:%(module)s:%(lineno)d \t %(message)s")

    if logFileName:
      # open passed filename as append
      fl=logging.FileHandler("%s.log" % aName)
    else:
      # otherwise, use log filename as a one-time use file
      fl=logging.FileHandler("%s.log" % aName, 'w')

    fl.setLevel(logging.DEBUG)
    fl.setFormatter(baseFormatter)

    # This will capture stdout and CRITICAL and beyond errors

    if outFileName:
      teeFile=PyExec.SuperTee("%s_out.log" % aName)
    else:
      teeFile=PyExec.SuperTee("%s_out.log" % aName, 'w')

    fl_out=logging.StreamHandler( teeFile )
    fl_out.setLevel(logging.CRITICAL)
    fl_out.setFormatter(errorFormatter)

    # Set up logging
    self.log=logging.getLogger('pyExec_main')
    log=self.log

    log.addHandler(fl)
    log.addHandler(fl_out)

    print "Test print statement."

    log.setLevel(logging.DEBUG)

    log.info("Starting %s", ME)
    log.critical("Critical.")

    # Caught exception
    try:
      raise Exception('Exception test.')
    except Exception,e:
      log.exception(str(e))

    # Uncaught exception
    a=2/0


PyExec('test_pyExec',None)

Ovviamente, se non sei soggetto a capricci come me, sostituisci LOG_IDENTIFIER con un'altra stringa che non ti piacerebbe mai vedere qualcuno scrivere su un registro.


0

Se si desidera registrare tutto l'output in un file E inviarlo in un file di testo, è possibile effettuare le seguenti operazioni. È un po 'confuso ma funziona:

import logging
debug = input("Debug or not")
if debug == "1":
    logging.basicConfig(level=logging.DEBUG, filename='./OUT.txt')
    old_print = print
    def print(string):
        old_print(string)
        logging.info(string)
print("OMG it works!")

EDIT: Notare che ciò non registra errori a meno che non si reindirizzi sys.stderr a sys.stdout

EDIT2: un secondo problema è che devi passare 1 argomento diversamente dalla funzione builtin.

EDIT3: vedi il codice prima di scrivere stdin e stdout su console e file con stderr solo andando su file

import logging, sys
debug = input("Debug or not")
if debug == "1":
    old_input = input
    sys.stderr.write = logging.info
    def input(string=""):
        string_in = old_input(string)
        logging.info("STRING IN " + string_in)
        return string_in
    logging.basicConfig(level=logging.DEBUG, filename='./OUT.txt')
    old_print = print
    def print(string="", string2=""):
        old_print(string, string2)
        logging.info(string)
        logging.info(string2)
print("OMG")
b = input()
print(a) ## Deliberate error for testing

-1

Ho scritto una sostituzione completa per sys.stderre proprio duplicato il codice di ridenominazione stderrper stdoutrenderlo disponibile anche per sostituire sys.stdout.

Per fare questo ho creare lo stesso tipo di oggetto come l'attuale stderre stdout, e in avanti tutti i metodi al sistema originale stderre stdout:

import os
import sys
import logging

class StdErrReplament(object):
    """
        How to redirect stdout and stderr to logger in Python
        /programming/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python

        Set a Read-Only Attribute in Python?
        /programming/24497316/set-a-read-only-attribute-in-python
    """
    is_active = False

    @classmethod
    def lock(cls, logger):
        """
            Attach this singleton logger to the `sys.stderr` permanently.
        """
        global _stderr_singleton
        global _stderr_default
        global _stderr_default_class_type

        # On Sublime Text, `sys.__stderr__` is set to None, because they already replaced `sys.stderr`
        # by some `_LogWriter()` class, then just save the current one over there.
        if not sys.__stderr__:
            sys.__stderr__ = sys.stderr

        try:
            _stderr_default
            _stderr_default_class_type

        except NameError:
            _stderr_default = sys.stderr
            _stderr_default_class_type = type( _stderr_default )

        # Recreate the sys.stderr logger when it was reset by `unlock()`
        if not cls.is_active:
            cls.is_active = True
            _stderr_write = _stderr_default.write

            logger_call = logger.debug
            clean_formatter = logger.clean_formatter

            global _sys_stderr_write
            global _sys_stderr_write_hidden

            if sys.version_info <= (3,2):
                logger.file_handler.terminator = '\n'

            # Always recreate/override the internal write function used by `_sys_stderr_write`
            def _sys_stderr_write_hidden(*args, **kwargs):
                """
                    Suppress newline in Python logging module
                    /programming/7168790/suppress-newline-in-python-logging-module
                """

                try:
                    _stderr_write( *args, **kwargs )
                    file_handler = logger.file_handler

                    formatter = file_handler.formatter
                    terminator = file_handler.terminator

                    file_handler.formatter = clean_formatter
                    file_handler.terminator = ""

                    kwargs['extra'] = {'_duplicated_from_file': True}
                    logger_call( *args, **kwargs )

                    file_handler.formatter = formatter
                    file_handler.terminator = terminator

                except Exception:
                    logger.exception( "Could not write to the file_handler: %s(%s)", file_handler, logger )
                    cls.unlock()

            # Only create one `_sys_stderr_write` function pointer ever
            try:
                _sys_stderr_write

            except NameError:

                def _sys_stderr_write(*args, **kwargs):
                    """
                        Hides the actual function pointer. This allow the external function pointer to
                        be cached while the internal written can be exchanged between the standard
                        `sys.stderr.write` and our custom wrapper around it.
                    """
                    _sys_stderr_write_hidden( *args, **kwargs )

        try:
            # Only create one singleton instance ever
            _stderr_singleton

        except NameError:

            class StdErrReplamentHidden(_stderr_default_class_type):
                """
                    Which special methods bypasses __getattribute__ in Python?
                    /programming/12872695/which-special-methods-bypasses-getattribute-in-python
                """

                if hasattr( _stderr_default, "__abstractmethods__" ):
                    __abstractmethods__ = _stderr_default.__abstractmethods__

                if hasattr( _stderr_default, "__base__" ):
                    __base__ = _stderr_default.__base__

                if hasattr( _stderr_default, "__bases__" ):
                    __bases__ = _stderr_default.__bases__

                if hasattr( _stderr_default, "__basicsize__" ):
                    __basicsize__ = _stderr_default.__basicsize__

                if hasattr( _stderr_default, "__call__" ):
                    __call__ = _stderr_default.__call__

                if hasattr( _stderr_default, "__class__" ):
                    __class__ = _stderr_default.__class__

                if hasattr( _stderr_default, "__delattr__" ):
                    __delattr__ = _stderr_default.__delattr__

                if hasattr( _stderr_default, "__dict__" ):
                    __dict__ = _stderr_default.__dict__

                if hasattr( _stderr_default, "__dictoffset__" ):
                    __dictoffset__ = _stderr_default.__dictoffset__

                if hasattr( _stderr_default, "__dir__" ):
                    __dir__ = _stderr_default.__dir__

                if hasattr( _stderr_default, "__doc__" ):
                    __doc__ = _stderr_default.__doc__

                if hasattr( _stderr_default, "__eq__" ):
                    __eq__ = _stderr_default.__eq__

                if hasattr( _stderr_default, "__flags__" ):
                    __flags__ = _stderr_default.__flags__

                if hasattr( _stderr_default, "__format__" ):
                    __format__ = _stderr_default.__format__

                if hasattr( _stderr_default, "__ge__" ):
                    __ge__ = _stderr_default.__ge__

                if hasattr( _stderr_default, "__getattribute__" ):
                    __getattribute__ = _stderr_default.__getattribute__

                if hasattr( _stderr_default, "__gt__" ):
                    __gt__ = _stderr_default.__gt__

                if hasattr( _stderr_default, "__hash__" ):
                    __hash__ = _stderr_default.__hash__

                if hasattr( _stderr_default, "__init__" ):
                    __init__ = _stderr_default.__init__

                if hasattr( _stderr_default, "__init_subclass__" ):
                    __init_subclass__ = _stderr_default.__init_subclass__

                if hasattr( _stderr_default, "__instancecheck__" ):
                    __instancecheck__ = _stderr_default.__instancecheck__

                if hasattr( _stderr_default, "__itemsize__" ):
                    __itemsize__ = _stderr_default.__itemsize__

                if hasattr( _stderr_default, "__le__" ):
                    __le__ = _stderr_default.__le__

                if hasattr( _stderr_default, "__lt__" ):
                    __lt__ = _stderr_default.__lt__

                if hasattr( _stderr_default, "__module__" ):
                    __module__ = _stderr_default.__module__

                if hasattr( _stderr_default, "__mro__" ):
                    __mro__ = _stderr_default.__mro__

                if hasattr( _stderr_default, "__name__" ):
                    __name__ = _stderr_default.__name__

                if hasattr( _stderr_default, "__ne__" ):
                    __ne__ = _stderr_default.__ne__

                if hasattr( _stderr_default, "__new__" ):
                    __new__ = _stderr_default.__new__

                if hasattr( _stderr_default, "__prepare__" ):
                    __prepare__ = _stderr_default.__prepare__

                if hasattr( _stderr_default, "__qualname__" ):
                    __qualname__ = _stderr_default.__qualname__

                if hasattr( _stderr_default, "__reduce__" ):
                    __reduce__ = _stderr_default.__reduce__

                if hasattr( _stderr_default, "__reduce_ex__" ):
                    __reduce_ex__ = _stderr_default.__reduce_ex__

                if hasattr( _stderr_default, "__repr__" ):
                    __repr__ = _stderr_default.__repr__

                if hasattr( _stderr_default, "__setattr__" ):
                    __setattr__ = _stderr_default.__setattr__

                if hasattr( _stderr_default, "__sizeof__" ):
                    __sizeof__ = _stderr_default.__sizeof__

                if hasattr( _stderr_default, "__str__" ):
                    __str__ = _stderr_default.__str__

                if hasattr( _stderr_default, "__subclasscheck__" ):
                    __subclasscheck__ = _stderr_default.__subclasscheck__

                if hasattr( _stderr_default, "__subclasses__" ):
                    __subclasses__ = _stderr_default.__subclasses__

                if hasattr( _stderr_default, "__subclasshook__" ):
                    __subclasshook__ = _stderr_default.__subclasshook__

                if hasattr( _stderr_default, "__text_signature__" ):
                    __text_signature__ = _stderr_default.__text_signature__

                if hasattr( _stderr_default, "__weakrefoffset__" ):
                    __weakrefoffset__ = _stderr_default.__weakrefoffset__

                if hasattr( _stderr_default, "mro" ):
                    mro = _stderr_default.mro

                def __init__(self):
                    """
                        Override any super class `type( _stderr_default )` constructor, so we can 
                        instantiate any kind of `sys.stderr` replacement object, in case it was already 
                        replaced by something else like on Sublime Text with `_LogWriter()`.

                        Assures all attributes were statically replaced just above. This should happen in case
                        some new attribute is added to the python language.

                        This also ignores the only two methods which are not equal, `__init__()` and `__getattribute__()`.
                    """
                    different_methods = ("__init__", "__getattribute__")
                    attributes_to_check = set( dir( object ) + dir( type ) )

                    for attribute in attributes_to_check:

                        if attribute not in different_methods \
                                and hasattr( _stderr_default, attribute ):

                            base_class_attribute = super( _stderr_default_class_type, self ).__getattribute__( attribute )
                            target_class_attribute = _stderr_default.__getattribute__( attribute )

                            if base_class_attribute != target_class_attribute:
                                sys.stderr.write( "    The base class attribute `%s` is different from the target class:\n%s\n%s\n\n" % (
                                        attribute, base_class_attribute, target_class_attribute ) )

                def __getattribute__(self, item):

                    if item == 'write':
                        return _sys_stderr_write

                    try:
                        return _stderr_default.__getattribute__( item )

                    except AttributeError:
                        return super( _stderr_default_class_type, _stderr_default ).__getattribute__( item )

            _stderr_singleton = StdErrReplamentHidden()
            sys.stderr = _stderr_singleton

        return cls

    @classmethod
    def unlock(cls):
        """
            Detach this `stderr` writer from `sys.stderr` and allow the next call to `lock()` create
            a new writer for the stderr.
        """

        if cls.is_active:
            global _sys_stderr_write_hidden

            cls.is_active = False
            _sys_stderr_write_hidden = _stderr_default.write



class StdOutReplament(object):
    """
        How to redirect stdout and stderr to logger in Python
        /programming/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python

        Set a Read-Only Attribute in Python?
        /programming/24497316/set-a-read-only-attribute-in-python
    """
    is_active = False

    @classmethod
    def lock(cls, logger):
        """
            Attach this singleton logger to the `sys.stdout` permanently.
        """
        global _stdout_singleton
        global _stdout_default
        global _stdout_default_class_type

        # On Sublime Text, `sys.__stdout__` is set to None, because they already replaced `sys.stdout`
        # by some `_LogWriter()` class, then just save the current one over there.
        if not sys.__stdout__:
            sys.__stdout__ = sys.stdout

        try:
            _stdout_default
            _stdout_default_class_type

        except NameError:
            _stdout_default = sys.stdout
            _stdout_default_class_type = type( _stdout_default )

        # Recreate the sys.stdout logger when it was reset by `unlock()`
        if not cls.is_active:
            cls.is_active = True
            _stdout_write = _stdout_default.write

            logger_call = logger.debug
            clean_formatter = logger.clean_formatter

            global _sys_stdout_write
            global _sys_stdout_write_hidden

            if sys.version_info <= (3,2):
                logger.file_handler.terminator = '\n'

            # Always recreate/override the internal write function used by `_sys_stdout_write`
            def _sys_stdout_write_hidden(*args, **kwargs):
                """
                    Suppress newline in Python logging module
                    /programming/7168790/suppress-newline-in-python-logging-module
                """

                try:
                    _stdout_write( *args, **kwargs )
                    file_handler = logger.file_handler

                    formatter = file_handler.formatter
                    terminator = file_handler.terminator

                    file_handler.formatter = clean_formatter
                    file_handler.terminator = ""

                    kwargs['extra'] = {'_duplicated_from_file': True}
                    logger_call( *args, **kwargs )

                    file_handler.formatter = formatter
                    file_handler.terminator = terminator

                except Exception:
                    logger.exception( "Could not write to the file_handler: %s(%s)", file_handler, logger )
                    cls.unlock()

            # Only create one `_sys_stdout_write` function pointer ever
            try:
                _sys_stdout_write

            except NameError:

                def _sys_stdout_write(*args, **kwargs):
                    """
                        Hides the actual function pointer. This allow the external function pointer to
                        be cached while the internal written can be exchanged between the standard
                        `sys.stdout.write` and our custom wrapper around it.
                    """
                    _sys_stdout_write_hidden( *args, **kwargs )

        try:
            # Only create one singleton instance ever
            _stdout_singleton

        except NameError:

            class StdOutReplamentHidden(_stdout_default_class_type):
                """
                    Which special methods bypasses __getattribute__ in Python?
                    /programming/12872695/which-special-methods-bypasses-getattribute-in-python
                """

                if hasattr( _stdout_default, "__abstractmethods__" ):
                    __abstractmethods__ = _stdout_default.__abstractmethods__

                if hasattr( _stdout_default, "__base__" ):
                    __base__ = _stdout_default.__base__

                if hasattr( _stdout_default, "__bases__" ):
                    __bases__ = _stdout_default.__bases__

                if hasattr( _stdout_default, "__basicsize__" ):
                    __basicsize__ = _stdout_default.__basicsize__

                if hasattr( _stdout_default, "__call__" ):
                    __call__ = _stdout_default.__call__

                if hasattr( _stdout_default, "__class__" ):
                    __class__ = _stdout_default.__class__

                if hasattr( _stdout_default, "__delattr__" ):
                    __delattr__ = _stdout_default.__delattr__

                if hasattr( _stdout_default, "__dict__" ):
                    __dict__ = _stdout_default.__dict__

                if hasattr( _stdout_default, "__dictoffset__" ):
                    __dictoffset__ = _stdout_default.__dictoffset__

                if hasattr( _stdout_default, "__dir__" ):
                    __dir__ = _stdout_default.__dir__

                if hasattr( _stdout_default, "__doc__" ):
                    __doc__ = _stdout_default.__doc__

                if hasattr( _stdout_default, "__eq__" ):
                    __eq__ = _stdout_default.__eq__

                if hasattr( _stdout_default, "__flags__" ):
                    __flags__ = _stdout_default.__flags__

                if hasattr( _stdout_default, "__format__" ):
                    __format__ = _stdout_default.__format__

                if hasattr( _stdout_default, "__ge__" ):
                    __ge__ = _stdout_default.__ge__

                if hasattr( _stdout_default, "__getattribute__" ):
                    __getattribute__ = _stdout_default.__getattribute__

                if hasattr( _stdout_default, "__gt__" ):
                    __gt__ = _stdout_default.__gt__

                if hasattr( _stdout_default, "__hash__" ):
                    __hash__ = _stdout_default.__hash__

                if hasattr( _stdout_default, "__init__" ):
                    __init__ = _stdout_default.__init__

                if hasattr( _stdout_default, "__init_subclass__" ):
                    __init_subclass__ = _stdout_default.__init_subclass__

                if hasattr( _stdout_default, "__instancecheck__" ):
                    __instancecheck__ = _stdout_default.__instancecheck__

                if hasattr( _stdout_default, "__itemsize__" ):
                    __itemsize__ = _stdout_default.__itemsize__

                if hasattr( _stdout_default, "__le__" ):
                    __le__ = _stdout_default.__le__

                if hasattr( _stdout_default, "__lt__" ):
                    __lt__ = _stdout_default.__lt__

                if hasattr( _stdout_default, "__module__" ):
                    __module__ = _stdout_default.__module__

                if hasattr( _stdout_default, "__mro__" ):
                    __mro__ = _stdout_default.__mro__

                if hasattr( _stdout_default, "__name__" ):
                    __name__ = _stdout_default.__name__

                if hasattr( _stdout_default, "__ne__" ):
                    __ne__ = _stdout_default.__ne__

                if hasattr( _stdout_default, "__new__" ):
                    __new__ = _stdout_default.__new__

                if hasattr( _stdout_default, "__prepare__" ):
                    __prepare__ = _stdout_default.__prepare__

                if hasattr( _stdout_default, "__qualname__" ):
                    __qualname__ = _stdout_default.__qualname__

                if hasattr( _stdout_default, "__reduce__" ):
                    __reduce__ = _stdout_default.__reduce__

                if hasattr( _stdout_default, "__reduce_ex__" ):
                    __reduce_ex__ = _stdout_default.__reduce_ex__

                if hasattr( _stdout_default, "__repr__" ):
                    __repr__ = _stdout_default.__repr__

                if hasattr( _stdout_default, "__setattr__" ):
                    __setattr__ = _stdout_default.__setattr__

                if hasattr( _stdout_default, "__sizeof__" ):
                    __sizeof__ = _stdout_default.__sizeof__

                if hasattr( _stdout_default, "__str__" ):
                    __str__ = _stdout_default.__str__

                if hasattr( _stdout_default, "__subclasscheck__" ):
                    __subclasscheck__ = _stdout_default.__subclasscheck__

                if hasattr( _stdout_default, "__subclasses__" ):
                    __subclasses__ = _stdout_default.__subclasses__

                if hasattr( _stdout_default, "__subclasshook__" ):
                    __subclasshook__ = _stdout_default.__subclasshook__

                if hasattr( _stdout_default, "__text_signature__" ):
                    __text_signature__ = _stdout_default.__text_signature__

                if hasattr( _stdout_default, "__weakrefoffset__" ):
                    __weakrefoffset__ = _stdout_default.__weakrefoffset__

                if hasattr( _stdout_default, "mro" ):
                    mro = _stdout_default.mro

                def __init__(self):
                    """
                        Override any super class `type( _stdout_default )` constructor, so we can 
                        instantiate any kind of `sys.stdout` replacement object, in case it was already 
                        replaced by something else like on Sublime Text with `_LogWriter()`.

                        Assures all attributes were statically replaced just above. This should happen in case
                        some new attribute is added to the python language.

                        This also ignores the only two methods which are not equal, `__init__()` and `__getattribute__()`.
                    """
                    different_methods = ("__init__", "__getattribute__")
                    attributes_to_check = set( dir( object ) + dir( type ) )

                    for attribute in attributes_to_check:

                        if attribute not in different_methods \
                                and hasattr( _stdout_default, attribute ):

                            base_class_attribute = super( _stdout_default_class_type, self ).__getattribute__( attribute )
                            target_class_attribute = _stdout_default.__getattribute__( attribute )

                            if base_class_attribute != target_class_attribute:
                                sys.stdout.write( "    The base class attribute `%s` is different from the target class:\n%s\n%s\n\n" % (
                                        attribute, base_class_attribute, target_class_attribute ) )

                def __getattribute__(self, item):

                    if item == 'write':
                        return _sys_stdout_write

                    try:
                        return _stdout_default.__getattribute__( item )

                    except AttributeError:
                        return super( _stdout_default_class_type, _stdout_default ).__getattribute__( item )

            _stdout_singleton = StdOutReplamentHidden()
            sys.stdout = _stdout_singleton

        return cls

    @classmethod
    def unlock(cls):
        """
            Detach this `stdout` writer from `sys.stdout` and allow the next call to `lock()` create
            a new writer for the stdout.
        """

        if cls.is_active:
            global _sys_stdout_write_hidden

            cls.is_active = False
            _sys_stdout_write_hidden = _stdout_default.write

Per usarlo puoi semplicemente chiamare StdErrReplament::lock(logger)e StdOutReplament::lock(logger) passare il logger che vuoi usare per inviare il testo di output. Per esempio:

import os
import sys
import logging

current_folder = os.path.dirname( os.path.realpath( __file__ ) )
log_file_path = os.path.join( current_folder, "my_log_file.txt" )

file_handler = logging.FileHandler( log_file_path, 'a' )
file_handler.formatter = logging.Formatter( "%(asctime)s %(name)s %(levelname)s - %(message)s", "%Y-%m-%d %H:%M:%S" )

log = logging.getLogger( __name__ )
log.setLevel( "DEBUG" )
log.addHandler( file_handler )

log.file_handler = file_handler
log.clean_formatter = logging.Formatter( "", "" )

StdOutReplament.lock( log )
StdErrReplament.lock( log )

log.debug( "I am doing usual logging debug..." )
sys.stderr.write( "Tests 1...\n" )
sys.stdout.write( "Tests 2...\n" )

Eseguendo questo codice, vedrai sullo schermo:

inserisci qui la descrizione dell'immagine

E sul contenuto del file:

inserisci qui la descrizione dell'immagine

Se si desidera visualizzare anche i contenuti delle log.debugchiamate sullo schermo, sarà necessario aggiungere un gestore di flusso al proprio logger. In questo caso sarebbe così:

import os
import sys
import logging

class ContextFilter(logging.Filter):
    """ This filter avoids duplicated information to be displayed to the StreamHandler log. """
    def filter(self, record):
        return not "_duplicated_from_file" in record.__dict__

current_folder = os.path.dirname( os.path.realpath( __file__ ) )
log_file_path = os.path.join( current_folder, "my_log_file.txt" )

stream_handler = logging.StreamHandler()
file_handler = logging.FileHandler( log_file_path, 'a' )

formatter = logging.Formatter( "%(asctime)s %(name)s %(levelname)s - %(message)s", "%Y-%m-%d %H:%M:%S" )
file_handler.formatter = formatter
stream_handler.formatter = formatter
stream_handler.addFilter( ContextFilter() )

log = logging.getLogger( __name__ )
log.setLevel( "DEBUG" )
log.addHandler( file_handler )
log.addHandler( stream_handler )

log.file_handler = file_handler
log.stream_handler = stream_handler
log.clean_formatter = logging.Formatter( "", "" )

StdOutReplament.lock( log )
StdErrReplament.lock( log )

log.debug( "I am doing usual logging debug..." )
sys.stderr.write( "Tests 1...\n" )
sys.stdout.write( "Tests 2...\n" )

Che produrrebbe in questo modo durante l'esecuzione:

inserisci qui la descrizione dell'immagine

Mentre continuerebbe a salvarlo nel file my_log_file.txt:

inserisci qui la descrizione dell'immagine

Disabilitando questo con StdErrReplament:unlock(), ripristinerà solo il comportamento standard del stderrflusso, poiché il logger collegato non può mai essere staccato perché qualcun altro può avere un riferimento alla sua versione precedente. Ecco perché è un singleton globale che non può mai morire. Pertanto, in caso di ricaricare questo modulo con impo qualcos'altro, non riprenderà mai la corrente sys.stderrin quanto era già stata iniettata su di essa e l'ha salvata internamente.


5
un livello sorprendente di complessità accidentale per la duplicazione di un flusso.
Attila Lendvai,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.