4

複数のモジュールと複数のハンドラーのロギングをどのように行うべきかについての具体的なアドバイスを探しています。ここに簡略化したコードを追加しましたが、答えにバイアスをかけたくありません。ベストプラクティスを教えてください。

すべてをファイルに記録し、コンソールに警告以上を記録したいと思います。

これは私level0.pyが指定したファイルに記録したいものです:

import logging
from flask import Flask
from level1 import function1

app = Flask(__name__)

logger = logging.getLogger('logger0')
logger.setLevel(logging.DEBUG)

file_handler = logging.FileHandler('../logs/logger0','w','utf-8')
file_handler.setLevel(logging.DEBUG)
file_format = logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d in %(funcName)s]')
file_handler.setFormatter(file_format)
logger.addHandler(file_handler)

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_format = logging.Formatter('%(message)s')
console_handler.setFormatter(console_format)
logger.addHandler(console_handler)

@app.route('/', methods=['GET', 'POST'])
def function0(foo):
    bar = function1(foo)
    logger.debug('function0')
    ...

また、level1スクリプトとして呼び出された場合、スタンドアロンモジュールにすることもできます。その場合、別のファイルに記録したいと思います。以下はlevel1.py(重複したロギングラインがあります):

import logging
logger = logging.getLogger('level0.level1')

from level2 import function2

def function1(foo):
    bar = function2(foo)
    logger.debug('function1')
    ...

if __name__ == "__main__":
    logger = logging.getLogger('logger0')
    logger.setLevel(logging.DEBUG)

    file_handler = logging.FileHandler('../logs/logger1','w','utf-8')
    file_handler.setLevel(logging.DEBUG)
    file_format = logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d in %(funcName)s]')
    file_handler.setFormatter(file_format)
    logger.addHandler(file_handler)

    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_format = logging.Formatter('%(message)s')
    console_handler.setFormatter(console_format)
    logger.addHandler(console_handler)

    bar = function1('foo')
    logger.info('level1 main')
    ...

このロギングチャンクをからコピーしましlevel0た。同じロギングが必要であり、メインに配置するのが直感的であるように思われたためです。level2スタンドアロンではないため、次の機能のみがあります。

import logging
logger = logging.getLogger('level0.level1.level2')

def function2(foo):
    logger.info('function2')
    ....

私はから始めましlogging.basicSetupたが、ファイルのエンコーディングを設定できず、UnicodeEncodeErrorASCII以外の文字列をログに記録しようとすると取得し続けました:

logger.warn(u'foo bar {}'.format(NON_ASCII_STR))

(ロガーがその親にメッセージを渡すときに、まだエラーが発生します)

したがって、この場合、またはより一般的には、複数のモジュール(エンコーディングを選択した場合-utf-8が必要)に最適なログ設計は何ですか?

4

2 に答える 2

10

多くのパーツで構成されるモジュールの場合、ドキュメントで推奨されている方法を使用します。この方法では、モジュールごとに1行しかありませんlogger = logging.getLogger(__name__)。ご指摘のとおり、モジュールはメッセージの送信方法や送信先を認識したり気にしたりする必要はなく、メインプログラムによって設定されているはずのロガーにメッセージを渡すだけです。

メインプログラムに応じてカットアンドペーストを減らすには、モジュールに意味のある階層があり、ロギングを設定する関数が1つだけあることを確認してください。この関数は、任意のメインプログラムから呼び出すことができます。

たとえば、logsetup.pyを作成します。

import logging

def configure_log(level=None,name=None):
    logger = logging.getLogger(name)
    logger.setLevel(level)

    file_handler = logging.FileHandler('../logs/%s' % name,'w','utf-8')
    file_handler.setLevel(logging.DEBUG)
    file_format = logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d in %(funcName)s]')
    file_handler.setFormatter(file_format)
    logger.addHandler(file_handler)

    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_format = logging.Formatter('%(message)s')
    console_handler.setFormatter(console_format)
    logger.addHandler(console_handler)

個々のモジュールにメインとして動作するモードを持たせるには、などの個別の関数を定義しますmain

level0.pyおよび/またはlevel1.pyの場合:

def main():
  # do whatever

そして、最上位のプログラムで、その関数を呼び出します。

import logging
from logsetup import configure_log
configure_log(logging.DEBUG,'level0') # or 'level1'
from level0 import main # or level1

if __name__ == "__main__"
  main()

そこにはまだ句があるはずです__name__ == "__main__"。一部のモジュール(cough multiprocessing cough)は、句が存在するかどうかによって動作が異なります。

于 2013-02-21T20:13:23.550 に答える
6

これをまとめるために、これが私がやったことです。すべてのモジュール/ファイルに次の 2 行を追加しました。

import logging
logger = logging.getLogger(__name__)

これにより、ログが設定されますが、ハンドラーは追加されません。次に、インポートしたモジュールを実行しているメイン ファイルのルート ロガーにハンドラーを追加します。したがって、それらのレコードをルート ロガーに渡すと、すべてが保存されて表示されます。私はCaptainMurphyが提案したようにそれを行いますがlogger = logging.getLogger('')、ルートロガーを操作するには

エンコーディングに対処するために、ASCII 以外の文字列をファイルに保存する際に問題がありました。FileHandlerそこで、エンコーディングを指定できるルートロガーに追加しました。でできませんでしたlogging.basicConfig

もう一度ありがとう@CaptainMurphy - 評判が低いので賛成できなくて申し訳ありませんが、答えにチェックマークを付けました.

于 2013-02-25T14:59:57.683 に答える