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

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


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

環境設定のための関数

以下の関数は logging モジュールの環境設定をします。これらの関数は、 logging.config にあります。これらの関数の使用はオプションです --- logging モジュールはこれらの関数を使うか、 (logging 自体で定義されている) 主要な API を呼び出し、 logginglogging.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, encoding=None)

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

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

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

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

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

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

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

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

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_loggersFalse を指定した設定を送信できます。

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以降)

    オプションの class キーはフォーマッタークラスの名前を表します(モジュールとクラス名をドットで繋げる)。インスタンス化時の引数は Formatter と同じであるため、このキーは 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 です。この値は、 incrementalTrue なら無視されます。

増分設定

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

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

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

環境設定辞書の値を使うことで、設定は 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 h1h2 で表される 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 ソース形式で見ると、 briefdefault フォーマッタは、それぞれ設定の部分辞書:

{
  '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)

警告

The values for keys such as bar, spam and answer in the above example should not be configuration dictionaries or references such as cfg://foo or ext://bar, because they will not be processed by the configuration machinery, but passed to the callable as-is.

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

特殊なキーワード '.' を指定することもできます。この場合その値には属性名とその値のマッピングをあらわす辞書を指定します。指定された属性は、もしそれが見つかった場合、戻り値のユーザー定義オブジェクトに設定されます。したがって、以下のように設定すると:

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

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

警告

The values for attributes such as foo and baz in the above example should not be configuration dictionaries or references such as cfg://foo or ext://bar, because they will not be processed by the configuration machinery, but set as attribute values as-is.

Handler configuration order

Handlers are configured in alphabetical order of their keys, and a configured handler replaces the configuration dictionary in (a working copy of) the handlers dictionary in the schema. If you use a construct such as cfg://handlers.foo, then initially handlers['foo'] points to the configuration dictionary for the handler named foo, and later (once that handler has been configured) it points to the configured handler instance. Thus, cfg://handlers.foo could resolve to either a dictionary or a handler instance. In general, it is wise to name handlers in a way such that dependent handlers are configured _after_ any handlers they depend on; that allows something like cfg://handlers.foo to be used in configuring a handler that depends on handler foo. If that dependent handler were named bar, problems would result, because the configuration of bar would be attempted before that of foo, and foo would not yet have been configured. However, if the dependent handler were named foobar, it would be configured after foo, with the result that cfg://handlers.foo would resolve to configured handler foo, and not its configuration dictionary.

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

環境設定が、例えば 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=
style=%
validate=True
class=logging.Formatter

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

注釈

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

参考

logging モジュール

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

logging.handlers モジュール

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