¿Cómo duplicar sys.stdout en un archivo de registro?

Edit: Ya que parece que no hay una solución, o estoy haciendo algo tan poco estándar que nadie sabe, revisaré mi pregunta para preguntar también: ¿Cuál es la mejor manera de lograr el registro cuando una aplicación de python está haciendo muchas llamadas al sistema?

Mi aplicación tiene dos modos. En el modo interactivo, quiero que toda la salida vaya a la pantalla, así como a un archivo de registro, incluida la salida de las llamadas del sistema. En modo daemon, toda la salida va al registro. El modo Daemon funciona muy bien usando os.dup2() . No puedo encontrar una manera de “dar” todos los resultados a un registro en modo interactivo, sin modificar todas y cada una de las llamadas al sistema.


En otras palabras, quiero la funcionalidad de la línea de comando ‘tee’ para cualquier salida generada por una aplicación python, incluida la salida de llamada del sistema .

Para aclarar:

Para redirigir toda la salida, hago algo como esto, y funciona muy 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()) 

Lo bueno de esto es que no requiere llamadas especiales de impresión del rest del código. El código también ejecuta algunos comandos de shell, por lo que es bueno no tener que lidiar con cada uno de sus resultados individualmente.

Simplemente, quiero hacer lo mismo, excepto duplicar en lugar de redirigir.

Al principio, pensé que simplemente revertir el dup2 debería funcionar. ¿Por qué no? Aquí está mi prueba:

 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) 

El archivo “a.log” debe ser idéntico a lo que se mostró en la pantalla.

Ya que te sientes cómodo generando procesos externos a partir de tu código, puedes usar tee sí mismo. No conozco ninguna llamada al sistema Unix que haga exactamente lo que hace 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) 

También puede emular tee usando el paquete de multiprocesamiento (o usar el procesamiento si está usando Python 2.5 o anterior).

Tuve este mismo problema antes y encontré este fragmento muy útil:

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

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

La statement de print llamará al método write() de cualquier objeto que asigne a sys.stdout.

Haría una pequeña clase para escribir en dos lugares a la vez …

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

Ahora la statement de print se reflejará en la pantalla y se agregará a su archivo de registro:

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

Esto obviamente es rápido y sucio. Algunas notas:

  • Probablemente debería parametizar el nombre de archivo de registro.
  • Probablemente debería revertir sys.stdout a si no va a iniciar sesión durante la duración del progtwig.
  • Es posible que desee la capacidad de escribir en varios archivos de registro a la vez, o manejar diferentes niveles de registro, etc.

Todos estos son tan sencillos que me siento cómodo dejándolos como ejercicios para el lector. La idea clave aquí es que la print simplemente llama a un “objeto similar a un archivo” que está asignado a sys.stdout .

Lo que realmente quieres es un módulo de logging de la biblioteca estándar. Cree un registrador y adjunte dos manejadores, uno estaría escribiendo en un archivo y el otro en stdout o stderr.

Consulte Registro en múltiples destinos para más detalles.

Aquí hay otra solución, que es más general que las otras: admite la división de salida (escrita en sys.stdout ) a cualquier número de objetos similares a archivos. No hay ningún requisito de que __stdout__ sea ​​incluido.

 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: Esta es una prueba de concepto. La implementación aquí no está completa, ya que solo envuelve los métodos de los objetos tipo archivo (p. Ej., Escritura), excluyendo miembros / propiedades / setattr, etc. Sin embargo, probablemente sea lo suficientemente bueno para la mayoría de las personas en su estado actual.

Lo que me gusta de él, aparte de su generalidad, es que está limpio en el sentido de que no hace ninguna llamada directa a write , flush , os.dup2 , etc.

Como se describe en otra parte, tal vez la mejor solución sea usar el módulo de registro directamente:

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

Sin embargo, hay algunas ocasiones (raras) en las que realmente desea redireccionar la salida estándar. Tuve esta situación cuando estaba extendiendo el comando runserver de django que usa imprimir: no quería piratear la fuente de django, pero necesitaba las instrucciones de impresión para ir a un archivo.

Esta es una forma de redireccionar stdout y stderr fuera del shell utilizando el módulo de registro:

 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' 

Solo debe usar esta implementación de LogFile si realmente no puede usar el módulo de registro directamente.

Escribí una implementación de tee() en Python que debería funcionar en la mayoría de los casos, y también funciona en Windows.

https://github.com/pycontribs/tendo

Además, puede usarlo en combinación con el módulo de logging de Python si lo desea.

(Ah, solo vuelve a leer tu pregunta y verás que esto no se aplica del todo).

