ロギング

logging モジュールは、バージョン2.3以降のPythonの標準ライブラリの一部です。 PEP 282 に簡潔に記述されています。 基本的なロギングチュートリアル を除いて、ドキュメントは読みにくいことはよく知られています。

ロギングには2つの目的があります:

  • 診断ログ は、アプリケーションの操作に関連するイベントを記録します。たとえば、ユーザーがエラーを報告するためにコールすると、ログでコンテキストを検索できます。
  • 監査ロギング は、ビジネス分析のイベントを記録します。 ユーザーのトランザクションを抽出し、レポートの他のユーザーの詳細と組み合わせたり、ビジネス目標を最適化することができます。

...またはプリント?

コマンドラインアプリケーションのヘルプ文を表示するのがゴールのときだけ、 print がロギングより良い選択肢です。 ロギングが print より優れている理由:

  • ログ記録イベントごとに作成される ログレコード は、ファイル名、フルパス、関数、ロギングイベントの行番号など、すぐに利用できる診断情報を含んでいます。
  • 含まれているモジュールにログインしたイベントは、フィルタリングしない限り、ルートロガーからアプリケーションのロギングストリームに自動的にアクセスできます。
  • ロギングは、logging.Logger.setLevel() メソッドを使用することによって選択的に消音することができます。または、属性 logging.Logger.disabledTrue に設定することによって無効にすることができます。

ライブラリのロギング

ライブラリのログを設定する ときの注意は ロギングチュートリアル にあります。 ライブラリーではなく user がロギング・イベントが発生したときに何が起こるかを指示する必要があるため、1つの警告は繰り返されます。

注釈

NullHandler以外のハンドラをライブラリのロガーに追加しないことを強くお勧めします。

ライブラリ内のロガーをインスタンス化する際のベストプラクティスは、グローバル変数 __name__ を使用してロガーを作成することです。logging モジュールはドット表記を使用してロガーの階層を作成するので、__name__ を使用すると名前が衝突しないと保証されます。

ここでは、 requests source からのベストプラクティスの例を示します。 - これをあなたの __init__.py に置きます。

# Set default logging handler to avoid "No handler found" warnings.
import logging
try:  # Python 2.7+
    from logging import NullHandler
except ImportError:
    class NullHandler(logging.Handler):
        def emit(self, record):
            pass

logging.getLogger(__name__).addHandler(NullHandler())

アプリケーションへのログイン

twelve factor app は、アプリケーション開発における優れた実践の権威ある参考資料で、 logging best practice のセクションを含んでいます。ログイベントをイベントストリームとして扱い、イベントストリームを標準出力に送信してアプリケーション環境で処理することを強調しています。

ロガーを設定するには、少なくとも3つの方法があります:

  • INI形式のファイルを使用する:
    • Pro: logging.config.listen() 関数を使って実行中に設定を更新することができます。
    • Con: ロガーをコードで設定するときほど、(たとえば、カスタムサブクラスのフィルターやロガーのようには)細かく制御できません。
  • 辞書またはJSON形式のファイルを使用する:
    • Pro: 実行中に更新することに加えて、Python 2.6から標準ライブラリに json モジュールを使用してファイルからロードすることができます。
    • Con: コードでロガーを設定するときほど細かく制御できません。
  • コードを使う:
    • Pro: 設定を完全に制御します。
    • Con: 変更にはソースコードを変更する必要があります。

INIファイルによる設定例

ファイルの名前が logging_config.ini であるとしましょう。 ファイル形式の詳細は、 ロギングチュートリアルlogging configuration セクションにあります。

[loggers]
keys=root

[handlers]
keys=stream_handler

[formatters]
keys=formatter

[logger_root]
level=DEBUG
handlers=stream_handler

[handler_stream_handler]
class=StreamHandler
level=DEBUG
formatter=formatter
args=(sys.stderr,)

[formatter_formatter]
format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s

次に、コードに logging.config.fileConfig() を使用します:

import logging
from logging.config import fileConfig

fileConfig('logging_config.ini')
logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')

辞書による設定例

Python 2.7では、設定の詳細を持つ辞書を使用できます。 PEP 391 は、構成辞書の必須要素とオプション要素のリストを含んでいます。

import logging
from logging.config import dictConfig

logging_config = dict(
    version = 1,
    formatters = {
        'f': {'format':
              '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'}
        },
    handlers = {
        'h': {'class': 'logging.StreamHandler',
              'formatter': 'f',
              'level': logging.DEBUG}
        },
    root = {
        'handlers': ['h'],
        'level': logging.DEBUG,
        },
)

dictConfig(logging_config)

logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')

コードの直接の構成例

import logging

logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter(
        '%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)

logger.debug('often makes a very good meal of %s', 'visiting tourists')