30

今日、私は1年ほど前に書いたPythonプロジェクトについて考えていました。そこでは、loggingかなり広範囲に使用していました。hotshotオーバーヘッド(これが私の最大のボトルネックの1つであることを示しています)のために、内部ループのようなシナリオ(90%のコード)で多くのロギング呼び出しをコメントアウトする必要があったことを覚えています。

いつもコメントしたりコメントを外したりせずに、Pythonアプリケーションのロギング呼び出しをプログラムで取り除くための標準的な方法があるのではないかと思います。検査/再コンパイルまたはバイトコード操作を使用してこのようなことを行い、ボトルネックを引き起こしているコードオブジェクトのみをターゲットにすることができると思います。このようにして、コンパイル後のステップとしてマニピュレータを追加し、次のように一元化された構成ファイルを使用できます。

[Leave ERROR and above]
my_module.SomeClass.method_with_lots_of_warn_calls

[Leave WARN and above]
my_module.SomeOtherClass.method_with_lots_of_info_calls

[Leave INFO and above]
my_module.SomeWeirdClass.method_with_lots_of_debug_calls

もちろん、ボトルネックであることが示されているコードオブジェクトに対してのみ、控えめに、おそらく関数ごとの粒度で使用することをお勧めしますlogging。誰かがこのようなことを知っていますか?

注:動的型付けと遅延バインディングのために、これをパフォーマンスの高い方法で実行するのがより困難になることがいくつかあります。たとえば、という名前のメソッドの呼び出しは、debugでラップする必要がある場合がありif not isinstance(log, Logger)ます。いずれにせよ、紳士協定または実行時チェックのいずれかによって、細部のすべてを克服できると思います。:-)

4

10 に答える 10

5

pypreprocessorを使用する

これはPYPI (Python Package Index)にもあり、pip を使用して取得できます。

基本的な使用例を次に示します。

from pypreprocessor import pypreprocessor

pypreprocessor.parse()

#define nologging

#ifdef nologging
...logging code you'd usually comment out manually...
#endif

基本的に、プリプロセッサは、以前に手動で行っていた方法でコードをコメントアウトします。定義内容に応じて、条件付きでその場で実行するだけです。

import ステートメントと parse() ステートメントの間に 'pypreprocessor.removeMeta = True' を追加することで、後処理されたコードからすべてのプリプロセッサ ディレクティブとコメント アウトされたコードを削除することもできます。

バイトコード出力 (.pyc) ファイルには、最適化された出力が含まれます。

補足: pypreprocessor は python2x および python3k と互換性があります。

免責事項: 私は pypreprocessor の作成者です。

于 2011-03-16T03:36:10.827 に答える
4

この方法で assert が使用されることも見てきました。

assert logging.warn('disable me with the -O option') is None

