"logging.config" --- ロギングの環境設定
***************************************

**ソースコード:** Lib/logging/config.py


Important
^^^^^^^^^

このページには、リファレンス情報だけが含まれています。チュートリアルは
、以下のページを参照してください

* 基本チュートリアル

* 上級チュートリアル

* ロギングクックブック

======================================================================

この節では、logging モジュールを設定するための API を解説します。


環境設定のための関数
====================

以下の関数は logging モジュールの環境設定をします。これらの関数は、
"logging.config" にあります。これらの関数の使用はオプションです ---
"logging" モジュールはこれらの関数を使うか、 ("logging" 自体で定義され
ている) 主要な API を呼び出し、 "logging" か "logging.handlers" で宣言
されているハンドラを定義することで設定できます。

logging.config.dictConfig(config)

      辞書からロギング環境設定を取得します。この辞書の内容は、以下の
      環境設定辞書スキーマ で記述されています。

      環境設定中にエラーに遭遇すると、この関数は適宜メッセージを記述し
      つつ "ValueError", "TypeError", "AttributeError" または
      "ImportError" を送出します。例外を送出する条件を (不完全かもしれ
      ませんが) 以下に列挙します:

      * 文字列でなかったり、実際のロギングレベルと関係ない文字列であっ
        たりする "level"。

      * ブール値でない "propagate" の値。

      * 対応する行き先を持たない id。

      * インクリメンタルな呼び出しの中で見つかった存在しないハンドラ
        id。

      * 無効なロガー名。

      * 内部や外部のオブジェクトに関わる不可能性。

      解析は "DictConfigurator" クラスによって行われます。このクラスの
      コンストラクタは環境設定に使われる辞書に渡され、このクラスは
      "configure()" メソッドを持ちます。 "logging.config" モジュールは
      、呼び出し可能属性 "dictConfigClass" を持ち、これはまず
      "DictConfigurator" に設定されます。 "dictConfigClass" の値は適切
      な独自の実装で置き換えられます。

      "dictConfig()" は "dictConfigClass" を、指定された辞書を渡して呼
      び出し、それから返されたオブジェクトの "configure()" メソッドを
      呼び出して、環境設定を作用させます:

         def dictConfig(config):
             dictConfigClass(config).configure()

      例えば、 "DictConfigurator" のサブクラスは、自身の "__init__()"
      で "DictConfigurator.__init__()" を呼び出し、それから続く
      "configure()" の呼び出しに使えるカスタムの接頭辞を設定できます。
      "dictConfigClass" は、この新しいサブクラスに束縛され、そして
      "dictConfig()" はちょうどデフォルトの、カスタマイズされていない
      状態のように呼び出せます。

   バージョン 3.2 で追加.

logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True)

   ログ記録の環境設定を "configparser" 形式ファイルから読み出します。
   そのファイルの形式は 環境設定ファイルの書式 で記述されているとおり
   にしなければなりません。この関数はアプリケーションから何度も呼び出
   すことができ、これによって、 (設定を選択し、選択された設定を読み出
   す機構をデベロッパが提供していれば) 複数の準備済みの設定からエンド
   ユーザが選択するようにできます。

   パラメータ:
      * **fname** -- ファイル名、あるいはファイルのようなオブジェクト
        、または "RawConfigParser" 派生のインスタンス。
        "RawConfigParser" 派生のインスタンスが与えられれば、それはその
        まま使われます。そうでない場合 "Configparser" がインスタンス化
        され、設定はそれを使って "fname" が指すオブジェクトから読み込
        まれます。それが "readline()" メソッドを持っていればそれはファ
        イルのようなオブジェクトと仮定され、 "read_file()" で読み込ま
        れます; そうでない場合、それはファイル名と仮定されて、
        "read()" に渡されます。

      * **defaults** -- ConfigParser に渡されるデフォルト値をこの引数
        で指定することができます。

      * **disable_existing_loggers** -- "False" が指定された場合は、こ
        の呼び出しが行われたときに存在するロガーは有効のまま残されます
        。後方互換性のあるやり方で古い振る舞いを保つので、デフォルト値
        は "True" になっています。そのような振る舞いでは、既存のノンル
        ートロガーまたはそれらのロガーの先祖がロギング設定の中で明示的
        に名付けられていない限り、既存のロガーを無効にします。

   バージョン 3.4 で変更: "fname" として "RawConfigParser" のサブクラ
   スのインスタンスが渡せ得るようになっています。これによってこのよう
   なことが容易になります:

   * ロギングの設定が、アプリケーション全体の設定における単なる一部で
     あるような設定ファイルの使用。

   * ファイルから設定を読み込み、 "fileConfig" に通す前に(例えばコマン
     ドラインパラメータやランタイム環境の他のなにかで)アプリケーション
     によって修正するようなこと。

logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None)

   指定されたポートでソケットサーバを起動し、新しい設定を待ち受けます
   。ポートが指定されなかった場合は、モジュールのデフォルトの
   "DEFAULT_LOGGING_CONFIG_PORT" が使用されます。ロギング設定は
   "dictConfig()" あるいは "fileConfig()" で処理できるファイルとして送
   信されます。 "Thread" インスタンスを返し、このインスタンスの
   "start()" を呼び出してサーバを起動し、適切なところで "join()" を呼
   び出すことができます。サーバを停止するには、 "stopListening()" を呼
   び出します。

   "verify" 引数を指定する場合は、これはソケットを通して受け取ったバイ
   ト文字列が妥当であるか、処理すべきであるかどうかを検査する callable
   である必要があります。ソケットを通じて、暗号化または署名あるいはそ
   の両方を受け取ることがあります。そのような場合に、 "verify"
   callable が署名の正当性検査または暗号化の復号あるいはその両方を実施
   することが出来ます。 "verify" callable は単一引数で呼び出されます -
   ソケットを通じて受け取ったバイト文字列です - そして処理すべきバイト
   文字列、または捨て去られるべきであることを示すための "None" を返す
   必要があります。返却されるバイト文字列は(たとえば正当性検査だけが行
   われて)渡されたものと同じかもしれませんし、あるいは(おそらく暗号化
   の復号が行われて)まったく異なるものかもしれません。

   ソケットに設定を送るには、まず設定ファイルを読み、それを
   "struct.pack('>L', n)" を使って長さ 4 バイトのバイナリにパックした
   ものを前に付けたバイト列としてソケットに送ります。

   注釈:

     設定の部分が "eval()" を通して渡されるので、この関数の使用はユー
     ザに対してセキュリティリスクを公開してしまうかもしれません。この
     関数は単に "localhost" 上のソケットに接続してリモートマシンからは
     接続を受け付けませんが、 "listen()" を呼んだプロセスのアカウント
     で信頼されていないコードが実行されるシナリオが存在します。特に、
     "listen()" を呼んだプロセスがユーザがお互いを信頼することができな
     いマルチユーザのマシン上で実行される場合、悪意のあるユーザは、犠
     牲者のユーザのプロセスで本質的に任意のコードを実行するように細工
     することができます。単に犠牲者の "listen()" ソケットに接続して、
     犠牲者のプロセスで実行したいコードを実行するような設定を送るだけ
     です。これは、特にデフォルトポートが使用されている場合に行うのが
     より簡単ですが、異なるポートが使用されていたとしてもそれほど難し
     くありません)。これが起こるリスクを避けるには、 "listen()" に
     "verify" 引数を使用して、認識されていない設定が適用されないように
     してください。

   バージョン 3.4 で変更: "verify" 引数が追加されました。

   注釈:

     既存のロガーを無効にしない構成をリスナーに送信する場合は、設定に
     はJSONフォーマットを使用する必要があります。これは、設定に
     "dictConfig()" を使用します。 このメソッドを使用すると、
     "disable_existing_loggers" に "False" を指定した設定を送信できま
     す。

logging.config.stopListening()

   "listen()" を呼び出して作成された、待ち受け中のサーバを停止します。
   通常 "listen()" の戻り値に対して "join()" が呼ばれる前に呼び出しま
   す。


環境設定辞書スキーマ
====================

