"logging.config" --- ログ記録の環境設定
***************************************

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


Important
^^^^^^^^^

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

* 基本チュートリアル

* 上級チュートリアル

* ロギングクックブック

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

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


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

以下の関数は logging モジュールを設定します。 これらは
"logging.config" モジュールにあります。 使用は任意です。 これらの関数
を使うほか、メイン API ("logging" 自体で定義されています) を呼び出し、
"logging" または "logging.handlers" で宣言されているハンドラを定義する
ことでも logging モジュールを設定できます。

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()" を呼び出せます。

   Added in version 3.2.

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

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

   ファイルが存在しない場合は "FileNotFoundError" を、ファイルが不正ま
   たは空の場合は "RuntimeError" を送出します。

   パラメータ:
      * **fname** -- ファイル名、ファイルライクオブジェクト、または
        "RawConfigParser" の派生クラスのインスタンスです。
        "RawConfigParser" の派生クラスのインスタンスが渡されると、その
        まま使われます。 それ以外の場合は "ConfigParser" のインスタン
        スを生成し、"fname" に渡されたオブジェクトから設定を読み込みま
        す。 そのオブジェクトが "readline()" メソッドを持つ場合はファ
        イルライクオブジェクトとみなして "read_file()" で読み込みます
        。 それ以外の場合はファイル名とみなして "read()" に渡します。

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

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

      * **encoding** -- *fname* がファイル名の場合に、ファイルをオープ
        ンする時のエンコーディングです。

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

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

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

   バージョン 3.10 で変更: *encoding* パラメータが追加されました。

   バージョン 3.12 で変更: 指定されたファイルが存在しない場合、不正な
   場合、または空の場合に例外を送出するようになりました。

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()" が呼ばれる前に呼び出しま
   す。


セキュリティで考慮すべき点
==========================

logging の設定機能は便利さを提供します。その便利さの一部は設定ファイル
内のテキストを logging の設定に使われる Python オブジェクトに変換する
機能を提供することによって実現されています - たとえば、 ユーザ定義オブ
ジェクト で説明されているような機能です。しかし、まさにこのメカニズム
(実行可能オブジェクトをユーザー定義モジュールからインポートし、設定フ
ァイルから読み込んだパラメータを使ってそれらを呼び出すこと) が任意のコ
ードを呼び出すことに利用できる可能性があります。そして、この理由により
、信頼できない情報源から取得した設定ファイルは *細心の注意* を払って取
り扱わなければなりません。そのようなファイルをロードする場合、そのファ
イルがいかなる問題も起こさないと確認した上で実際にファイルをロードして
ください。


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

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


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

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

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

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

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

  設定辞書は "Formatter"  オブジェクトを作成するときの引数に対応する、
  次のようなオプションキーがないか探索されます。

  * "format"

  * "datefmt"

  * "style"

  * "validate" (バージョン3.8以降)

  * "defaults" (バージョン 3.12 以降)

  オプションの "class" キーはフォーマッタークラスの名前を表します（モ
  ジュールとクラス名をドットで繋げる）。インスタンス化時の引数は
  "Formatter" と同じであるため、このキーは "Formatter" をカスタマイズ
  したサブクラスのインスタンス化に使うのがもっとも便利です。例えばトレ
  ースバックにさらに情報を付加したり、情報を要約代替クラスを実装すると
  いったことが想定されます。もし、自作のフォーマッターが異なる引数や追
  加の設定引数を持つ場合は、 ユーザ定義オブジェクト を使うべきです。

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

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

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

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

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

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

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

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

    バージョン 3.11 で変更: "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 のリストです。

    バージョン 3.11 で変更: "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:
       # id が 'brief' のフォーマッタの設定をここに記述する
     precise:
       # id が 'precise' のフォーマッタの設定をここに記述する
   handlers:
     h1: # これは id
      # id が 'h1' のハンドラの設定をここに記述する
      formatter: brief
     h2: # これは別の id
      # id が 'h2' のハンドラの設定をここに記述する
      formatter: precise
   loggers:
     foo.bar.baz:
       # ロガー '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)

警告:

  上記の例で "bar", "spam" または "answer" のようなキーワード引数に指
  定する値は、設定作業の途中で処理されることなくそのまま呼び出し可能オ
  ブジェクトに渡されるため、 "cfg://foo" や "ext://bar" のような設定用
  の辞書や参照であってはいけません。

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

バージョン 3.11 で変更: "handlers" と "loggers" の "filters" メンバー
は id に加えてフィルタのインスタンスを受け取ることができるようになりま
した。

属性名から値へのマッピングを値とする特殊キー "'.'" も指定できます。 こ
のキーがあれば、ユーザ定義オブジェクトを返す前に、指定された属性が設定
されます。 例えば、次のように設定します。

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

戻り値のフォーマッタは属性 "foo" と "baz" をもち、それぞれの値は
"'bar'" と "'bozz'" に設定されます。

警告:

  上記の例で "foo" や "baz" のような属性に設定する値は、フォーマッタの
  設定作業の途中で処理されることなくそのまま属性値として設定されるため
  、 "cfg://foo" や "ext://bar" のような設定用の辞書や参照であってはい
  けません。


