我有几个进程都有自己的线程写入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
。异常消息也会被重定向到文件中,即使它在线程之外。
答案 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
对象。
但是等等!我需要捕获StringIO
,sys.stdout
,sys.__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流对于进程是常见的,它不是每个线程。