2016-05-03 6 views
2

同じutils.pyパッケージを使用する複数のモジュールがあります。呼び出し元(つまりClassAまたはClassB)からロガー変数を渡すことなく、utils.pyのロガーを異なるようにするにはどうすればよいですか?ロガー変数を渡さずに異なるロガーを使用するにはどうすればいいですか? (Pythonロギング)

非常に簡単なサンプルコードは次のとおりです。実際には、私はutils.pyに多くの関数とクラスを持っています。そのため、utils.pylogger変数を渡したくありません。

~/test-two-loggers$ tree . 

├── main.py 
├── configs.py 
├── ClassA.py 
├── ClassB.py 
└── utils.py 

0 directories, 5 files 

main.py

import ClassA 
import ClassB 

ClassA.func() 
ClassB.func() 

ClassA.py

import utils 
import configs 
import logging 

def func(): 
    logger = logging.getLogger("classA") 
    logger.info("in ClassA") 
    utils.common_func(logger) # I want to change this line!!!! 

ClassB.py

import utils 
import configs 
import logging 

def func(): 
    logger = logging.getLogger("classB") 
    logger.info("in ClassB") 
    utils.common_func(logger) # I want to change this line!!!! 

utils.py

def common_func(logger): # I want to change this line!!!! 
    # do a lot of things ClassA and ClassB both need to do 
    logger.info("in utils - step one finished") 
    # do a lot of things ClassA and ClassB both need to do 
    logger.info("in utils - step two finished") 
    # do a lot of things ClassA and ClassB both need to do 
    logger.info("in utils - step three finished") 

configs.py

import logging.config 

logging_config = { 
     "version": 1, 
     "formatters": { 
      "formatter_a": { 
       "format": u"[A][%(levelname)s] %(module)s.%(lineno)d: %(message)s" 
      }, 
      "formatter_b": { 
       "format": u"[B][%(levelname)s] %(module)s.%(lineno)d: %(message)s" 
      }, 
     }, 
     "handlers": { 
      "console_a": { 
       "class": "logging.StreamHandler", 
       "level": "DEBUG", 
       "formatter": "formatter_a", 
       "stream": "ext://sys.stdout" 
      }, 
      "console_b": { 
       "class": "logging.StreamHandler", 
       "level": "DEBUG", 
       "formatter": "formatter_b", 
       "stream": "ext://sys.stdout" 
      }, 
     }, 
     "loggers": { 
      "classA": { 
       "level": "DEBUG", 
       "handlers": ["console_a"], 
       "propagate": "no" 
      }, 
      "classB": { 
       "level": "DEBUG", 
       "handlers": ["console_b"], 
       "propagate": "no" 
      }, 
     }, 
} 

logging.config.dictConfig(logging_config) 

結果私がしたい:

~/test-two-loggers$ python main.py 
[A][INFO] ClassA.7: in ClassA 
[A][INFO] utils.3: in utils - step one finished 
[A][INFO] utils.5: in utils - step two finished 
[A][INFO] utils.7: in utils - step three finished 
[B][INFO] ClassB.7: in ClassB 
[B][INFO] utils.3: in utils - step one finished 
[B][INFO] utils.5: in utils - step two finished 
[B][INFO] utils.7: in utils - step three finished 

しかし、私はこれ以外の別の解決策を求めています。変数loggerutilsに渡したくありません。

+1