ハンドラの設定順序
------------------

ハンドラはキーのアルファベット順に設定され、設定済みのハンドラはスキー
マの "handlers" 辞書 (の作業用コピー) 内にある設定辞書を置き換えます。
"cfg://handlers.foo" のような記法を使った場合、"handlers['foo']" は初
めは "foo" という名前のハンドラの設定辞書を指し、そのハンドラが設定さ
れた後は設定済みのハンドラのインスタンスを指します。 そのため、
"cfg://handlers.foo" は辞書にもハンドラのインスタンスにも解決され得ま
す。 一般に、依存先のハンドラがすべて設定された *後* に依存するハンド
ラが設定されるよう、ハンドラに名前を付けるのがよいでしょう。 そうすれ
ば、ハンドラ "foo" に依存するハンドラの設定で "cfg://handlers.foo" の
ような記法を使えます。 依存するハンドラの名前が "bar" だと、"foo" より
先に "bar" を設定しようとして、まだ "foo" が設定されていないため問題に
なります。 一方、名前が "foobar" であれば "foo" の後に設定されるため、
"cfg://handlers.foo" は設定辞書ではなく、設定済みのハンドラ "foo" に解
決されます。


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

環境設定が、例えば "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:
       # ファイルハンドラの設定をここに記述する

     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()" でラップする必要はありません。


QueueHandler と QueueListener の設定
------------------------------------

"QueueHandler" は通常 "QueueListener" と組み合わせて使うため、両方をま
とめて設定できます。 設定後、生成されたハンドラの "listener" 属性で
"QueueListener" インスタンスを取得できます。 このハンドラ自体は、設定
で "QueueHandler" に付けた名前を "getHandlerByName()" に渡して取得でき
ます。 この組を設定するための辞書スキーマを、次の YAML のコード片に示
します。

   handlers:
     qhand:
       class: logging.handlers.QueueHandler
       queue: my.module.queue_factory
       listener: my.package.CustomListener
       handlers:
         - hand_name_1
         - hand_name_2
         ...

"queue" キーと "listener" キーは省略可能です。

"queue" キーがある場合、対応する値には次のいずれかを指定できます:

* "Queue.put_nowait" と "Queue.get" の公開 API を実装したオブジェクト
  。 例えば、"queue.Queue" またはそのサブクラスの実際のインスタンスや
  、"multiprocessing.managers.SyncManager.Queue()" で取得したプロキシ
  を指定できます。

  もちろん、これが可能なのはコード内で設定辞書を作成または変更する場合
  だけです。

* 引数なしで呼び出すと使用するキューのインスタンスを返す、呼び出し可能
  オブジェクトに解決される文字列。 この呼び出し可能オブジェクトは
  "queue.Queue" のサブクラスでも、"my.module.queue_factory()" のように
  適切なキューのインスタンスを返す関数でも構いません。

* ユーザ定義オブジェクト で説明している通常の方法で構築するための、
  "'()'" キーを持つ辞書。 この構築の結果は "queue.Queue" のインスタン
  スでなければなりません。

"queue" キーがない場合は、サイズに上限のない標準の "queue.Queue" イン
スタンスを作成して使います。

"listener" キーがある場合、対応する値には次のいずれかを指定できます:

* "logging.handlers.QueueListener" のサブクラス。 もちろん、これが可能
  なのはコード内で設定辞書を作成または変更する場合だけです。

* "'my.package.CustomListener'" のような、"QueueListener" のサブクラス
  に解決される文字列。

* ユーザ定義オブジェクト で説明している通常の方法で構築するための、
  "'()'" キーを持つ辞書。 この構築の結果は、"QueueListener" の初期化メ
  ソッドと同じシグネチャを持つ呼び出し可能オブジェクトでなければなりま
  せん。

"listener" キーがない場合は "logging.handlers.QueueListener" を使いま
す。

"handlers" キーの値は、設定内の他のハンドラ (上のコード片では省略して
います) の名前で、これらのハンドラはキューリスナに渡されます。

独自のキューハンドラやリスナのクラスは、"QueueHandler" および
"QueueListener" と同じ初期化シグネチャを持つよう定義する必要があります
。

Added in version 3.12.


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

"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" パッケージの名前空間のコンテキストで 評価 されま
す。

"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" パッケージの名前空間のコンテキストで 評価 される
時点では、ハンドラークラスのコンストラクタのための引数のリストです。典
型的なコンストラクタの引数が生成される方法については、各ハンドラのコン
ストラクタまたは下記の例を参照してください。指定されなければデフォルト
は "()" になります。

オプションの "kwargs" は、 "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 %(customfield)s
   datefmt=
   style=%
   validate=True
   defaults={'customfield': 'defaultvalue'}
   class=logging.Formatter

フォーマッターのセクション の辞書スキーマと同じキーを持つ、フォーマッ
ター設定の引数です。

"defaults" エントリは、"logging" パッケージの名前空間で 評価 すると、
独自の書式フィールドのデフォルト値を持つ辞書になります。 指定しない場
合のデフォルトは "None" です。

注釈:

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

参考:

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

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