3

名前付きの子ロガーを作成して、そのロガーによって出力されるすべてのログがその名前でマークされるようにすることができます。そのロガーは、関数/クラス/その他でのみ使用できます。

ただし、そのコードが、ロギング モジュール関数 (ルート ロガーへのプロキシ) のみを使用してロギングを利用する別のモジュールの関数を呼び出す場合、これらのログ メッセージが同じロガーを通過するようにするにはどうすればよいですか (または少なくとも同じようにログインします)?

例えば:

main.py

import logging

import other

def do_stuff(logger):
    logger.info("doing stuff")
    other.do_more_stuff()

if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger("stuff")
    do_stuff(logger)

other.py

import logging

def do_more_stuff():
    logging.info("doing other stuff")

出力:

$ python main.py 
INFO:stuff:doing stuff
INFO:root:doing other stuff

両方のログ行に「stuff」という名前を付けて、main.py を変更するだけでこれを実行できるようにしたいと考えています。

そのモジュールを変更せずに、other.py のロギング呼び出しで別のロガーを使用するにはどうすればよいですか?

4

4 に答える 4

1

これは私が思いついた解決策です:

スレッド ローカル データを使用してコンテキスト情報を保存し、ルート ロガー ハンドラーで Filter を使用して、この情報を LogRecords に追加してから発行します。

context = threading.local()                                                      
context.name = None                                                              

class ContextFilter(logging.Filter):                                             

    def filter(self, record):                                                    
        if context.name is not None:                                             
            record.name = "%s.%s" % (context.name, record.name)              
        return True                                                              

このメッセージがログに記録されたときに実行されていたタスクを示すためにロガー名を使用しているため、これは私にとっては問題ありません。

次に、コンテキスト マネージャーまたはデコレーターを使用して、コードの特定の部分からのログをすべて、特定の子ロガーからログが記録されたかのように見せることができます。

@contextlib.contextmanager                                                       
def logname(name):                                                               
    old_name = context.name                                                      
    if old_name is None:                                                         
        context.name = name                                                      
    else:                                                                        
        context.name = "%s.%s" % (old_name, name)                                

    try:                                                                                 
        yield                                                                        
    finally:                                                                         
        context.name = old_name                                                      

def as_logname(name):                                                            
    def decorator(f):                                                            
        @functools.wraps(f)                                                      
        def wrapper(*args, **kwargs):                                            
            with logname(name):                                                  
                return f(*args, **kwargs)                                        
        return wrapper                                                           
    return decorator                                                             

それで、私はできる:

with logname("stuff"):
    logging.info("I'm doing stuff!")
    do_more_stuff()

また:

@as_logname("things")
def do_things():
    logging.info("Starting to do things")
    do_more_stuff()

重要なことは、まったくdo_more_stuff()変更する必要なく、「stuff」または「things」の子ロガーでログが記録されたかのようにログが記録されることdo_more_stuff()です。

子ロガーごとに異なるハンドラーを使用する場合、このソリューションには問題があります。

于 2013-11-04T15:57:04.520 に答える
0

これが logging.handlers (または logging モジュールのハンドラー) の目的です。ロガーを作成するだけでなく、1 つ以上のハンドラーを作成してログ情報をさまざまな場所に送信し、それらをルート ロガーに追加します。ロギングを行うほとんどのモジュールは、独自の目的で使用するロガーを作成しますが、制御スクリプトに依存してハンドラーを作成します。一部のフレームワークは、非常に便利で、ハンドラーを追加することにしました。

logging docsを読んでください。すべてそこにあります。

(編集)

logging.basicConfig() は、単一のハンドラーをルート ロガーに追加するヘル​​パー関数です。「format=」パラメーターを使用して、使用するフォーマット文字列を制御できます。すべてのモジュールに「もの」を表示するだけの場合は、 を使用しますlogging.basicConfig(level=logging.INFO, format="%(levelname)s:stuff:%(message)s")

于 2013-07-30T16:56:13.913 に答える
0

他のモジュールで使用logging.setLoggerClassされるすべてのロガーがあなたのロガー サブクラスを使用するように使用します (強調は私のものです):

ロガーをインスタンス化するときにクラス klass を使用するようにロギング システムに指示します。クラスは__init__()、 name 引数のみが必要になるように定義する必要があり、__init__()は を呼び出す必要がありますLogger.__init__()この関数は通常、カスタム ロガー動作を使用する必要があるアプリケーションによってロガーがインスタンス化される前に呼び出されます

于 2013-07-30T14:13:56.927 に答える
-1

メソッドは、呼び出されたオブジェクトのそれぞれのlogging.{info,warning,…}メソッドを呼び出すだけです(ロギング モジュール ソースを参照)。そのため、モジュールがモジュールによってエクスポートされた関数のみを呼び出していることがわかっている場合は、名前空間内のモジュールをオブジェクトで上書きできます。Loggerroototherloggingloggingotherlogger

import logging

import other

def do_stuff(logger):
    logger.info("doing stuff")
    other.do_more_stuff()

if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger("stuff")
    # Overwrite other.logging with your just-generated logger object:
    other.logging = logger
    do_stuff(logger)
于 2013-07-30T14:23:30.820 に答える