ロギング設定を記述するには、生成するさまざまなオブジェクトと、それらの
つながりを列挙しなければなりません。例えば、 'console' という名前のハ
ンドラを生成し、'startup' という名前のロガーがメッセージを 'console'
ハンドラに送るというようなことを記述します。これらのオブジェクトは、
"logging" モジュールによって提供されるものに限らず、独自のフォーマッタ
やハンドラクラスを書くことも出来ます。このクラスへのパラメータは、
"sys.stderr" のような外部オブジェクトを必要とすることもあります。これ
らのオブジェクトとつながりを記述する構文は、以下の オブジェクトの接続
で定義されています。


辞書スキーマの詳細
------------------

"dictConfig()" に渡される辞書は、以下のキーを含んでいなければなりませ
ん:

* *version* - スキーマのバージョンを表す整数値に設定されます。現在有効
  な値は 1 だけですが、このキーがあることで、このスキーマは後方互換性
  を保ちながら発展できます。

その他すべてのキーは省略可能ですが、与えられたなら以下に記述するように
解釈されます。以下のすべての場合において、 '環境設定辞書' と記載されて
いる所では、その辞書に特殊な "'()'" キーがあるかを調べることで、カスタ
ムのインスタント化が必要であるか判断されます。その場合は、以下の ユー
ザ定義オブジェクト で記述されている機構がインスタンス生成に使われます
。そうでなければ、インスタンス化するべきものを決定するのにコンテキスト
が使われます。

* *formatters* - 対応する値は辞書で、そのそれぞれのキーがフォーマッタ
  id になり、それぞれの値が対応する "Formatter" インスタンスをどのよう
  に環境設定するかを記述する辞書になります。

  環境設定辞書から、 (デフォルトが "None" の) キー "format" と
  "datefmt" を検索し、それらが "Formatter" インスタンスを構成するのに
  使われます。

* *filters* - 対応する値は辞書で、そのそれぞれのキーがフィルタ id にな
  り、それぞれの値が対応する Filter インスタンスをどのように環境設定す
  るかを記述する辞書になります。

  環境設定辞書は、(デフォルトが空文字列の) キー "name" を検索され、そ
  れらが "logging.Filter" インスタンスを構成するのに使われます。

* *handlers* - 対応する値は辞書で、そのそれぞれのキーがハンドラ id に
  なり、それぞれの値が対応する Handler インスタンスをどのように環境設
  定するかを記述する辞書になります。

  環境設定辞書は、以下のキーを検索されます:

  * "class" (必須)。これはハンドラクラスの完全に修飾された名前です。

  * "level" (任意)。ハンドラのレベルです。

  * "formatter" (任意)。このハンドラへのフォーマッタの id です。

  * "filters" (任意)。このハンドラへのフィルタの id のリストです。

  その他の *すべての* キーは、ハンドラのコンストラクタにキーワード引数
  として渡されます。例えば、以下のコード片が与えられたとすると:

     handlers:
       console:
         class : logging.StreamHandler
         formatter: brief
         level   : INFO
         filters: [allow_foo]
         stream  : ext://sys.stdout
       file:
         class : logging.handlers.RotatingFileHandler
         formatter: precise
         filename: logconfig.log
         maxBytes: 1024
         backupCount: 3

  id が "console" であるハンドラが、 "sys.stdout" を根底のストリームに
  して、 "logging.StreamHandler" としてインスタンス化されます。id が
  "file" であるハンドラが、 "filename='logconfig.log', maxBytes=1024,
  backupCount=3" をキーワード引数にして、
  "logging.handlers.RotatingFileHandler" としてインスタンス化されます
  。

* *loggers* - 対応する値は辞書で、そのそれぞれのキーがロガー名になり、
  それぞれの値が対応する Logger インスタンスをどのように環境設定するか
  を記述する辞書になります。

  環境設定辞書は、以下のキーを検索されます:

  * "level" (任意)。ロガーのレベルです。

  * "propagate" (任意)。ロガーの伝播の設定です。

  * "filters" (任意)。このロガーへのフィルタの id のリストです。

  * "handlers" (任意)。このロガーへのハンドラの id のリストです。

  指定されたロガーは、指定されたレベル、伝播、ハンドラに従って環境設定
  されます。

