11

独自のスレッドがすべて書き込まれているプロセスがいくつかあり、問題ありません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アプリ全体がリダイレクトされます。例外メッセージは、スレッドの外にある場合でも、ファイルにリダイレクトされます。

4

4 に答える 4

9

これを正確に行う方法を探しているときに、この投稿に出くわしました。ajaxを使用してリクエストをサーバーにプロキシし、実行中のスレッドに対してのみすべての出力を返すインタラクティブなPythonコンソールを作成したかったのです。私はそれを理解することになり、私の解決策を共有したいと思いました。

モジュールレベルの関数をプロパティのように動作させることができるwerkzeugと呼ばれる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()

そして、私が今呼んでいるスレッドでは、次のようになります。

string_io = stdout_helpers.redirect()
于 2017-04-27T20:29:51.653 に答える
2

この回答により、特定のファイルにリダイレクトできますが、複数の出力(sys.stdout + file)があり、メインスレッドログを各スレッドファイルログにリダイレクトすることもできます(私の場合は非常に便利でした)。

まず、新しいリダイレクトのクラスを定義しましょう。

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

しかし、私の場合、メインスレッドを登録したくないので、メインスレッドのすべての標準は他のすべてのログに書き込まれます

于 2019-09-18T16:16:43.137 に答える
0

単一のスレッドに対してstdoutをリダイレクトすることはできませんが、たとえばファイルを開いてwrite()そのスレッドから呼び出すことにより、別のfdに書き込むことができます。stdoutとstderrは、プロセス全体のfdテーブルの一部である特定のfdにマップされるため、1つのスレッドでstdout / stderrをいじると、それらすべてがいじります。

したがって、この状況では、単一のスレッドの下にあるstdoutを単純に混乱させることはできません。そのため、たとえばバニラを「印刷」と呼ぶことができます。スポーンされたスレッドとメインスレッドの前の回答で行われた区別は関係ありません。別のfdに実際に印刷するには、選択したスレッドが必要です。

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

私はそれがあなたが望むものをあなたに与えないことを知っています、しかし後の読者のためにこれに沿って来ることは知ることが重要です。

概念的に役立つ可能性があるのは、内部でprintコマンドが何を実行するかを理解することです。基本的には(フォーマットの良さを法として)と同じようなことをfd.write()しますが、特にファイルハンドルとしてstdoutを使用します。これは、を使用して自分で行うことができますsys.stdout.write()

于 2016-11-10T01:24:08.803 に答える
-2

dup2を使用して、出力を選択したファイルにリダイレクトします。fdをファイル記述子に設定し、fd2を1(stdout)に設定します。

注:これは、メインスレッドではなく、生成されたスレッド内で実行します。

于 2013-02-15T08:56:53.337 に答える