(警告は常に none を返すと思います。そうでない場合は、AssertionError が返されます。

しかし、実際にはこれは面白い方法です。

if __debug__: logging.warn('disable me with the -O option')

-O オプションを使用してその行を含むスクリプトを実行すると、その行は最適化された .pyo コードから削除されます。代わりに、次のように独自の変数がある場合は、(変数の値に関係なく) 常に実行される条件がありますが、条件は関数呼び出しよりも速く実行する必要があります。

my_debug = True
...
if my_debug: logging.warn('disable me by setting my_debug = False')

したがって、デバッグに関する私の理解が正しければ、不要なロギング呼び出しを取り除く良い方法のように思えます。反対に、すべてのアサートも無効になるため、アサートが必要な場合は問題になります。

于 2009-04-04T07:22:20.253 に答える
2

不完全なショートカットとして、MiniMockloggingのようなものを使用して特定のモジュールでモックアウトするのはどうですか?

たとえば、次の場合my_module.py:

import logging
class C(object):
    def __init__(self, *args, **kw):
        logging.info("Instantiating")

の使用を次のように置き換えますmy_module

from minimock import Mock
import my_module
my_module.logging = Mock('logging')
c = my_module.C()

モジュールを最初にインポートする前に、これを一度だけ行う必要があります。

レベル固有の動作を取得することは、特定のメソッドをモックするか、いくつかのメソッドが無効で、他のメソッドが実際のモジュールlogging.getLoggerに委譲されたモック オブジェクトを返すことで十分に簡単です。logging

実際には、おそらく MiniMock をよりシンプルで高速なものに置き換えたいと思うでしょう。少なくとも、使用法を標準出力に出力しないものです! もちろん、これはモジュールAloggingがモジュールBからインポートする問題を処理しません(したがって、AはBのログ粒度もインポートします)...

これは、ログ ステートメントをまったく実行しないほど高速ではありませんが、ログ モジュールの奥深くまで進んで、このレコードが最終的にログに記録されるべきではないことを発見するよりもはるかに高速です。

于 2009-02-07T00:27:15.963 に答える
1

私はいくつかの派手なロギングデコレータ、またはそれらの束を使用します:

def doLogging(logTreshold):
    def logFunction(aFunc):
        def innerFunc(*args, **kwargs):
            if LOGLEVEL >= logTreshold:
                print ">>Called %s at %s"%(aFunc.__name__, time.strftime("%H:%M:%S"))
                print ">>Parameters: ", args, kwargs if kwargs else "" 
            try:
                return aFunc(*args, **kwargs)
            finally:
                print ">>%s took %s"%(aFunc.__name__, time.strftime("%H:%M:%S"))
        return innerFunc
    return logFunction

必要なのは、各モジュールで LOGLEVEL 定数を宣言することだけです (または単にグローバルにすべてのモジュールにインポートするだけです)。その後、次のように使用できます。

@doLogging(2.5)
def myPreciousFunction(one, two, three=4):
    print "I'm doing some fancy computations :-)"
    return

LOGLEVEL が 2.5 以上の場合、次のような出力が得られます。

>>Called myPreciousFunction at 18:49:13
>>Parameters:  (1, 2) 
I'm doing some fancy computations :-)
>>myPreciousFunction took 18:49:13

ご覧のとおり、kwargs をより適切に処理するにはいくつかの作業が必要であるため、デフォルト値が存在する場合はそれらが出力されますが、それは別の問題です。

生のステートメントの代わりに何らかのloggerモジュールを使用する必要があるかもしれませんprintが、私はデコレーターのアイデアに焦点を当て、コードが長くなりすぎないようにしたかったのです。

とにかく、このようなデコレーターを使用すると、関数レベルのログ、任意の多くのログ レベル、新しい関数への適用の容易さ、およびログを無効にするには、LOGLEVEL を設定するだけで済みます。また、必要に応じて、関数ごとに異なる出力ストリーム/ファイルを定義できます。doLogging は次のように記述できます。

 def doLogging(logThreshold, outStream=sys.stdout):
      .....
      print >>outStream, ">>Called %s at %s" etc.

また、機能ごとに定義されたログ ファイルを利用します。

于 2009-02-07T17:59:32.550 に答える
1

次のようなことを試すことができます:

# Create something that accepts anything
class Fake(object):
    def __getattr__(self, key):
        return self
    def __call__(self, *args, **kwargs):
        return True

# Replace the logging module
import sys
sys.modules["logging"] = Fake()

Fake基本的に、ロギングモジュールのスペースを、単純に何でも取り込むインスタンスに置き換えます (または最初に埋めます) 。logging モジュールをどこでも使用しようとする前に、上記のコードを (一度だけ!) 実行する必要があります。ここにテストがあります:

import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(levelname)-8s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    filename='/temp/myapp.log',
                    filemode='w')
logging.debug('A debug message')
logging.info('Some information')
logging.warning('A shot across the bows')

上記では、予想どおり、何もログに記録されませんでした。

于 2009-02-07T01:30:41.780 に答える
1

:-) 私たちはそれをプリプロセッサと呼んでいました。C のプリプロセッサにはこれらの機能の一部がありましたが、IBM メインフレーム PL/I のプリプロセッサは「丘の王者」でした。プリプロセッサで広範な言語サポート (完全な代入、条件、ループなど) を提供し、PL/I PP だけを使用して「プログラムを作成したプログラム」を作成することができました。

私は、開発とテストで使用するために、本格的な高度なプログラムとデータのトレース (当時はバックエンド プロセス用の適切なデバッガーがありませんでした) を備えた多くのアプリケーションを作成し、適切な「ランタイム フラグ」を使用してコンパイルしたときに、それらをテストしました。パフォーマンスに影響を与えることなく、すべてのトレース コードをきれいに取り除きました。

デコレータのアイデアは良いと思います。ロギングが必要な関数をラップするデコレーターを作成できます。次に、実行時の配布のために、デコレータは「no-op」になり、デバッグ ステートメントが削除されます。

ジョン・R

于 2010-05-11T03:43:26.800 に答える
0

私は 'if __debug_' ソリューションが好きですが、それをすべての呼び出しの前に置くのは少し気を散らして醜いです。私はこれと同じ問題を抱えていましたが、ソース ファイルを自動的に解析し、ロギング ステートメントをパス ステートメントに置き換える (そしてロギング ステートメントのコピーをコメント化した) スクリプトを作成することで、それを克服しました。この変換を元に戻すこともできます。

本番環境で不要なログ ステートメントが多数あり、パフォーマンスに影響を与えている場合に、新しいコードを本番環境にデプロイするときに使用します。

ここでスクリプトを見つけることができます: http://dound.com/2010/02/python-logging-performance/

于 2010-02-07T21:16:58.140 に答える