* *root* - これは、ルートロガーへの設定になります。この環境設定の進行
  は、"propagate" 設定が適用されないことを除き、他のロガーと同じです。

* *incremental* - この環境設定が既存の環境設定に対する増分として解釈さ
  れるかどうかです。この値のデフォルトは "False" で、指定された環境設
  定は、既存の "fileConfig()" API によって使われているのと同じ意味上で
  、既存の環境設定を置き換えます。

  指定された値が "True" なら、環境設定は 増分設定 の節で記述されている
  ように進行します。

* *disable_existing_loggers* - 既存の非ルートロガーをすべて無効にする
  べきかどうかです。この設定は、 "fileConfig()" における同じ名前のパラ
  メータと同じです。設定されていなければ、このパラメータのデフォルトは
  "True" です。この値は、 *incremental* が "True" なら無視されます。


増分設定
--------

増分設定に完全な柔軟性を提供するのは難しいです。例えば、フィルタやフォ
ーマッタのようなオブジェクトは匿名なので、一旦環境設定がなされると、設
定を拡張するときにそのような匿名オブジェクトを参照することができません
。

さらに、一旦環境設定がなされた後、実行時にロガー、ハンドラ、フィルタ、
フォーマッタのオブジェクトグラフを任意に変えなければならない例もありま
せん。ロガーとハンドラの冗長性は、レベル (または、ロガーの場合には、伝
播フラグ) を設定することによってのみ制御できます。安全な方法でオブジェ
クトグラフを任意に変えることは、マルチスレッド環境で問題となります。不
可能ではないですが、その効用は実装に加えられる複雑さに見合いません。

従って、環境設定辞書の "incremental" キーが与えられ、これが "True" で
あるとき、システムは "formatters" と "filters" の項目を完全に無視し、
"handlers" の項目の "level" 設定と、"loggers" と "root" の項目の
"level" と "propagate" 設定のみを処理します。

環境設定辞書の値を使うことで、設定は pickle 化された辞書としてネットワ
ークを通してソケットリスナに送ることができます。これにより、長時間起動
するアプリケーションのロギングの冗長性を、アプリケーションを止めて再起
動する必要なしに、いつでも変更することができます。


オブジェクトの接続
------------------

このスキーマは、ロギングオブジェクトの一揃い - ロガー、ハンドラ、フォ
ーマッタ、フィルタ - について記述します。これらは、オブジェクトグラフ
上でお互い接続されます。従って、このスキーマは、オブジェクト間の接続を
表現しなければなりません。例えば、環境設定で、特定のロガーが特定のハン
ドラに取り付けられたとします。この議論では、ロガーとハンドラが、これら
2 つの接続のそれぞれ送信元と送信先であるといえます。もちろん、この設定
オブジェクト中では、これはハンドラへの参照を保持しているロガーで表され
ます。設定辞書中で、これは次のようになされます。まず、送信先オブジェク
トを曖昧さなく指定する id を与えます。そして、その id を送信元オブジェ
クトの環境設定で使い、送信元とその id をもつ送信先が接続されていること
を示します。

ですから、例えば、以下の YAML のコード片を例にとると:

   formatters:
     brief:
       # configuration for formatter with id 'brief' goes here
     precise:
       # configuration for formatter with id 'precise' goes here
   handlers:
     h1: #This is an id
      # configuration of handler with id 'h1' goes here
      formatter: brief
     h2: #This is another id
      # configuration of handler with id 'h2' goes here
      formatter: precise
   loggers:
     foo.bar.baz:
       # other configuration for logger 'foo.bar.baz'
       handlers: [h1, h2]

(注釈: YAML がここで使われているのは、辞書の等価な Python 形式よりもこ
ちらのほうが少し読みやすいからです。)

ロガーの id は、プログラム上でロガーへの参照を得るために使われるロガー
名で、たとえば "foo.bar.baz" です。フォーマッタとフィルタの id は、(上
の "brief", "precise" のような) 任意の文字列値にできます。これらは一時
的なもので、環境設定辞書の処理にのみ意味があり、オブジェクト間の接続を
決定するのに使われます。また、これらは設定の呼び出しが完了したとき、ど
こにも残りません。

