2

さまざまなロックを使用するマルチスレッドPythonアプリケーションをデバッグしようとしています。

ロックが取得および解放される場所とタイミングを追跡するためにショット全体にlog.debug(...)ステートメントを配置するのではなく、私の考えは、メソッドthreading.Lock.acquire()およびthreading.Lock.release( )を装飾することです。 、および呼び出しの前に次のようなものを付けます。

log.debug("lock::acquire() [%s.%s.%s]" %
          (currentThread().getName(),
           self.__class__.__name__,
           sys._getframe().f_code.co_name))

ここで、logはグローバルロギングオブジェクトです-議論のために。

理想的には、ログエントリの「lock」という名前は実行時に派生する必要があります。これにより、ログでこれらのメソッドが呼び出されるロックオブジェクトに関係なく、名前、装飾された操作、現在のスレッド、クラス、および関数が出力されます。操作(取得|解放)が呼び出されます。

免責事項:上記のコードは、このようなデコレータの実装には不十分であることを認めます。これは、私が達成できると思うことの味を与えるためだけに提供されています。

スレッドライブラリの元のソースコードをドクターせずに、つまり呼び出し元のアプリケーションコード内から、標準ライブラリメソッドを装飾できるかどうか誰かが知っていますか?

おそらく私は間違った木を吠えていますが、デコレータを使用せずに同じ目的を達成するためのより良い方法がありますか?これが実際に当てはまる場合は、ガイダンスを事前に感謝します。

解決策:(lazyrに触発されて)

次のコードは、ロック操作をログに記録し、ロック操作を呼び出すメソッド/関数の名前を示します(Conditionsとその追加のwait()およびnotify()メソッドで動作するようにコードを調整しています)。

# Class to wrap Lock and simplify logging of lock usage
class LogLock(object):
    """
    Wraps a standard Lock, so that attempts to use the
    lock according to its API are logged for debugging purposes

    """
    def __init__(self, name, log):
        self.name = str(name)
        self.log = log
        self.lock = threading.Lock()
        self.log.debug("{0} created {1}".format(
            inspect.stack()[1][3], self.name))

    def acquire(self, blocking=True):
        self.log.debug("{0} trying to acquire {1}".format(
            inspect.stack()[1][3], self.name))
        ret = self.lock.acquire(blocking)
        if ret == True:
            self.log.debug("{0} acquired {1}".format(
                inspect.stack()[1][3], self.name))
        else:
            self.log.debug("{0} non-blocking acquire of {1} lock failed".format(
                inspect.stack()[1][3], self.name))
        return ret

    def release(self):
        self.log.debug("{0} releasing {1}".format(inspect.stack()[1][3], self.name))
        self.lock.release()

    def __enter__(self):
        self.acquire()

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.release()
        return False # Do not swallow exceptions

ログインスタンスがLogLockに渡された場所。initはlogging.Formatterで次のように定義され、呼び出し元のスレッドのIDを取得しました。

# With the following format
log_format = \
        logging.Formatter('%(asctime)s %(levelname)s %(threadName)s %(message)s')
4

1 に答える 1

5

私は最近あなたの問題を抱えていました。この回答のように、スレッド名を自動的にログに記録するようにロガーを設定しました。Lock をサブクラス化できないことがわかったので、次のようにラップする必要がありました。

class LogLock(object):
    def __init__(self, name):
        self.name = str(name)
        self.lock = Lock()

    def acquire(self, blocking=True):
        log.debug("{0:x} Trying to acquire {1} lock".format(
            id(self), self.name))
        ret = self.lock.acquire(blocking)
        if ret == True:
            log.debug("{0:x} Acquired {1} lock".format(
                id(self), self.name))
        else:
            log.debug("{0:x} Non-blocking aquire of {1} lock failed".format(
                id(self), self.name))
        return ret

    def release(self):
        log.debug("{0:x} Releasing {1} lock".format(id(self), self.name))
        self.lock.release()

    def __enter__(self):
        self.acquire()

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.release()
        return False    # Do not swallow exceptions

オブジェクトの ID をログに記録したので、同じ名前の複数のロックを区別できましたが、必要ないかもしれません。

于 2011-03-16T15:45:39.007 に答える