Pythonのライブラリ・フレームワークにおけるloggingの設計基礎、4つのキワードを理解する

Pythonのログイングを理解する上で重要な4つのキーワードを解説する

  1. Loggers  : loggingシステムにメッセージを送信するためにアプリケーションコードによって使用される
  2. Formatters : 出力のためにメッセージをフォーマットする
  3. Filters : 細かい出力制御を提供
  4. Handlers : フォーマットされた出力をファイルなどの宛先に送る。

アプリケーション、フレームワーク、ライブラリの中で、これらのタイプのそれぞれをいつ、どこで、どのように使うかを知ることが重要だ。どんな完全なスタンドアロンのアプリケーションでも、適切なロギング出力を得るためには、4つのうち少なくとも3つを使用すべきだといわれている。標準ライブラリのドキュメントには、これを行う方法がかなり明確に記載されている。 これら4つのタイプのうち、フィルタはおそらくあまり使ったことがない人がおおいのではないか。フィルタは、例えば、マルチスレッドネットワーク指向のコードでは、文脈に沿った情報をログに取得するのに便利な手段となる。 (例: リモートIPアドレスやユーザ名、データベース接続固有の情報など)。ほとんどの場合、ライブラリやフレームワークの作者は、Logger オブジェクトだけを使うことになる。Loggerを取得して、それを使ってログイベントを公開する。Loggingレベルは安易に設定しないほうがいいし、フォーマッタやハンドラも設定には慎重になったほうがいいだろう。ライブラリやフレームワークがロギングパッケージを使用する際には、ロギングパッケージを適切に使用することが不可欠だ。不適切な使用は、ライブラリやフレームワークをより大きなアプリケーションに組み込むことを困難にする。

基本的には

log = logging.getLogger(__name__)

だけでできないかをまず考えよう。 これは、特定のロギングイベントがどのモジュールから発生したかを正確に示す名前のロガーを取得する。 (このロガーが定義されているモジュールでのみ使用されると仮定している)。一般的に、ログを取る必要のあるすべてのモジュールは、nameインスタンス化されたモジュールレベルのロガーを持つべきであり、そのモジュールのコードはそのロガーを使用します。より細かい制御が必要な場合は、そのモジュールロガーの子ロガーを持つこともできる。しかし、ロガーはシングルトンなので、クラス インスタンスでロガーへの参照を保持する必要はない。ロガーがシングルトンであるのには、それなりの理由があります。Loggerがシングルトンでない場合、これはできない。

ライブラリではロギングレベル、フォーマッタ、またはハンドラを設定する必要は基本的にない。もしあなたがライブラリの作者で、ユーザの利便性のためにこれらのうちの1つ以上を行いたいと思っているならば、本当にそうなのか慎重に考えよう。ユーザーの助けになるどころか、ユーザーをイライラさせるだけになるかもしれないからだ。

一方Djangoのようなフレームワークでロギングを使うときには、健全なプラクティスに従うことが重要だ。健全なプラクティスとはフレームワークが特定のハンドラやフォーマッタを設定する場合、それを簡単に無効にできる方法で行い、それを無効にする方法を明確に文書化するといったことを意味する。無効にするということは、フィルタ、ハンドラ、フォーマッタがインスタンス化されたり、ロギングレベルが設定されたり(setLevelで)、ハンドラがロガーに追加されたり(addHandlerで)するコードブランチをすべて無効にすることを意味します。たとえば、ほとんどのユーザーが非常に特定のタイプのロギング出力を必要とすることが予想されるため、特定のハンドラとフォーマッタのセットをセットアップしたい場合がある。この例としては、CherryPy のようなウェブフレームワークがあり、Apache によって生成されたものと同様の標準のアクセスログやエラーログを生成するためにロギングシステムを使用することができる。これは、単純なウェブアプリケーションを立ち上げて実行するときに便利だ。しかしながら、より高度な使用例では、フレームワーク内で行われるロギング設定を上書きしたり無効にしたりする簡単な方法がない場合、非常にライブラリユーザーをイラつかせる結果となるだろう。

アプリケーションのロギングは、ほとんどの場合、起動時に一度設定される。この設定プロセスは、アプリケーション開発者によって行われた設定に加えて、フレームワークやライブラリが独自のロギング設定を行うことによって、カオスになりがちだ。最初の設定が勝つ場合もあれば (例えば logging.basicConfig)、2つの競合する設定 (例えば、2つのハンドラが同じファイルに出力を送って重複したメッセージになるなど)、最後の1つが勝つ場合もあります (例えば、与えられたロガーやハンドラのロギングレベルを設定するなど)。ロギングがインポート時に設定されている場合(一般的には避けるべきことです)、モジュールがインポートされる順序は、奇妙で直感的でない方法でロギングの動作を変更することができる。これはすべて、不適切に使用された場合、ロギングパッケージの動作を追うのがとても難しくなることを意味する。これは、フレームワークやライブラリがロギングを設定してはいけないときにロギングを設定してしまうと目も当てられない。

フレームワークやライブラリの開発者としては、ロギングレベル、フィルタ、フォーマット、ハンドラのための単一の設定ポイントを常に許可したり、提供したりするべきだ。ライブラリを開発するときは、ロガーを取得してログを記録するだけにすべきだ。フレームワークを開発するときは、logging.config.dictConfig で設定できるものを上書きしたり、無効にしたりする方法を提供するようにすべきだ。これらのシンプルなルールに従うことで、あなたのライブラリやフレームワークは、高度なロギング設定を設定しようとしている人にとって、より便利になるだろう。

https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library