3

メッセージを表示するためにPythonのロギングモジュールを使用するプロジェクトに取り組んでいます。共通のリポジトリからのモジュールを使用する独自のプロジェクトがあるので、コードのその部分のロギングステートメントを変更したくありません。

ただし、メモリ使用量は私のプログラムではかなり問題になっているようです。そのため、次のように、時間とメッセージとともに、各ログステートメントにメモリ使用量を記録したいと思います。

YYYY-MM-DD HH:MM:SS,mmm NAME LEVEL MemTotal:#M,Swap:#M MESSAGE

ロギングモジュールを使用してこれを行う簡単な方法はありますか?

フィルタを使用してコンテキスト情報を追加する方法だと思います(http://docs.python.org/howto/logging-cookbook.html#filters-contextualを参照)。)が、このフィルターをすべてのレベルとロガーのすべてのインスタンスに一度に追加する方法はないようです。ドキュメントでは、外部モジュールでフィルターを使用しないため、ロガーではなくハンドラーにフィルターを追加することを提案しています。ただし、これを最も明白な方法(ハンドラーを作成し、フィルターを追加してから、ハンドラーをルートロガーにアタッチする)で行うと、予期しない動作が発生しました。出力がまったくなく、エラーメッセージも表示されないか、(basicConfigを最初に使用した場合)エラーメッセージが表示されることを除いて、正しい動作が得られました。最後のケースでは、実際には2つのハンドラーがあり、1つは正しく機能し、もう1つは誤って動作していると思います。

これまでのところ、私は次の解決策を考え出しましたが、これはあまりエレガントではないと思います(https://stackoverflow.com/a/938800/819110に感謝します)。醜い部分は、ロガーからハンドラーを手動で抽出し、それにフィルターを追加する必要があることです。設定ファイルにフィルタを追加できないようです。これははるかに便利です。それでも、このアプローチは(Linuxでは)機能するようですが、これを行うにはもっと簡単な方法があるはずだと思います。

import logging
import external_module

class MemuseFilter(logging.Filter):
    def filter(self, record):
        """ This function overrides logging.Filter, adds memuse as a field
        """ 
        record.memuse = self.str_mem()
        return True

    # Following code from https://stackoverflow.com/a/938800/819110: 
    _proc_status = '/proc/%d/status' % os.getpid()
    _scale = {'kB': 1024.0, 'mB': 1024.0*1024.0,
              'KB': 1024.0, 'MB': 1024.0*1024.0}

    def _VmB(self,VmKey):
        """Private.
        """
        # get pseudo file  /proc/<pid>/status
        try:
            t = open(self._proc_status)
            v = t.read()
            t.close()
        except:
            return 0.0  # non-Linux?
        # get VmKey line e.g. 'VmRSS:  9999  kB\n ...'
        i = v.index(VmKey)
        v = v[i:].split(None, 3)  # whitespace
        if len(v) < 3:
            return 0.0  # invalid format?
        # convert Vm value to bytes
        return float(v[1]) * self._scale[v[2]]

    def memory(self,since=0.0):
        """Return memory usage in bytes.
        """
        return self._VmB('VmSize:') - since

    def swapsize(self,since=0.0):
        """Return swap size in bytes.
        """
        return self._VmB('VmSwap:') - since

    def byte_to_mb(self,byte):
        """return size in MB (being lazy)
        """
        return byte/(1024*1024)

    def str_mem(self):
        """Return a string with the total memuse and swap size in MB
        """
        return "MemTotal:%.0fM,Swap:%.0fM"%(\
            self.byte_to_mb(self.memory()),self.byte_to_mb(self.swapsize()) )

if __name__ == '_main__':
    logging.config.fileConfig('logging.conf') # Get basic config
    log = logging.getLogger('')               # Get root logger
    f = MemuseFilter()                        # Create filter
    log.handlers[0].addFilter(f)         # The ugly part:adding filter to handler
    log.warning("Foo")
    function_from_module_using_logging()

読み取りは次のexternal_moduleようになります。

log = logging.getLogger(__name__)
def function_from_module_using_logging():
    log.warning("Bar")

そして、logging.confこれは次のようなものです。

[loggers]
keys=root

[handlers]
keys=fileHandler

[formatters]
keys=memuseFormatter

[logger_root]
level=DEBUG
handlers=fileHandler

[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=memuseFormatter
args=('some.log','w')

[formatter_memuseFormatter]
format=%(asctime)-15s %(name)-5s %(levelname)-8s %(memuse)-22s %(message)s
datefmt=

より良い解決策が大歓迎です!

編集:間違ったSO質問を参照しました。

4

2 に答える 2

1

まず、誤解を解き明かしましょう。ロガーはこの情報を追加するのに適切な場所ではありません。ロガーは、dictでログレベルのキーを検索するだけです。フォーマッタはロガーを調べないため、各ロガーにメモリ使用量を追加する意味はありません。

正しい解決策は、(あなたが行ったように)独自のフォーマッターを作成し、新しい変数を実装することです。これを行うためのより良い方法はありません。たとえば、ログファイルに書き込まれることのないロガー呼び出しのメモリ情報を取得したくない場合があります。

于 2012-06-01T10:17:04.903 に答える
1

Python 2.7以降を使用している場合はdictConfig()、構成でフィルターをサポートするを使用できます。以前のバージョンのPythonを使用している場合は、logutilsプロジェクトを使用してdictConfig()機能を提供できます。への外部依存関係が必要ない場合は、そこから独自のコードにlogutilsコピーすることができます(Djangoが行った/行ったように)。dictConfig()

于 2012-06-01T12:11:19.503 に答える