将stdout重定向到仅用于特定线程的文件

时间:2013-02-15 08:51:53

标签: python stdout

我有几个进程都有自己的线程写入stdout并且我很好。现在,我必须添加另一个线程,它将向stdout转储大量垃圾,我不希望这样。

有什么办法可以将单个线程的stdout重定向到文件吗?

更新

owobeid所述,我试过了......我正在将stderr重定向到文件...

def startServer():
    fd = os.open("foo.txt", os.O_RDWR|os.O_CREAT )
    fd2 = 2
    os.dup2(fd, fd2)

    # rest of the code

if __name__ == '__main__':
    threading.Thread(target=startServer).start()
    raise Exception("My Exception")

问题:重定向整个应用的stderr。异常消息也会被重定向到文件中,即使它在线程之外。

5 个答案:

答案 0 :(得分:2)

我偶然发现了这篇文章,同时正在寻找一种方法来做到这一点。我想创建一个交互式Python控制台,它使用ajax代理服务器的请求,并仅返回执行的线程的所有输出。我最终想出来并希望分享我的解决方案。

有一个名为werkzeug的{​​{1}} python库附带的类可以使模块级函数的行为类似于属性。例如,这将使local.LocalProxy完全正常,但它将通过sys.stdout类代理。

LocalProxy

对此进行扩展,然后我编写了一个函数来代替上面的import sys import werkzeug sys.stdout = werkzeug.local.LocalProxy(lambda: sys.stdout) ,如果它是一个不同的线程,则返回一个lambda对象:

StringIO

然后在我想要重定向的任何线程中,我可以调用:

import threading
import sys
import cStringIO
import werkzeug

thread_proxies = {}
def redirect():
    ident = threading.currentThread().ident
    thread_proxies[ident] = cStringIO.StringIO()
    return thread_proxies[ident]

def proxy():
    ident = threading.currentThread().ident
    return thread_proxies.get(ident, sys.stdout)

sys.stdout = werkzeug.local.LocalProxy(proxy)

现在,string_io = redirect() 的所有输出都会写入sys.stdout对象。

但是等等!我需要捕获StringIOsys.stdoutsys.__stdout__sys.stderr,因此我编写了这个库,我在我的代码库中调用了sys.__stderr__

stdout_helpers

现在,在我的应用程序开始时,我打电话给:

import threading
import sys
import cStringIO
from werkzeug import local

# Save all of the objects for use later.
orig___stdout__ = sys.__stdout__
orig___stderr__ = sys.__stderr__
orig_stdout = sys.stdout
orig_stderr = sys.stderr
thread_proxies = {}


def redirect():
    """
    Enables the redirect for the current thread's output to a single cStringIO
    object and returns the object.

    :return: The StringIO object.
    :rtype: ``cStringIO.StringIO``
    """
    # Get the current thread's identity.
    ident = threading.currentThread().ident

    # Enable the redirect and return the cStringIO object.
    thread_proxies[ident] = cStringIO.StringIO()
    return thread_proxies[ident]


def stop_redirect():
    """
    Enables the redirect for the current thread's output to a single cStringIO
    object and returns the object.

    :return: The final string value.
    :rtype: ``str``
    """
    # Get the current thread's identity.
    ident = threading.currentThread().ident

    # Only act on proxied threads.
    if ident not in thread_proxies:
        return

    # Read the value, close/remove the buffer, and return the value.
    retval = thread_proxies[ident].getvalue()
    thread_proxies[ident].close()
    del thread_proxies[ident]
    return retval


def _get_stream(original):
    """
    Returns the inner function for use in the LocalProxy object.

    :param original: The stream to be returned if thread is not proxied.
    :type original: ``file``
    :return: The inner function for use in the LocalProxy object.
    :rtype: ``function``
    """
    def proxy():
        """
        Returns the original stream if the current thread is not proxied,
        otherwise we return the proxied item.

        :return: The stream object for the current thread.
        :rtype: ``file``
        """
        # Get the current thread's identity.
        ident = threading.currentThread().ident

        # Return the proxy, otherwise return the original.
        return thread_proxies.get(ident, original)

    # Return the inner function.
    return proxy