ここでコードの匂いが少しあります。あなたがユーティリティモジュールを持っているなら、あなたはユーティリティ名前空間からロギングする必要があるようです。何らかの理由でスタックトレースを使用したい場合は、[独自のフォーマッタ](https://github.com/python/cpython/blob/5b5fb38cc70122354c95f0a88cecaffab7d0b523/Lib/logging/__init__.py#L514)を作成するだけで済みます。それがどこから呼び出されたのかを理解するのに役立ちます。 –

+1

@WayneWerner 'ユーティリティモジュールをお持ちの場合は、ユーティリティの名前空間からログを取る必要があります。しかし、ユーティリティのログは、コンテキストがなくても意味のないものになります(ClassAまたはClassBのどちらであっても、ユーティリティが呼び出される前にClassAまたはClassBで実行されたことがユーティリティでエラーが発生した場合に発生します)。しかし私はこれが私達が試みることができる哲学であると思う。たぶん、ユーティリティに依存しないログを作成し、ClassA/Bにログを追加して、結果の概要をユーティリティで表示することができます。これは確かにオプションです。 –

+0

これは間違いなく私の好みです。あなたのアプリを改造して、奇妙な伐採林で飛び降りるのはまれです。また、fnコールをtry/exceptでラップし、 'logger.exception( 'utilityで問題が発生しました')'を使用することもできます。それは例外的なケースにアプローチする便利な方法です。 –

答えて

0

フォーマッタを変更し、extraキーワード引数を使用して、追加の辞書引数をログメッセージに渡すことができます。これにより、ロガーを呼び出しているふりをしたい "モジュール"を渡すことができます。

だから、からあなたのフォーマッタを変更:に

"[A][%(levelname)s] %(module)s.%(lineno)d: %(message)s" 

"[A][%(levelname)s] %(mymodule)s.%(lineno)d: %(message)s" 

として、あなたの関数を呼び出す:あなたは本当モジュールを使用したい場合は

logger.info("in utils", extra={'mymodule':'somemodule'}) 

から電話しています。'somemodule'をに変更してください。 の値をオーバーライドしてもよいので(フォーマッタを変更する必要があります)、loggingは許可していないため、フォーマッタを変更する必要があるようです。


EDIT:

はちょうどあなたのClassA.pyfunc()に変更する必要があり、それは余分明確にする:

def func(): 
    logger = logging.getLogger("classA") 
    logger.info("in ClassA", extra={'mymodule':__name__) 
    logger.info("in utils", extra={'mymodule':'utils') 
    utils.common_func() #call the function without passing the logger 

そして、あなたのlogging_configのdictに、あなたは文字列を変更する必要がありますmodulelogging_config['formatters']['formatter_a']['format']mymoduleにあります。

同じものをClassBに適用する必要があります。そして、明らかに、を使用するcommon_funcの行を削除する必要があります。


また参照:あなたがutil.common_func内部あなたロガーを使用する必要がある場合に

+0

こんにちは、私はあなたの方法を完全に理解していません。変更する必要があるすべてのコードを貼り付けることができますか?そして、これが古いものと全く同じ出力を生成できることは確かですか? –

+0

私は十分にはっきりしていたと思うが、もっと明確にするために投稿を編集した。ちなみに、この場合は、2つのロガー/ハンドラ/フォーマッタは実際には必要ありません。唯一の違いは、文字A/Bです。あなたはフォーマッタを変更し、A/Bを渡すために 'extra 'に別のキー/値を追加することができます。 [自分自身を繰り返さない](https://en.wikipedia.org/wiki/Don't_repeat_yourself) –

+0

こんにちは、私は今あなたの方法を理解していますが、私のニーズに合っていません。私が書いたのは単なるサンプルです。 'utils'とその機能を削除して別のモジュールに複製するべきではありません。実際、 'utils.py'にはたくさんのメソッドがあり、そのために二つのクラスが' utils'を共有しています。 'utils.py'を削除し、' utils.py'の関数を 'ClassA'と' ClassB'に戻すと、一つのプロジェクトで多くの重複コードが発生します。 –

0

、それを渡す必要はありません。ちょうどlogging.getLoggerを使用してutils.common_funcの中のあなたのロガーを検索することができます。同じ文字列(たとえばclassA)を渡す限り、logging.getLoggerは同じロギングオブジェクトを返します。 documentationから

logger1 = logging.getLogger('classA') 
def func(): 
    logger2 = logging.getLogger('classA') 
    print logger1 is logger2 #True 

:ロガー は直接、常にモジュールレベル 関数logging.getLogger(name)を介してインスタンス化されることはありませんことを

注意。同じ名前の でgetLogger()を複数呼び出すと、常に同じLogger オブジェクトへの参照が返されます。

+0

こんにちは、しかし、 'classA'と' classB'の両方が 'utils.common_func'の呼び出し元です。 –

1

暗黙のパラメータのようなものを探しているようです。

これはPythonにはないものです(明示的暗黙よりも)。

しかし、いつものように、それをエミュレートするために多かれ少なかれエレガントな方法があります:

class LoggerWrapper: 
    def __init__(self, logger_name): 
     self.logger = logging.getLogger(logger_name) 

    def common_func(self): 
     pass # do stuff here 

logger = LoggerWrapper('classA') 
logger.common_func() 
+0

こんにちは、これは私が考えている方法です! 'LoggerWrapper'ではなく、' UtilsWithLogger'と呼ぶ方がいいと思います。これは確かに答えですが、2つの不完全な点があります。 (1) 'ClassA'と' ClassB'の両方で 'LoggerWrapper' initが必要です(現行の解決策よりも優れていますが、完璧ではありません)(2)実際には'utils.py'には複数のクラス(名前空間の目的)があります。このメソッドを使うとネストされたクラスに対処する必要があります。ネストされたクラスに対処したくなければ、クラスAとクラスBの両方で 'utils'のすべてのクラスを初期化しなければなりません。クラスB '。別の方法があるのを待つよ。 –

+0

私はあなたの2番目の問題を理解していません...あなたはそれを違った説明をしようとすることができますか? – GingerPlusPlus

+0

こんにちは、約(2): 'utils.py'には、複数のクラスがあります。 'class CrawlerUtils - staticmethods crawl_json()、crawl_url()'/'クラスDbUtils - 静的メソッドinsert_with_md5()、my_insert_many()'/'クラスOtherUtils ... 'です。だから私がこのラッパーメソッドを使うと、(a)元のクラスの上に親クラスを追加するか、ネストされたクラスで 'self.logger'を処理する必要があります。 db_utils = DbUtilsWithLoggers( 'classA')、another_utils = ... etc'これらの初期化ステップは、ClassAとClassB内で実行されます。 –

1

最も簡単な解決策は、あなたがあなたのアプリケーションをスレッド化していないと仮定すると、単にモジュールの属性をモンキーパッチすることです:

util.py

import logging 

logger = logging.getLogger(__name__) 


def do_something(): 
    logger.info('Doing something') 

whatever.py 01理由のために、モンスキッペッチングは面倒です。

関連する問題