単一のファイルを使用したPythonロギング(関数名、ファイル名、行番号)


109

アプリケーションの仕組みを学びたいと思っています。そして、このために、関数の名前とログ出力にメッセージを送信する(コード内の)行番号をログに記録することを目的として、各関数の本文の最初の行としてデバッグコマンドを挿入しています。最後に、このアプリケーションは多くのファイルで構成されているので、アプリケーションの制御フローをよりよく理解できるように、単一のログファイルを作成したいと思います。

これが私が知っていることです:

  1. 関数名を取得するために使用できますfunction_name.__name__が、function_nameを使用したくありません(Log.info("Message")すべての関数の本体にジェネリックをすばやくコピーして貼り付けることができるようにするため)。これはCで__func__マクロを使用して実行できることはわかっていますが、Pythonについてはわかりません。

  2. ファイル名と行番号を取得するために、私は(と私がいることを信じている)私のアプリケーションは、Pythonの使用していることを見てきたlocals():機能が、構文で、私は例えばの完全に認識していないだということをoptions = "LOG.debug('%(flag)s : %(flag_get)s' % locals())、私は次のように使用して、それを試してみましたLOG.info("My message %s" % locals())のようなものを生成します{'self': <__main__.Class_name object at 0x22f8cd0>}。これについて何かご意見がありますか?

  3. ロギングを使用してファイルにログを記録するハンドラーを追加する方法を知っていますが、単一のファイルを使用して、プロジェクト内の関数呼び出しの正しい順序ですべてのログメッセージを記録できるかどうかわかりません。

どんな助けにも感謝します。

ありがとう!


を使用してpythonデバッガーにドロップし、import pdb; pdb.set_trace()インタラクティブにコードをステップ実行できます。これは、プログラムの流れを追跡するのに役立ちます。
マシュー・シンケル

いい案!マットに感謝します。毎回デバッグする必要がないように、質問で述べたようにログを取得することは依然として役に立ちます。また、デバッグを容易にするために使用できるEclipse for Java(Ctrl +クリックで関数定義に移動できます)と同じくらい良いPython用IDEを知っていますか?
user1126425

回答:


28

ここで、わずかに関連するいくつかの質問があります。

私は最も簡単なものから始めましょう:(3)。を使用loggingすると、単一のログファイルまたは他の出力ターゲットへのすべての呼び出しを集約できます。呼び出しは、プロセスで発生した順序になります。

次は(2)です。locals()現在のスコープの口述を提供します。したがって、他の引数を持たないメソッドではself、現在のインスタンスへの参照を含むスコープ内にいます。困惑しているのは、%演算子のRHSとしてdictを使用する文字列フォーマットです。"%(foo)s" % barの値に置き換えられbar["foo"]ます。

最後に、pdbより多くの情報を記録できるのと同様に、いくつかのイントロスペクショントリックを使用できます。

def autolog(message):
    "Automatically log the current function details."
    import inspect, logging
    # Get the previous frame in the stack, otherwise it would
    # be this function!!!
    func = inspect.currentframe().f_back.f_code
    # Dump the message + the name of this function to the log.
    logging.debug("%s: %s in %s:%i" % (
        message, 
        func.co_name, 
        func.co_filename, 
        func.co_firstlineno
    ))

これは、渡されたメッセージと、(元の)関数名、定義が表示されるファイル名、およびそのファイルの行を記録します。見てい検査を-ライブオブジェクトを点検して詳細については。

以前のコメントで述べたように、pdbimport pdb; pdb.set_trace()を挿入してプログラムを再実行することにより、いつでも対話型デバッグプロンプトにドロップすることもできます。これにより、コードをステップ実行して、選択したとおりにデータを検査できます。


マットありがとう!この自動ログ機能を試します。%演算子のRHSとしてdictを使用することについて少し混乱しています:の値'%(foo)s : %(bar)s'も出力しbar["foo"]ますか?それともあなたの例とは少し異なりますか?
user1126425

基本的に、フォームのすべては%(<foo>)s、dictでによって参照されるオブジェクトの値に置き換えられます<foo>docs.python.org/library/stdtypes.html#string-formatting
Matthew Schinckelの

3
@synthesizerpatelの回答がはるかに役立ちます。
1

503

これに対する正しい答えは、すでに提供されているfuncName変数を使用することです

import logging
logger = logging.getLogger('root')
FORMAT = "[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s"
logging.basicConfig(format=FORMAT)
logger.setLevel(logging.DEBUG)

次に、必要な場所に追加します。

logger.debug('your message') 

私が今取り組んでいるスクリプトからの出力例:

[invRegex.py:150 -          handleRange() ] ['[A-Z]']
[invRegex.py:155 -     handleRepetition() ] [[<__main__.CharacterRangeEmitter object at 0x10ba03050>, '{', '1', '}']]
[invRegex.py:197 -          handleMacro() ] ['\\d']
[invRegex.py:155 -     handleRepetition() ] [[<__main__.CharacterRangeEmitter object at 0x10ba03950>, '{', '1', '}']]
[invRegex.py:210 -       handleSequence() ] [[<__main__.GroupEmitter object at 0x10b9fedd0>, <__main__.GroupEmitter object at 0x10ba03ad0>]]

61
これが答えだったはずです!
user3885927

1
いいですね。ログファイルに動的にコードファイルと同じ名前を付けることはできますか?例:logging.basicConfig(filename = "%(filename)"、format = FORMAT)で動的にファイル名を取得しようとしましたが、静的な値が使用されました。なにか提案を?
2014

2
@外れ値いいえ、それを達成するための推奨される方法は、getLogger(__name__)
farthVader 2015年

2
質問が1つあります。Javaのどこかで、ロガーが呼び出されている行を特定するのに余分な時間がかかるため、行番号を出力することはお勧めしません。Pythonではこれは本当ではありませんか?
McSonk 2017年

2
無関係logging.getLogger('root')ですが、おそらく期待しているものではありません。rootロガーではなく、「root」という名前の通常のロガーです。
0xc0de

5

funcname、ロギングを行った最後の関数に関する情報linenamelineno提供します。

ロガーのラッパー(シングルトンロガーなど)を使用している場合、@ synthesizerpatelの回答が機能しない可能性があります。

コールスタックで他の発信者を見つけるには、次のようにします。

import logging
import inspect

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class MyLogger(metaclass=Singleton):
    logger = None

    def __init__(self):
        logging.basicConfig(
            level=logging.INFO,
            format="%(asctime)s - %(threadName)s - %(message)s",
            handlers=[
                logging.StreamHandler()
            ])

        self.logger = logging.getLogger(__name__ + '.logger')

    @staticmethod
    def __get_call_info():
        stack = inspect.stack()

        # stack[1] gives previous function ('info' in our case)
        # stack[2] gives before previous function and so on

        fn = stack[2][1]
        ln = stack[2][2]
        func = stack[2][3]

        return fn, func, ln

    def info(self, message, *args):
        message = "{} - {} at line {}: {}".format(*self.__get_call_info(), message)
        self.logger.info(message, *args)

1
あなたの答えは私が私の問題を解決するために必要なものでした。ありがとうございました。
エラー-構文的反省

Pythonの3.8以来、loggingクラスのサポートスタックレベルは、すぐにスキップ:方法が好きでlog()debug()今受け入れるなど、stacklevel引数を。docsを参照してください。
メインの
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.