上記のコード片は、"foo.bar.baz" というの名ロガーに、ハンドラ id "h1"
と "h2" で表される 2 つのハンドラを接続することを示します。"h1" のフォ
ーマッタは id "brief" で記述されるもので、"h2" のフォーマッタは id
"precise" で記述されるものです。


ユーザ定義オブジェクト
----------------------

このスキーマは、ハンドラ、フィルタ、フォーマッタのための、ユーザ定義オ
ブジェクトをサポートします。(ロガーは、異なるインスタンスに対して異な
る型を持つ必要はないので、この環境設定スキーマは、ユーザ定義ロガークラ
スをサポートしていません。)

設定されるオブジェクトは、それらの設定を詳述する辞書によって記述されま
す。場所によっては、あるオブジェクトがどのようにインスタンス化されるか
というコンテキストを、ロギングシステムが推測できます。しかし、ユーザ定
義オブジェクトがインスタンス化されるとき、システムはどのようにこれを行
うかを知りません。ユーザ定義オブジェクトのインスタンス化を完全に柔軟な
ものにするため、ユーザは 'ファクトリ' - 設定辞書を引数として呼ばれ、イ
ンスタンス化されたオブジェクトを返す呼び出し可能オブジェクト - を提供
する必要があります。これは特殊キー "'()'" で利用できる、ファクトリへの
絶対インポートパスによって合図されます。ここに具体的な例を挙げます:

   formatters:
     brief:
       format: '%(message)s'
     default:
       format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
       datefmt: '%Y-%m-%d %H:%M:%S'
     custom:
         (): my.package.customFormatterFactory
         bar: baz
         spam: 99.9
         answer: 42

上記の YAML コード片は 3 つのフォーマッタを定義します。 1 つ目は、id
が "brief" で、指定されたフォーマット文字列をもつ、標準
"logging.Formatter" インスタンスです。 2 つ目は、id が "default" で、
長いフォーマットを持ち、時間フォーマットも定義していて、結果はその 2
つのフォーマット文字列で初期化された "logging.Formatter" になります。
Python ソース形式で見ると、 "brief" と "default" フォーマッタは、それ
ぞれ設定の部分辞書:

   {
     'format' : '%(message)s'
   }

および:

   {
     'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s',
     'datefmt' : '%Y-%m-%d %H:%M:%S'
   }

を持ち、これらの辞書が特殊キー "'()'" を持たないので、インスタンス化は
コンテキストから推測され、結果として標準の "logging.Formatter" インス
タンスが生成されます。id が "custom" である、3 つ目のフォーマッタの設
定をする部分辞書は:

   {
     '()' : 'my.package.customFormatterFactory',
     'bar' : 'baz',
     'spam' : 99.9,
     'answer' : 42
   }

で、ユーザ定義のインスタンス化が望まれることを示す特殊キー "'()'" を含
みます。この場合、指定された呼び出し可能ファクトリオブジェクトが使われ
ます。これが実際の呼び出し可能オブジェクトであれば、それが直接使われま
す - そうではなく、(この例でのように) 文字列を指定したなら、実際の呼び
出し可能オブジェクトは、通常のインポート機構を使って検索されます。その
呼び出し可能オブジェクトは、環境設定の部分辞書の、**残りの** 要素をキ
ーワード引数として呼ばれます。上記の例では、id が "custom" のフォーマ
ッタは、以下の呼び出しによって返されるものとみなされます:

   my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42)

キー "'()'" が特殊キーとして使われるのは、キーワードパラメータ名として
不正で、呼び出しに使われるキーワード引数と衝突し得ないからです。"'()'"
はまた、対応する値が呼び出し可能オブジェクトであると覚えやすくします。


外部オブジェクトへのアクセス
----------------------------

環境設定が、例えば "sys.stderr" のような、設定の外部のオブジェクトへの
参照を必要とすることがあります。設定辞書が Python コードで構成されてい
れば話は簡単ですが、これがテキストファイル (JSON, YAML 等) を通して提
供されていると問題となります。テキストファイルでは、"sys.stderr" をリ
テラル文字列 "'sys.stderr'" と区別する標準の方法がありません。この区別
を容易にするため、環境設定システムは、文字列中の特定の特殊接頭辞を見つ
け、それらを特殊に扱います。例えば、リテラル文字列
"'ext://sys.stderr'" が設定中の値として与えられたら、この "ext://" は
剥ぎ取られ、この値の残りが普通のインポート機構で処理されます。