def enable_proxy():
    """
    Overwrites __stdout__, __stderr__, stdout, and stderr with the proxied
    objects.
    """
    sys.__stdout__ = local.LocalProxy(_get_stream(sys.__stdout__))
    sys.__stderr__ = local.LocalProxy(_get_stream(sys.__stderr__))
    sys.stdout = local.LocalProxy(_get_stream(sys.stdout))
    sys.stderr = local.LocalProxy(_get_stream(sys.stderr))


def disable_proxy():
    """
    Overwrites __stdout__, __stderr__, stdout, and stderr with the original
    objects.
    """
    sys.__stdout__ = orig___stdout__
    sys.__stderr__ = orig___stderr__
    sys.stdout = orig_stdout
    sys.stderr = orig_stderr

在我现在称之为的任何主题中:

stdout_helpers.enable_proxy()

答案 1 :(得分:0)

您无法为单个线程重定向stdout,但您可以通过以下方式写入不同的fd:打开一个文件并从该线程调用write()。 stdout和stderr映射到特定的fds,这些fds是整个进程的fd表的一部分,因此在任何一个线程中混乱使用stdout / stderr都会混淆所有这些。

因此,在这种情况下,您不能简单地在单个线程下面使用stdout,这样您就可以仍称为香草'打印'。在先前的答案b / w中产生的线程和主线程的区别是不相关的。您需要选择的线程才能实际打印到单独的fd:

with open('blah', 'w') as f:
    f.write('bla blah\n')

我知道这并没有给你你想要的东西,但对于后来的读者而言,这一点很重要。

在概念上可以帮助的是理解打印命令在引擎盖下做什么。本质上它与fd.write()(格式化格式化细节)的作用相同,但它特别使用stdout作为文件句柄。您可以使用sys.stdout.write()自己完成此操作。

答案 2 :(得分:0)

此答案允许重定向到特定文件,但也可以有多个输出(sys.stdout +文件),还可以将主线程日志重定向到每个线程文件日志(在我的情况下非常有用)。

首先,让我们定义一个用于新重定向的类:

class SysRedirect(object):
    def __init__(self):
        self.terminal = sys.stdout                  # To continue writing to terminal
        self.log={}                                 # A dictionary of file pointers for file logging

    def register(self,filename):                    # To start redirecting to filename
        ident = threading.currentThread().ident     # Get thread ident (thanks @michscoots)
        if ident in self.log:                       # If already in dictionary :
            self.log[ident].close()                 # Closing current file pointer
        self.log[ident] = open(filename, "a")       # Creating a new file pointed associated with thread id

    def write(self, message):
        self.terminal.write(message)                # Write in terminal (comment this line to remove terminal logging)
        ident = threading.currentThread().ident     # Get Thread id
        if ident in self.log:                       # Check if file pointer exists
            self.log[ident].write(message)          # write in file
        else:                                       # if no file pointer 
            for ident in self.log:                  # write in all thread (this can be replaced by a Write in terminal)
                 self.log[ident].write(message)  
     def flush(self):
            #this flush method is needed for python 3 compatibility.
            #this handles the flush command by doing nothing.
            #you might want to specify some extra behavior here.
            pass    

然后,我只需要在主线程中初始化

sys.stdout=SysRedirect()

然后在每个线程中,我只需要注册并指定文件名

sys.stdout.register('threadX.log')

在主线程中,例如,我可以重定向到:

sys.stdout.register('mainthread.log')

但是在我的情况下,我不想注册主线程,因此主线程中的所有std都写在所有其他日志中

答案 3 :(得分:-1)

使用dup2将输出重定向到您选择的文件。将fd设置为文件描述符,将fd2设置为1(stdout)。

注意:在生成的线程内执行此操作,而不是在主线程中执行此操作。

答案 4 :(得分:-3)

我不熟悉Python。但我想你会在Python中有一个日志框架来将日志消息重定向到不同的appender(stdout,文件等)。您的新线程可以使用不同的appender(文件可能是),其他线程可能会记录到stdout appender。

Stdout流对于进程是常见的,它不是每个线程。