Aquí hay un progtwig de ejemplo que hace uso del módulo de registro de python . Este módulo de registro ha estado en todas las versiones desde la 2.3. En este ejemplo, el registro se puede configurar mediante las opciones de la línea de comandos.

En modo bastante solo se registrará en un archivo, en modo normal se registrará tanto en un archivo como en la consola.

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

Para completar la respuesta de John T: https://stackoverflow.com/a/616686/395687

__exit__ métodos __enter__ y __exit__ para usarlo como administrador de contexto con la palabra clave with , que proporciona este código

 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 

Entonces puede ser usado como

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

Sé que esta pregunta ha sido respondida repetidamente, pero para esto tomé la respuesta principal de la respuesta de John T y la modifiqué para que contenga la descarga sugerida y siguiera su versión revisada vinculada. También he agregado la entrada y salida como se menciona en la respuesta de cladmi para usar con la statement with. Además, la documentación menciona la os.fsync() archivos con os.fsync() así que también lo he agregado. No sé si realmente necesitas eso pero está ahí.

 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 

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

Otra solución utilizando el módulo de registro:

 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' 

Ninguna de las respuestas anteriores parece responder realmente al problema planteado. Sé que este es un hilo antiguo, pero creo que este problema es mucho más simple de lo que todos lo están haciendo:

 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) 

Ahora esto repetirá todo en el controlador sys.stderr normal y su archivo. Crea otra clase tee_out para sys.stdout .

Según una solicitud de @ user5359531 en los comentarios bajo la respuesta de @John T, aquí hay una copia de la publicación referenciada a la versión revisada de la discusión vinculada en esa respuesta:

 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, 人言落日是天涯,望极天涯不见家 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 restre 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 

Estoy escribiendo una secuencia de comandos para ejecutar secuencias de comandos de línea de cmd. (Debido a que en algunos casos, simplemente no hay un sustituto viable para un comando de Linux, como el caso de rsync).

Lo que realmente quería era usar el mecanismo de registro de Python predeterminado en todos los casos en que fuera posible hacerlo, pero capturar cualquier error cuando algo salía mal y no estaba previsto.

Este código parece hacer el truco. Puede que no sea especialmente elegante o eficiente (aunque no usa una cadena + = cadena, por lo que al menos no tiene ese potencial cuello de botella en particular). Lo estoy publicando en caso de que le dé alguna otra idea útil.

 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) 

Obviamente, si no estás tan sujeto a la fantasía como yo, reemplaza LOG_IDENTIFIER con otra cadena que no quieras ver como alguien alguna vez escribe en un registro.

Si desea registrar todos los resultados en un archivo Y hacerlo en un archivo de texto, puede hacer lo siguiente. Es un poco hacky pero funciona:

 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!") 

EDITAR: tenga en cuenta que esto no registra errores a menos que redirija sys.stderr a sys.stdout

EDIT2: Un segundo problema es que tiene que pasar 1 argumento a diferencia de la función incorporada.

EDIT3: vea el código anterior para escribir stdin y stdout en la consola y archivar con stderr solo para archivar

 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 

Escribí un reemplazo completo para sys.stderr y simplemente sys.stderr el código cambiando el nombre de stderr a stdout para que también esté disponible para reemplazar sys.stdout .

Para hacer esto, creo el mismo tipo de objeto que stderr y stdout actuales, y reenvío todos los métodos al stderr y stdout sistema original:

 import os import sys import logging class StdErrReplament(object): """ How to redirect stdout and stderr to logger in Python https://stackoverflow.com/questions/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python Set a Read-Only Attribute in Python? https://stackoverflow.com/questions/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 https://stackoverflow.com/questions/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? https://stackoverflow.com/questions/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 https://stackoverflow.com/questions/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python Set a Read-Only Attribute in Python? https://stackoverflow.com/questions/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 https://stackoverflow.com/questions/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? https://stackoverflow.com/questions/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 

To use this you can just call StdErrReplament::lock(logger) and StdOutReplament::lock(logger) passing the logger you want to use to send the output text. Por ejemplo:

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

Running this code, you will see on the screen:

introduzca la descripción de la imagen aquí

And on the file contents:

introduzca la descripción de la imagen aquí

If you would like to also see the contents of the log.debug calls on the screen, you will need to add a stream handler to your logger. On this case it would be like this:

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

Which would output like this when running:

introduzca la descripción de la imagen aquí

While it would still saving this to the file my_log_file.txt :

introduzca la descripción de la imagen aquí

When disabling this with StdErrReplament:unlock() , it will only restre the standard behavior of the stderr stream, as the attached logger cannot be never detached because someone else can have a reference to its older version. This is why it is a global singleton which can never dies. Therefore, in case of reloading this module with imp or something else, it will never recapture the current sys.stderr as it was already injected on it and have it saved internally.