このような接頭辞の処理は、プロトコルの処理と同じようになされます。どち
らの機構も、正規表現 "^(?P<prefix>[a-z]+)://(?P<suffix>.*)$" にマッチ
する接頭辞を検索し、それによって "prefix" が認識されたなら、接頭辞に応
じたやり方で "suffix" が処理され、その処理の結果によって文字列値が置き
換えられます。接頭辞が認識されなければ、その文字列値はそのまま残されま
す。


内部オブジェクトへのアクセス
----------------------------

外部オブジェクトと同様、環境設定内部のオブジェクトへのアクセスを必要と
することもあります。これは、その各オブジェクトを司る環境設定システムに
よって暗黙に行われます。例えば、ロガーやハンドラの "level" に対する文
字列値 "'DEBUG'" は、自動的に値 "logging.DEBUG" に変換されますし、
"handlers", "filters" および "formatter" の項目は、オブジェクト id を
取って、適切な送信先オブジェクトを決定します。

しかし、ユーザ定義モジュールには、 "logging" モジュールには分からない
ような、より一般的な機構が必要です。例えば、
"logging.handlers.MemoryHandler" があって、委譲する先の別のハンドラで
ある "target" 引数を取るとします。システムはこのクラスをすでに知ってい
ますから、設定中で、与えられた "target" は関連するターゲットハンドラの
オブジェクト id でさえあればよく、システムはその id からハンドラを決定
します。しかし、ユーザが "my.package.MyHandler" を定義して、それが
"alternate" ハンドラを持つなら、設定システムは "alternate" がハンドラ
を参照していることを知りません。これを知らせるのに、一般的な解析システ
ムで、ユーザはこのように指定できます:

   handlers:
     file:
       # configuration of file handler goes here

     custom:
       (): my.package.MyHandler
       alternate: cfg://handlers.file

リテラル文字列 "'cfg://handlers.file'" は、"ext://" 接頭辞が付いた文字
列と同じように分析されますが、インポート名前空間ではなく、環境設定自体
が検索されます。この機構は "str.format" でできるのと同じようにドットや
インデックスのアクセスができます。従って、環境設定において以下のコード
片が与えられれば:

   handlers:
     email:
       class: logging.handlers.SMTPHandler
       mailhost: localhost
       fromaddr: my_app@domain.tld
       toaddrs:
         - support_team@domain.tld
         - dev_team@domain.tld
       subject: Houston, we have a problem.

文字列 "'cfg://handlers'" は、キー "handlers" をもつ辞書であると分析さ
れ、文字列 "'cfg://handlers.email'" は、"handlers" 辞書内の、"email"
キーをもつ辞書であると分析されます。文字列
"'cfg://handlers.email.toaddrs[1]" は、"'dev_team@domain.tld'" と分析
され、"'cfg://handlers.email.toaddrs[0]'" は値
"'support_team@domain.tld'" と分析されます。"subject" の値には、
"'cfg://handlers.email.subject'" または等価な
"'cfg://handlers.email[subject]'" でアクセスできます。後者が必要なのは
、キーがスペースや非アルファベット文字を含むときのみです。インデックス
値が十進数字のみで構成されているなら、まず対応する整数値を使ってアクセ
スが試みられ、必要なら文字列値で代替します。

文字列 "cfg://handlers.myhandler.mykey.123" が与えられると、これは
"config_dict['handlers']['myhandler']['mykey']['123']" と分析されます
。文字列が "cfg://handlers.myhandler.mykey[123]" と指定されたら、シス
テムは "config_dict['handlers']['myhandler']['mykey'][123]" から値を引
き出そうとし、失敗したら
"config_dict['handlers']['myhandler']['mykey']['123']" で代替します。


インポート解決とカスタムインポーター
------------------------------------

