我写了一个完整的替代品sys.stderr,只是重复了代码重命名stderr,stdout以使其也可以替代sys.stdout。
为此,我创建与current stderr和相同的对象类型stdout,并将所有方法转发到原始系统stderr和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
要使用此功能,您只需调用StdErrReplament::lock(logger)并StdOutReplament::lock(logger)
传递要用于发送输出文本的记录器即可。例如:
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" )
运行此代码,您将在屏幕上看到: 

并在文件内容上:

如果您还希望log.debug在屏幕上看到呼叫的内容,则需要在记录器中添加流处理程序。在这种情况下,将是这样的:
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" )
运行时将输出如下内容:

虽然它仍然会将其保存到文件中my_log_file.txt:

当使用禁用此功能时StdErrReplament:unlock(),它将仅恢复stderr流的标准行为,因为附加的记录器永远都不会分离,因为其他人可以引用其旧版本。这就是为什么它是永远不会消失的全球单身人士的原因。因此,万一重新加载该模块imp或其他模块,它将永远不会重新捕获sys.stderr已注入到模块中的电流并在内部保存它。