インポート解決は、デフォルトではインポートを行うために "__import__()"
組み込み関数を使用します。これを独自のインポートメカニズムに置き換えた
いと思うかもしれません: もしそうなら、 "DictConfigurator" あるいはその
上位クラスである "BaseConfigurator" クラスの "importer" 属性を置換する
ことができます。ただし、この関数はクラスからディスクリプタ経由でアクセ
スされる点に注意する必要があります。インポートを行うために Python
callable を使用していて、それをインスタンスレベルではなくクラスレベル
で定義したければ、 "staticmethod()" でそれをラップする必要があります。
例えば:

   from importlib import import_module
   from logging.config import BaseConfigurator

   BaseConfigurator.importer = staticmethod(import_module)

configurator *インスタンス* に対してインポート callable をセットする場
合は、 "staticmethod()" でラップする必要はありません。


環境設定ファイルの書式
======================

"fileConfig()" が解釈できる環境設定ファイルの形式は、 "configparser"
の機能に基づいています。ファイルには、 "[loggers]", "[handlers]",
"[formatters]" といったセクションが入っていなければならず、各セクショ
ンではファイル中で定義されている各タイプのエンティティを名前で指定して
います。こうしたエンティティの各々について、そのエンティティをどう設定
するかを示した個別のセクションがあります。すなわち、 "log01" という名
前の "[loggers]" セクションにあるロガーに対しては、対応する詳細設定が
セクション "[logger_log01]" に収められています。同様に、 "hand01" とい
う名前の "[handlers]" セクションにあるハンドラは "[handler_hand01]" と
呼ばれるセクションに設定をもつことになり、 "[formatters]" セクションに
ある "form01" は "[formatter_form01]" というセクションで設定が指定され
ています。ルートロガーの設定は "[logger_root]" と呼ばれるセクションで
指定されていなければなりません。

注釈:

  "fileConfig()" API は "dictConfig()" API よりも古く、ロギングのある
  種の側面についてカバーする機能に欠けています。たとえば
  "fileConfig()" では数値レベルを超えたメッセージを単に拾うフィルタリ
  ングを行う "Filter" オブジェクトを構成出来ません。 "Filter" のインス
  タンスをロギングの設定において持つ必要があるならば、 "dictConfig()"
  を使う必要があるでしょう。設定の機能における将来の拡張は
  "dictConfig()" に対して行われることに注意してください。ですから、そ
  うするのが便利であるときに新しい API に乗り換えるのは良い考えです。

ファイルにおけるこれらのセクションの例を以下に示します。

   [loggers]
   keys=root,log02,log03,log04,log05,log06,log07

   [handlers]
   keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09

   [formatters]
   keys=form01,form02,form03,form04,form05,form06,form07,form08,form09

ルートロガーでは、レベルとハンドラのリストを指定しなければなりません。
ルートロガーのセクションの例を以下に示します。

   [logger_root]
   level=NOTSET
   handlers=hand01

"level" エントリは "DEBUG, INFO, WARNING, ERROR, CRITICAL" のうちの一
つか、 "NOTSET" になります。ルートロガーの場合にのみ、 "NOTSET" はすべ
てのメッセージがログ記録されることを意味します。レベル値は "logging"
パッケージの名前空間のコンテキストにおいて "eval()" されます。

"handlers" エントリはコンマで区切られたハンドラ名からなるリストで、
"[handlers]" セクションになくてはなりません。また、これらの各ハンドラ
の名前に対応するセクションが設定ファイルに存在しなければなりません。

ルートロガー以外のロガーでは、いくつか追加の情報が必要になります。これ
は以下の例のように表されます。

   [logger_parser]
   level=DEBUG
   handlers=hand01
   propagate=1
   qualname=compiler.parser

"level" および "handlers" エントリはルートロガーのエントリと同様に解釈
されますが、非ルートロガーのレベルが "NOTSET" に指定された場合、ロギン
グシステムはロガー階層のより上位のロガーにロガーの実効レベルを問い合わ
せるところが違います。"propagate" エントリは、メッセージをロガー階層に
おけるこのロガーの上位のハンドラに伝播させることを示す 1 に設定される
か、メッセージを階層の上位に伝播 **しない** ことを示す 0 に設定されま
す。"qualname" エントリはロガーのチャネル名を階層的に表したもの、すな
わちアプリケーションがこのロガーを取得する際に使う名前になります。

ハンドラの環境設定を指定しているセクションは以下の例のようになります。

   [handler_hand01]
   class=StreamHandler
   level=NOTSET
   formatter=form01
   args=(sys.stdout,)

"class" エントリはハンドラのクラス ("logging" パッケージの名前空間にお
いて "eval()" で決定されます) を示します。 "level" はロガーの場合と同
じように解釈され、 "NOTSET" は "すべてを記録する (log everything)" と
解釈されます。

"formatter" エントリはこのハンドラのフォーマッタに対するキー名を表しま
す。空文字列の場合、デフォルトのフォーマッタ
("logging._defaultFormatter") が使われます。名前が指定されている場合、
その名前は "[formatters]" セクションになくてはならず、対応するセクショ
ンが設定ファイル中になければなりません。

"args" エントリは、 "logging" パッケージの名前空間のコンテキストで
"eval()" される際、ハンドラクラスのコンストラクタに対する引数からなる
リストになります。典型的なエントリがどうやって作成されるかについては、
対応するハンドラのコンストラクタか、以下の例を参照してください。もし指
定しなかった場合にはデフォルトは "()" となります。

オプションの "kwargs" エントリーは、 "eval()" が "logging" パッケージ
の名前空間のコンテキストで利用された時に、ハンドラークラスのコンストラ
クタに渡されるキーワード引数辞書となります。指定されなかったときのデフ
ォルトは "{}" です。

   [handler_hand02]
   class=FileHandler
   level=DEBUG
   formatter=form02
   args=('python.log', 'w')

   [handler_hand03]
   class=handlers.SocketHandler
   level=INFO
   formatter=form03
   args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)

   [handler_hand04]
   class=handlers.DatagramHandler
   level=WARN
   formatter=form04
   args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)

   [handler_hand05]
   class=handlers.SysLogHandler
   level=ERROR
   formatter=form05
   args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)

   [handler_hand06]
   class=handlers.NTEventLogHandler
   level=CRITICAL
   formatter=form06
   args=('Python Application', '', 'Application')

   [handler_hand07]
   class=handlers.SMTPHandler
   level=WARN
   formatter=form07
   args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')
   kwargs={'timeout': 10.0}

   [handler_hand08]
   class=handlers.MemoryHandler
   level=NOTSET
   formatter=form08
   target=
   args=(10, ERROR)

   [handler_hand09]
   class=handlers.HTTPHandler
   level=NOTSET
   formatter=form09
   args=('localhost:9022', '/log', 'GET')
   kwargs={'secure': True}

フォーマッタの環境設定を指定しているセクションは以下のような形式です。

   [formatter_form01]
   format=F1 %(asctime)s %(levelname)s %(message)s
   datefmt=
   class=logging.Formatter

The "format" entry is the overall format string, and the "datefmt"
entry is the "strftime()"-compatible date/time format string.  If
empty, the package substitutes something which is almost equivalent to
specifying the date format string "'%Y-%m-%d %H:%M:%S'".  This format
also specifies milliseconds, which are appended to the result of using
the above format string, with a comma separator.  An example time in
this format is "2003-01-23 00:29:50,411".

"class" エントリはオプションです。これはフォーマッタクラスの名前を (モ
ジュール名とクラス名をドットでつないだもので) 指し示すものです。このオ
プションは "Formatter" の子クラスをインスタンス化するのに便利です。
"Formatter" の子クラスが、展開もしくは要約された形式の例外トレースバッ
クを表示することができます。

注釈:

  "eval()" を使用していることで、上述のようにソケット経由で設定を送受
  信するために "listen()" を使用していることに起因する潜在的なセキュリ
  ティリスクがあります。そのリスクは、相互に信頼できない多数のユーザが
  同じマシン上でコードを実行する場合に制限されています; 詳細は
  "listen()" ドキュメンテーションを参照してください。

参考:

  "logging" モジュール
     logging モジュールの API リファレンス。

  "logging.handlers" モジュール
     logging モジュールに含まれる、便利なハンドラです。
