目次

前のトピックへ

15.5. getopt — コマンドラインオプションのパーザ

次のトピックへ

15.7. getpass — 可搬性のあるパスワード入力機構

このページ

15.6. logging — Python 用ロギング機能

バージョン 2.3 で追加.

このモジュールでは、アプリケーションのための柔軟なエラーログ記録 (logging) システムを実装するための関数やクラスが定義されています。

ログ記録は Logger クラスのインスタンス (以降 ロガー :logger) のメソッドを呼び出すことで行われます。各インスタンスは名前をもち、ドット (ピリオド) を区切り文字として表記することで、概念的には名前空間中の階層構造に配置されることになります。例えば、 “scan” と名づけられたロガーは “scan.text”, “scan.html”, “scan.pdf” ロガーの親ロガーとなります。ロガーにはどんな名前を付けてもよく、ロガー名はログに記録されるメッセージの生成元となるアプリケーション中の特定の領域を示すことになります。

ログ記録されたメッセージにはまた、重要度レベル (level of importance) が関連付けられています。デフォルトのレベルとして提供されているものは DEBUG, INFO, WARNING, ERROR, CRITICAL です。簡便性のために、 Logger の適切なメソッド群を呼ぶことで、ログに記録されたメッセージの重要性を指定することができます。そのようなメソッド群としては、デフォルトのレベルを反映して、 debug(), info(), warning(), error(), critical() があります。また、これらのレベルを使用することに制限されるわけではありません: Logger のより汎用的なメソッドで、明示的なレベル指定のための引数を持つ log() を使って自分自身でレベルを定義したり使用したりできます。

15.6.1. チュートリアル

標準ライブラリモジュールとしてログ記録 API が提供される利点は、すべての Python モジュールがログ記録に参加できることであり、これによってあなたが書くアプリケーションのログにサードパーティーのモジュールが出力するメッセージを含ませることができます。

もちろん、複数のメッセージをそれぞれ別々の冗長性レベルで別々の出力先にログ記録することができます。ログメッセージをファイルへ、 HTTP GET/POST 先へ、 SMTP 経由で電子メールへ、汎用のソケットへ、もしくは OS 毎のログ記録機構へ書き込むことは、すべて標準モジュールでサポートしています。これら組み込みのクラスが特別な要求仕様に合わないような場合には、独自のログ出力先クラスを作り出すこともできます。

15.6.1.1. 単純な例

ほとんどのアプリケーションではファイルにログ記録することを望むことになるでしょうから、まずはこのケースから始めましょう。 basicConfig() 関数を使って、デバッグメッセージがファイルに書き込まれるように、デフォルトのハンドラをセットアップします。 (この例では、カレントディレクトリの中に example.log というファイルを作るための適切な権限があると仮定しています)

import logging
LOG_FILENAME = 'example.log'
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)

logging.debug('This message should go to the log file')

ではこのファイルを開いて結果を確認しましょう。こんなログメッセージが見つかるでしょう:

DEBUG:root:This message should go to the log file

スクリプトを繰り返し実行すると、さらなるログメッセージがファイルに追記されていきます。毎回新しいファイルが作られた方が良ければ、 basicConfig() に渡す filemode 引数を 'w' にします。ファイルサイズを自分で管理する代わりに、 RotatingFileHandler を使うともっと簡単になります。

import glob
import logging
import logging.handlers

LOG_FILENAME = 'logging_rotatingfile_example.out'

# Set up a specific logger with our desired output level
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)

# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(
              LOG_FILENAME, maxBytes=20, backupCount=5)

my_logger.addHandler(handler)

# Log some messages
for i in range(20):
    my_logger.debug('i = %d' % i)

# See what files are created
logfiles = glob.glob('%s*' % LOG_FILENAME)

for filename in logfiles:
    print filename

結果は分割された 6 つのファイルになっているはずで、それぞれがアプリケーションのログ記録の一部になっています。

logging_rotatingfile_example.out
logging_rotatingfile_example.out.1
logging_rotatingfile_example.out.2
logging_rotatingfile_example.out.3
logging_rotatingfile_example.out.4
logging_rotatingfile_example.out.5

最新のファイルはいつでも logging_rotatingfile_example.out で、サイズの上限に達するたびに拡張子 .1 を付けた名前に改名されます。既にあるバックアップファイルはその拡張子がインクリメントされ (.1.2 になるなど)、 .6 ファイルは消去されます。

明らかに、ここでは例示のためにファイルの大きさをとんでもなく小さな値に設定しています。実際に使うときは maxBytes を適切な値に設定してください。

ログ記録 API のもう一つの便利な機能が、異なるメッセージを異なるログレベルで生成する能力です。これを使えば、たとえばコードの中にデバッグメッセージを埋め込みつつ、出荷段階でログ記録レベルを落としてデバッグメッセージが記録されないようにするといったことができます。デフォルトで設定されているレベルは NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL です。

ロガー、ハンドラ、ログメッセージ呼び出しは、どれもレベルを指定します。ログメッセージはハンドラとロガーがそのレベル以下を出力する設定の時だけ出力されます。たとえば、メッセージが CRITICAL でロガーが ERROR の設定ならば、メッセージは出力されます。一方、メッセージが WARNING でロガーが ERROR だけ生成するように設定されているならば、メッセージは出力されません。

import logging
import sys

LEVELS = {'debug': logging.DEBUG,
          'info': logging.INFO,
          'warning': logging.WARNING,
          'error': logging.ERROR,
          'critical': logging.CRITICAL}

if len(sys.argv) > 1:
    level_name = sys.argv[1]
    level = LEVELS.get(level_name, logging.NOTSET)
    logging.basicConfig(level=level)

logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical error message')

スクリプトを ‘debug’ や ‘warning’ といった引数で実行して、レベルの違いによってどのメッセージが現れるか見てみましょう。

$ python logging_level_example.py debug
DEBUG:root:This is a debug message
INFO:root:This is an info message
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical error message

$ python logging_level_example.py info
INFO:root:This is an info message
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical error message

すべてのログメッセージに root が埋め込まれていることに気付いたかもしれません。 logging モジュールは異なる名前のロガーの階層をサポートしているのです。ログメッセージがどこから発生しているかを教える簡単な方法は、プログラムのモジュールごとに別々のロガーオブジェクトを利用することです。それぞれの新しいロガーはその親の設定を「継承」していて、あるロガーに送られたログメッセージはそのロガーの名前を含みます。場合によっては、ロガーをそれぞれ異なるように設定して、それぞれのモジュールからのメッセージを異なったやり方で扱うこともできます。では、単純な例でメッセージの発信源が簡単に追跡できるように別々のモジュールからログ記録を行う方法を見てみましょう。

import logging

logging.basicConfig(level=logging.WARNING)

logger1 = logging.getLogger('package1.module1')
logger2 = logging.getLogger('package2.module2')

logger1.warning('This message comes from one module')
logger2.warning('And this message comes from another module')

出力はこうなります:

$ python logging_modules_example.py
WARNING:package1.module1:This message comes from one module
WARNING:package2.module2:And this message comes from another module

他にもオプションはもっといろいろあります。ログ記録方法の設定、たとえばログメッセージフォーマットを変えるオプション、メッセージを複数の出力先に配送するようなもの、ソケットインターフェイスを通して長時間実行されるアプリケーションの設定を途中で変更するものなどです。すべてのオプションはライブラリモジュールの文書の中でもっと細かく説明してあります。

15.6.1.2. ロガー

logging ライブラリはモジュラー・アプローチを取っていて、コンポーネントのカテゴリーをいくつかに分けています: ロガー、ハンドラ、フィルタ、フォーマッタです。ロガーはアプリケーションのコードが直接使うインターフェイスを外部に公開しています。ハンドラはログ記録を適切な出力先に送ります。フィルタはどのログ記録をハンドラに送るかを決めるさらにきめ細かい機構を提供します。フォーマッタは最終的なログ記録のレイアウトを指定します。

Logger オブジェクトの仕事は大きく三つに分かれます。一つ目は、アプリケーションが実行中にメッセージを記録できるように、いくつかのメソッドをアプリケーションから呼べるようにしています。二つ目に、ロガーオブジェクトはどのメッセージに対して作用するかを、深刻度 (デフォルトのフィルタ機構) またはフィルタオブジェクトに基づいて決定します。三つ目に、ロガーオブジェクトは関心を持っているすべてのログハンドラに関連するログメッセージを回送します。

とりわけ広く使われるロガーオブジェクトのメソッドは、二つのカテゴリーに分類できます: 設定とメッセージ送信です。

  • Logger.setLevel() はロガーが扱うログメッセージの最も低い深刻度を指定します。組み込みの深刻度の中では debug が一番低く、 critical が一番高くなります。たとえば、深刻度が info と設定されたロガーは info, warning, error, critical のメッセージしか扱わず debug メッセージは無視します。
  • Logger.addFilter()Logger.removeFilter() はロガーオブジェクトにフィルタを追加または削除します。このチュートリアルではフィルタは説明しません。

設定されたロガーオブジェクトを使い、以下のメソッドはログメッセージを作り出します:

  • Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical() はすべて、メッセージとメソッド名に対応したレベルでログ記録を作り出します。メッセージは実際にはフォーマット文字列であり、通常の文字列代入に使う %s, %d, %f などを含むことができます。残りの引数はメッセージの代入される位置に対応するオブジェクトのリストです。 **kwargs については、ログ記録メソッドが気にするキーワードは exc_info だけで、例外の情報をログに記録するかを決定するのに使います。
  • Logger.exception()Logger.error() と似たログメッセージを作成します。違いは Logger.exception() がスタックトレースを一緒にダンプすることです。例外ハンドラでだけ使うようにしてください。
  • Logger.log() はログレベルを明示的な引数として受け取ります。これは上に挙げた便宜的なログレベル毎のメソッドを使うより少しコード量が多くなりますが、独自のログレベルを使うことができます。

getLogger() は、指定されればその特定の名前の、そうでなければ root のロガーインスタンスへの参照を返します。ロガーの名前はピリオド区切りの階層構造を表します。同じ名前で getLogger() を複数回呼び出した場合、同一のロガーオブジェクトへの参照が返されます。階層リストを下ったロガーはリスト上位のロガーの子です。たとえば、名前が foo であるロガーがあったとして、 foo.bar, foo.bar.baz, foo.bam といった名前のロガーはすべて foo の子孫になります。子ロガーはメッセージを親ロガーのハンドラに伝えます。このため、アプリケーションが使っているすべてのロガーのためのハンドラを定義して設定する必要はありません。トップレベルのロガーのためのハンドラだけ設定しておいて必要に応じて子ロガーを作成すれば十分です。

15.6.1.3. ハンドラ

Handler オブジェクトは適切なログメッセージを (ログメッセージの深刻度に基づいて) ハンドラの指定された出力先に振り分けることに責任を持ちます。ロガーオブジェクトには addHandler() メソッドで 0 個以上のハンドラを追加することができます。例として、あるアプリケーションがすべてのログメッセージをログファイルに、 error 以上のすべてのログメッセージを標準出力に、 critical のメッセージはすべてメールアドレスに、それぞれ送りたいとします。この場合、 3 つの個別のハンドラがそれぞれの深刻度と宛先に応じて必要になります。

このライブラリには多くのハンドラが用意されていますが、このチュートリアルでは StreamHandlerFileHandler だけを例に取り上げます。

アプリケーション開発者にとってハンドラを扱う上で気にするべきメソッドは極々限られています。組み込みのハンドラオブジェクトを使う (つまり自作ハンドラを作らない) 開発者に関係あるハンドラのメソッドは、次の設定用のメソッドだけでしょう:

  • Handler.setLevel() メソッドは、ロガーオブジェクトの場合と同様に、適切な出力先に振り分けられるべき最も低い深刻度を指定します。なぜ 2 つも setLevel() メソッドがあるのでしょうか? ロガーで設定されるレベルは、付随するハンドラにどんな深刻度のメッセージを渡すか決めます。ハンドラで設定されるレベルは、ハンドラがどのメッセージを送るべきか決めます。
  • setFormatter() でこのハンドラが使用する Formatter オブジェクトを選択します。
  • addFilter() および removeFilter() はそれぞれハンドラへのフィルタオブジェクトの設定と解除を行います。

アプリケーションのコード中では Handler のインスタンスを直接インスタンス化して使ってはなりません。代わりに、 Handler クラスはすべてのハンドラが持つべきインターフェイスを定義し、子クラスが使える (もしくはオーバライドできる) いくつかのデフォルトの振る舞いを規定します。

15.6.1.4. フォーマッタ

フォーマッタオブジェクトは最終的なログメッセージの順序、構造および内容を設定します。基底クラスである logging.Handler とは違って、アプリケーションのコードはフォーマッタクラスをインスタンス化しても構いません。特別な振る舞いをさせたいアプリケーションではフォーマッタのサブクラスを使う可能性もあります。コンストラクタは二つのオプション引数を取ります: メッセージのフォーマット文字列と日付のフォーマット文字列です。メッセージのフォーマット文字列がなければ、デフォルトではメッセージをそのまま使います。日付のフォーマット文字列がなければ、デフォルトは

%Y-%m-%d %H:%M:%S

で、最後にミリ秒が付きます。

メッセージのフォーマット文字列は %(<dictionary key>)s 形式の文字列代入を用います。使えるキーについては Formatter オブジェクト に書いてあります。

次のメッセージフォーマット文字列は、人が読みやすい形式の時刻、メッセージの深刻度、およびメッセージの内容を、順番に出力します。

"%(asctime)s - %(levelname)s - %(message)s"

15.6.1.5. ログ記録方法の設定

ログ記録方法を設定するには、上述のような設定メソッドで (Python コードを使って) main モジュールの中でロガー、ハンドラ、フォーマッタを明示的に作成するか、ログ記録設定ファイルを作ります。以下のコードは、例としてごく単純なロガー、コンソールハンドラ、そして単純なフォーマッタを Python モジュールの中で設定しています。

import logging

# create logger
logger = logging.getLogger("simple_example")
logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# create formatter
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)

# "application" code
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")

このモジュールをコマンドラインから実行すると、以下の出力が得られます。

$ python simple_logging_module.py
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
2005-03-19 15:10:26,697 - simple_example - ERROR - error message
2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message

次の Python モジュールもロガー、ハンドラ、フォーマッタを上の例とほぼ同じ形で生成しますが、オブジェクトの名前だけが異なります。

import logging
import logging.config

logging.config.fileConfig("logging.conf")

# create logger
logger = logging.getLogger("simpleExample")

# "application" code
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")

そしてこれが logging.conf ファイルです:

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

出力は設定ファイルを使わないバージョンとほぼ同じです。

$ python simple_logging_config.py
2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message
2005-03-19 15:38:55,979 - simpleExample - INFO - info message
2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message
2005-03-19 15:38:56,055 - simpleExample - ERROR - error message
2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message

設定ファイル経由の方が Python コード経由に比べていくつかの利点を有していることが見て取れると思います。設定とコードの分離が最大の違いで、プログラマ以外にも容易にログ出力の表現を変更できます。

15.6.1.6. ライブラリのためのログ記録方法の設定

ログ記録を行うライブラリを開発するときには、いくつかその設定について考えておくべきことがあります。ライブラリを使うアプリケーションがログ記録を使っていないときに、ライブラリがログ記録を呼び出すと “No handlers could be found for logger X.Y.Z” (「ロガー X.Y.Z に対するハンドラが見つかりません」) というメッセージがコンソールに一度だけ流れます。このメッセージはログ記録の設定ミスを捕らえるためのものですが、ライブラリがログ記録を使っているとアプリケーション開発者が知らない場合混乱につながりかねません。

ライブラリがログ記録をどのように使っているのかを文書化しておくだけでなく、意図しないメッセージを出さないために何もしないハンドラを加えるように設定しておくのが良い方法です。こうすれば (ハンドラが見つかるので) メッセージが出力されるのを防ぐことができ、何も出力されないようになります。ライブラリを使ってアプリケーションを書くユーザーがログ記録の設定をするならば、おそらくその設定で何かハンドラを追加することでしょう。その中でレベルが適切に設定されていればライブラリコード中のログ記録呼び出しはそのハンドラに (普段通りに) 出力を送ります。

何もしないハンドラは以下のように簡単に定義できます。

import logging

class NullHandler(logging.Handler):
    def emit(self, record):
        pass

このハンドラのインスタンスがライブラリで使われるログ記録の名前空間の最上位ロガーに追加されなければなりません。ライブラリ foo のログ記録がすべて “foo.x.y” にマッチする名前のロガーで行われるならば、次のコードで望むような効果を得られます。

import logging

h = NullHandler()
logging.getLogger("foo").addHandler(h)

組織がいくつものライブラリを世に出しているならば、指定されるロガーの名前は単なる “foo” ではなく “orgname.foo” かもしれません。

15.6.2. ログレベル

ログレベルの数値は以下の表のように与えられています。これらは基本的に自分でレベルを定義したい人のためのもので、定義するレベルを既存のレベルの間に位置づけるためには具体的な値が必要になります。もし数値が他のレベルと同じだったら、既存の値は上書きされその名前は失われます。

レベル 数値
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0

レベルはロガーに関連付けることもでき、開発者が設定することも、保存されたログ記録設定を読み込む際に設定することもできます。ロガーに対してログ記録メソッドが呼び出されると、ロガーは自らのレベルとメソッド呼び出しに関連付けられたレベルを比較します。ロガーのレベルがメソッド呼び出しのレベルよりも高い場合、実際のログメッセージは生成されません。これはログ出力の冗長性を制御するための基本的なメカニズムです。

ログ記録されるメッセージは LogRecord クラスのインスタンスとしてエンコードされます。ロガーがあるイベントを実際にログ出力すると決定した場合、ログメッセージから LogRecord インスタンスが生成されます。

ログ記録されるメッセージは、ハンドラ (handlers) を通してディスパッチ機構にかけられます。ハンドラは Handler クラスのサブクラスのインスタンスで、ログ記録された (LogRecord 形式の) メッセージが、そのメッセージの伝達対象となる相手 (エンドユーザ、サポートデスクのスタッフ、システム管理者、開発者) に行き着くようにする役割を持ちます。ハンドラには特定の出力先を意図された LogRecord インスタンスが渡されます。各ロガーは 0 個以上のハンドラを (LoggeraddHandler() メソッド) で関連付けることができます。ロガーに直接関連付けられたハンドラに加えて、ロガーの上位にあるロガーすべてに関連付けられたハンドラ がメッセージを処理する際に呼び出されます。 (ただしロガーの propagate フラグが false 値にセットされている場合を除きます。その場合は、祖先ハンドラへの伝搬はそこで止まります。)

ロガーと同様に、ハンドラは関連付けられたレベルを持つことができます。ハンドラのレベルはロガーのレベルと同じ方法で、フィルタとして働きます。ハンドラがあるイベントを実際に処理すると決定した場合、 emit() メソッドが使われ、メッセージを出力先に送信します。ほとんどのユーザ定義の Handler のサブクラスで、この emit() をオーバライドする必要があるでしょう。

15.6.3. 有用なハンドラ集

基底クラスとなる Handler クラスに加えて、多くの有用なサブクラスが提供されています:

  1. StreamHandler のインスタンスは、ストリーム (file-like オブジェクト) にエラーメッセージを送信します。
  1. FileHandler のインスタンスは、ディスク上のファイルにエラーメッセージを送信します。
  1. BaseRotatingHandler は、ログファイルをある時点でローテーションさせるハンドラの基底クラスです。直接インスタンス化するためのクラスではありません。 RotatingFileHandlerTimedRotatingFileHandler を使うようにしてください。
  1. RotatingFileHandler のインスタンスは、ディスク上のファイルにエラーメッセージを送信します。最大ログファイルのサイズ指定とログファイルのローテーション機能をサポートしています。
  1. TimedRotatingFileHandler のインスタンスは、ディスク上のファイルにエラーメッセージを送信します。ログファイルを一定時間間隔ごとにローテーションします。
  1. handlers.SocketHandler のインスタンスは、 TCP/IP ソケットにエラーメッセージを送信します。
  1. DatagramHandler のインスタンスは、 UDP ソケットにエラーメッセージを送信します。
  1. SMTPHandler のインスタンスは、指定された電子メールアドレスにエラーメッセージを送信します。
  1. SysLogHandler のインスタンスは、 Unix syslog デーモンにエラーメッセージを送信します。遠隔マシン上の syslog デーモンに送信することもできます。
  1. NTEventLogHandler のインスタンスは、 Windows NT/2000/XP イベントログにエラーメッセージを送信します。
  1. MemoryHandler のインスタンスは、メモリ上のバッファにエラーメッセージを送信し、指定された条件でフラッシュされるようにします。
  1. HTTPHandler のインスタンスは、 GET または POST セマンティクスを使って HTTP サーバにエラーメッセージを送信します。
  1. WatchedFileHandler のインスタンスは、ログ記録を行うファイルを監視します。もしファイルが変更されれば、一旦ファイルを閉じた後ファイル名を使って再度開きます。このハンドラは Unix ライクなシステムでだけ有用です。 Windows では元にしている機構がサポートされていません。

StreamHandler および FileHandler クラスは、コア logging パッケージ内で定義されています。他のハンドラはサブモジュール logging.handlers で定義されています。 (サブモジュールにはもうひとつ logging.config があり、これは環境設定機能のためのものです。)

ログ記録されたメッセージは Formatter クラスのインスタンスを介し、表示用に書式化されます。これらのインスタンスは % 演算子と辞書を使うのに適した書式化文字列で初期化されます。

複数のメッセージの初期化をバッチ処理するために、 BufferingFormatter のインスタンスを使うことができます。書式化文字列 (バッチ処理で各メッセージに適用されます) に加えて、ヘッダ (header) およびトレイラ (trailer) 書式化文字列が用意されています。

ロガーレベル、ハンドラレベルの両方または片方に基づいたフィルタリングが十分でない場合、 (addFilter() メソッドを介して) Logger および Handler インスタンスに Filter のインスタンスを追加することができます。メッセージの処理を進める前に、ロガーとハンドラはともに、すべてのフィルタでメッセージの処理が許可されているか調べます。いずれかのフィルタが false 値を返した場合、メッセージの処理は行われません。

基本的な Filter 機能では、指定されたロガー名でフィルタを行えるようになっています。この機能が利用された場合、名前付けされたロガーとその下位にあるロガーに送られたメッセージがフィルタを通過できるようになり、その他のメッセージは捨てられます。

15.6.4. モジュールレベル関数

上で述べたクラスに加えて、いくつかのモジュールレベルの関数が存在します。

logging.getLogger([name])

指定された名前のロガーを返します。名前が指定されていない場合、ロガー階層のルート (root) にあるロガーを返します。 name を指定する場合には、通常は “a”, “a.b”, “a.b.c.d” といったドット区切りの階層的な名前にします。名前の付け方はログ機能を使う開発者次第です。

与えられた名前に対して、この関数はどの呼び出しでも同じロガーインスタンスを返します。したがって、ロガーインスタンスをアプリケーションの各部でやりとりする必要はありません。

logging.getLoggerClass()

標準の Logger クラスか、最後に setLoggerClass() に渡したクラスを返します。この関数は、新たなクラス定義の中で呼び出して、カスタマイズした Logger クラスのインストールが既に他のコードで適用したカスタマイズを取り消さないことを保証するために使われることがあります。例えば以下のようにします:

class MyLogger(logging.getLoggerClass()):
    # ... override behaviour here
logging.debug(msg[, *args[, **kwargs]])

レベル DEBUG のメッセージをルートロガーで記録します。 msg はメッセージの書式化文字列で、 argsmsg に文字列書式化演算子を使って取り込むための引数です。 (これは、書式化文字列の中でキーワードを使い、引数として単一の辞書を渡すことができる、ということを意味します。)

キーワード引数 kwargs からは 2 つのキーワードが調べられます。一つ目は exc_info で、この値の評価値が false でない場合、例外情報をログメッセージに追加します。 (sys.exc_info() の返す形式の) 例外情報を表すタプルが与えられていれば、それをメッセージに使います。それ以外の場合には、 sys.exc_info() を呼び出して例外情報を取得します。

もう一つのキーワード引数は extra で、当該ログイベント用に作られる LogRecoed の __dict__ にユーザー定義属性を加えるのに使われる辞書を渡すために用いられます。これらの属性は好きなように使えます。たとえば、ログメッセージの一部にすることもできます。以下の例を見てください。

FORMAT = "%(asctime)-15s %(clientip)s %(user)-8s %(message)s"
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logging.warning("Protocol problem: %s", "connection reset", extra=d)

出力はこのようになります:

2006-02-08 22:20:02,165 192.168.0.1 fbloggs  Protocol problem: connection reset

extra で渡される辞書のキーはロギングシステムで使われているものと衝突しないようにしなければなりません。 (どのキーがロギングシステムで使われているかについての詳細は Formatter のドキュメントを参照してください。)

これらの属性をログメッセージに使うことにしたなら、少し注意が必要です。上の例では、 ‘clientip’ と ‘user’ が LogRecord の属性辞書に含まれていることを期待した書式化文字列で Formatter がセットアップされています。もしこれらが欠けていると、書式化例外が発生してしまうためメッセージはログに残りません。したがってこの場合、常にこれらのキーを含む extra 辞書を渡す必要があります。

このようなことは煩わしいかもしれませんが、この機能は限定された場面で使われるように意図しているものなのです。たとえば同じコードがいくつものコンテキストで実行されるマルチスレッドのサーバで、興味のある条件が現れるのがそのコンテキストに依存している (上の例で言えば、リモートのクライアント IP アドレスや認証されたユーザ名など)、というような場合です。そういった場面では、それ用の Formatter が特定の Handler と共に使われるというのはよくあることです。

バージョン 2.5 で変更: extra が追加されました。

logging.info(msg[, *args[, **kwargs]])

レベル INFO のメッセージをルートロガーで記録します。引数は debug() と同じように解釈されます。

logging.warning(msg[, *args[, **kwargs]])

レベル WARNING のメッセージをルートロガーで記録します。引数は debug() と同じように解釈されます。

logging.error(msg[, *args[, **kwargs]])

レベル ERROR のメッセージをルートロガーで記録します。引数は debug() と同じように解釈されます。

logging.critical(msg[, *args[, **kwargs]])

レベル CRITICAL のメッセージをルートロガーで記録します。引数は debug() と同じように解釈されます。

logging.exception(msg[, *args])

レベル ERROR のメッセージをルートロガーで記録します。引数は debug() と同じように解釈されます。例外情報がログメッセージに追加されます。このメソッドは例外ハンドラからのみ呼び出されます。

logging.log(level, msg[, *args[, **kwargs]])

レベル level のメッセージをルートロガーで記録します。その他の引数は debug() と同じように解釈されます。

logging.disable(lvl)

すべてのロガーに対して、ロガー自体のレベルに優先するような上書きレベル lvl を与えます。アプリケーション全体にわたって一時的にログ出力を抑制する必要が生じた場合にはこの関数が有効です。その効果は、深刻度 lvl 以下のすべてのログ呼び出しを無効にすることです。そのためこの関数を値 INFO を伴って呼び出した場合、すべての INFO と DEBUG イベントは捨てられ、ロガーの実効レベルに従って優先度 WARNING 以上のものは処理されるでしょう。

logging.addLevelName(lvl, levelName)

内部的な辞書の中でレベル lvl をテキスト levelName に関連付けます。これは例えば Formatter でメッセージを書式化する際のように、数字のレベルをテキスト表現に対応付ける際に用いられます。この関数は自作のレベルを定義するために使うこともできます。使われるレベルに対する唯一の制限は、レベルは正の整数でなくてはならず、メッセージの深刻度が上がるに従ってレベルの数も上がらなくてはならないということです。

logging.getLevelName(lvl)

ログ記録レベル lvl のテキスト表現を返します。レベルが定義済みのレベル CRITICAL, ERROR, WARNING, INFO, DEBUG のいずれかである場合、対応する文字列が返されます。 addLevelName() を使ってレベルに名前を関連付けていた場合、 lvl に関連付けられた名前が返されます。定義済みのレベルに対応する数値を指定した場合、レベルに対応した文字列表現を返します。そうでない場合、文字列 “Level %s” % lvl を返します。

logging.makeLogRecord(attrdict)

属性が attrdict で定義された、新しい LogRecord インスタンスを生成して返します。この関数は、 pickle された LogRecord 属性の辞書をソケットを介して送信し、受信端で LogRecord インスタンスとして再構成する場合に便利です。

logging.basicConfig([**kwargs])

デフォルトの Formatter を持つ StreamHandler を生成してルートロガーに追加し、ロギングシステムの基本的な環境設定を行います。関数 debug(), info(), warning(), error(), critical() は、ルートロガーにハンドラが定義されていない場合に自動的に basicConfig() を呼び出します。

この関数はルートロガーに設定されたハンドラがあれば何もしません。

バージョン 2.4 で変更: 以前は basicConfig() はキーワード引数を取りませんでした。

以下のキーワード引数がサポートされます。

Format 説明
filename StreamHandler ではなく指定された名前で FileHandler が作られます
filemode filename が指定されているとき、ファイルモードを指定します (filemode が指定されない場合デフォルトは ‘a’ です)
format 指定された書式化文字列をハンドラで使います
datefmt 指定された日付/時刻の書式を使います
level ルートロガーのレベルを指定されたものにします
stream 指定されたストリームを StreamHandler の初期化に使います。この引数は ‘filename’ と同時には使えないことに注意してください。両方が指定されたときには ‘stream’ は無視されます
logging.shutdown()

ロギングシステムに対して、バッファのフラッシュを行い、すべてのハンドラを閉じることで順次シャットダウンを行うように告知します。この関数はアプリケーションの終了時に呼ばれるべきであり、また呼び出し以降はそれ以上ロギングシステムを使ってはなりません。

logging.setLoggerClass(klass)

ロギングシステムに対して、ロガーをインスタンス化する際にクラス klass を使うように指示します。指定するクラスは引数として名前だけをとるようなメソッド __init__() を定義していなければならず、 __init__() では Logger.__init__() を呼び出さなければなりません。典型的な利用法として、この関数は自作のロガーを必要とするようなアプリケーションにおいて、他のロガーがインスタンス化される前にインスタンス化されます。

参考

PEP 282 - A Logging System
本機能を Python 標準ライブラリに含めるよう記述している提案書。
この logging パッケージのオリジナル
オリジナルの logging パッケージ。このサイトにあるバージョンのパッケージは、標準で logging パッケージを含まない Python 1.5.2, 2.1.x, 2.2.x でも使用できます。

15.6.5. Logger オブジェクト

ロガーには以下のような属性とメソッドがあります。ロガーを直接インスタンス化することはできず、常にモジュール関数 logging.getLogger(name) を介してインスタンス化することに注意してください。

Logger.propagate

この値の評価結果が false になる場合、ログ記録しようとするメッセージはこのロガーに渡されず、また子ロガーから上位の (親の) ロガーのハンドラに渡されません。コンストラクタはこの属性を 1 に設定します。

Logger.setLevel(lvl)

このロガーの閾値を lvl に設定します。ログ記録しようとするメッセージで、 lvl よりも深刻でないものは無視されます。ロガーが生成された際、レベルは NOTSET (これによりすべてのメッセージについて、ロガーがルートロガーであれば処理される、そうでなくてロガーが非ルートロガーの場合には親ロガーに委譲させる) に設定されます。ルートロガーは WARNING レベルで生成されることに注意してください。

「親ロガーに委譲」という用語の意味は、もしロガーのレベルが NOTEST ならば、祖先ロガーの系列の中を NOTEST 以外のレベルの祖先を見つけるかルートに到達するまで辿っていく、ということです。

もし NOTEST 以外のレベルの祖先が見つかったなら、その祖先のレベルが探索を開始したロガーの実効レベルとして扱われ、ログイベントがどのように処理されるかを決めるのに使われます。

ルートに到達した場合、ルートのレベルが NOTEST ならばすべてのメッセージは処理されます。そうでなければルートのレベルが実効レベルとして使われます。

Logger.isEnabledFor(lvl)

深刻度が lvl のメッセージが、このロガーで処理されることになっているかどうかを示します。このメソッドはまず、 logging.disable(lvl) で設定されるモジュールレベルの深刻度レベルを調べ、次にロガーの実効レベルを getEffectiveLevel() で調べます。

Logger.getEffectiveLevel()

このロガーの実効レベルを示します。 NOTSET 以外の値が setLevel() で設定されていた場合、その値が返されます。そうでない場合、 NOTSET 以外の値が見つかるまでロガーの階層をルートロガーの方向に追跡します。見つかった場合、その値が返されます。

Logger.debug(msg[, *args[, **kwargs]])

レベル DEBUG のメッセージをこのロガーで記録します。 msg はメッセージの書式化文字列で、 argsmsg に文字列書式化演算子を使って取り込むための引数です。 (これは、書式化文字列の中でキーワードを使い、引数として単一の辞書を渡すことができる、ということを意味します。)

キーワード引数 kwargs からは 2 つのキーワードが調べられます。一つ目は exc_info で、この値の評価値が false でない場合、例外情報をログメッセージに追加します。 (sys.exc_info() の返す形式の) 例外情報を表すタプルが与えられていれば、それをメッセージに使います。それ以外の場合には、 sys.exc_info() を呼び出して例外情報を取得します。

もう一つのキーワード引数は extra で、当該ログイベント用に作られる LogRecoed の __dict__ にユーザー定義属性を加えるのに使われる辞書を渡すために用いられます。これらの属性は好きなように使えます。たとえば、ログメッセージの一部にすることもできます。以下の例を見てください。

FORMAT = "%(asctime)-15s %(clientip)s %(user)-8s %(message)s"
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logger = logging.getLogger("tcpserver")
logger.warning("Protocol problem: %s", "connection reset", extra=d)

出力はこのようになります:

2006-02-08 22:20:02,165 192.168.0.1 fbloggs  Protocol problem: connection reset

extra で渡される辞書のキーはロギングシステムで使われているものと衝突しないようにしなければなりません。 (どのキーがロギングシステムで使われているかについての詳細は Formatter のドキュメントを参照してください。)

これらの属性をログメッセージに使うことにしたなら、少し注意が必要です。上の例では、 ‘clientip’ と ‘user’ が LogRecord の属性辞書に含まれていることを期待した書式化文字列で Formatter がセットアップされています。もしこれらが欠けていると、書式化例外が発生してしまうためメッセージはログに残りません。したがってこの場合、常にこれらのキーを含む extra 辞書を渡す必要があります。

このようなことは煩わしいかもしれませんが、この機能は限定された場面で使われるように意図しているものなのです。たとえば同じコードがいくつものコンテキストで実行されるマルチスレッドのサーバで、興味のある条件が現れるのがそのコンテキストに依存している (上の例で言えば、リモートのクライアント IP アドレスや認証されたユーザ名など)、というような場合です。そういった場面では、それ用の Formatter が特定の Handler と共に使われるというのはよくあることです。

バージョン 2.5 で変更: extra が追加されました。

Logger.info(msg[, *args[, **kwargs]])

レベル INFO のメッセージをこのロガーで記録します。引数は debug() と同じように解釈されます。

Logger.warning(msg[, *args[, **kwargs]])

レベル WARNING のメッセージをこのロガーで記録します。引数は debug() と同じように解釈されます。

Logger.error(msg[, *args[, **kwargs]])

レベル ERROR のメッセージをこのロガーで記録します。引数は debug() と同じように解釈されます。

Logger.critical(msg[, *args[, **kwargs]])

レベル CRITICAL のメッセージをこのロガーで記録します。引数は debug() と同じように解釈されます。

Logger.log(lvl, msg[, *args[, **kwargs]])

整数で表したレベル lvl のメッセージをこのロガーで記録します。その他の引数は debug() と同じように解釈されます。

Logger.exception(msg[, *args])

レベル ERROR のメッセージをこのロガーで記録します。引数は debug() と同じように解釈されます。例外情報がログメッセージに追加されます。このメソッドは例外ハンドラからのみ呼び出されます。

Logger.addFilter(filt)

指定されたフィルタ filt をこのロガーに追加します。

Logger.removeFilter(filt)

指定されたフィルタ filt をこのロガーから取り除きます。

Logger.filter(record)

このロガーのフィルタをレコード (record) に適用し、レコードがフィルタを透過して処理されることになる場合には true を返します。

Logger.addHandler(hdlr)

指定されたハンドラ hdlr をこのロガーに追加します。

Logger.removeHandler(hdlr)

指定されたハンドラ hdlr をこのロガーから取り除きます。

Logger.findCaller()

呼び出し元のソースファイル名と行番号を調べます。ファイル名と行番号と関数名を 3 要素のタプルで返します。

バージョン 2.4 で変更: 関数名も加えられました。 以前のバージョンではファイル名と行番号を 2 要素のタプルで返していました。

Logger.handle(record)

レコードを、このロガーおよびその上位ロガー (ただし propagate の値が false になったところまで) に関連付けられているすべてのハンドラに渡して処理します。このメソッドは、ローカルで生成されたレコードだけでなく、ソケットから受信した unpickle されたレコードに対しても同様に用いられます。 filter() によって、ロガーレベルでのフィルタが適用されます。

Logger.makeRecord(name, lvl, fn, lno, msg, args, exc_info[, func, extra])

このメソッドは、特殊な LogRecord インスタンスを生成するためにサブクラスでオーバライドできるファクトリメソッドです。

バージョン 2.5 で変更: funcextra が追加されました。

15.6.6. 基本的な使い方

バージョン 2.4 で変更: 以前は basicConfig() はキーワード引数を取りませんでした。

logging パッケージには高い柔軟性があり、その設定にたじろぐこともあるでしょう。そこでこの節では、 logging パッケージを簡単に使う方法もあることを示します。

以下の最も単純な例では、コンソールにログを表示します。

import logging

logging.debug('A debug message')
logging.info('Some information')
logging.warning('A shot across the bows')

上のスクリプトを実行すると、以下のようなメッセージを目にするでしょう:

WARNING:root:A shot across the bows

ここではロガーを特定しなかったので、システムはルートロガーを使っています。デバッグメッセージや情報メッセージは表示されませんが、これはデフォルトのルートロガーが WARNING 以上の重要度を持つメッセージしか処理しないように設定されているからです。メッセージの書式もデフォルトの設定に従っています。出力先は sys.stderr で、これもデフォルトの設定です。重要度レベルやメッセージの形式、ログの出力先は、以下の例のように簡単に変更できます:

import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(levelname)s %(message)s',
                    filename='myapp.log',
                    filemode='w')
logging.debug('A debug message')
logging.info('Some information')
logging.warning('A shot across the bows')

ここでは、 basicConfig() メソッドを使って、以下のような出力例になる (そして myapp.log に書き込まれる) ように、デフォルト設定を変更しています。

2004-07-02 13:00:08,743 DEBUG A debug message
2004-07-02 13:00:08,743 INFO Some information
2004-07-02 13:00:08,743 WARNING A shot across the bows

今度は、重要度が DEBUG か、それ以上のメッセージが処理されました。メッセージの形式も変更され、出力はコンソールではなく特定のファイルに書き出されました。

出力の書式化には、通常の Python 文字列に対する初期化を使います - 文字列フォーマット操作 節を参照してください。書式化文字列は、以下の指定子 (specifier) を常に取ります。指定子の完全なリストについては Formatter のドキュメントを参照してください。

書式 説明
%(name)s ロガーの名前 (ログチャネル) の名前です。
%(levelname)s メッセージのログレベル ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL') です。
%(asctime)s LogRecord が生成された際の時刻を、人間が読み取れる形式にしたものです。デフォルトでは、 “2003-07-08 16:49:45,896” のような形式 (コンマの後ろはミリ秒) です。
%(message)s ログメッセージです。

以下のように、追加のキーワードパラメータ datefmt を渡すと日付や時刻の書式を変更できます。

import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(levelname)-8s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    filename='/temp/myapp.log',
                    filemode='w')
logging.debug('A debug message')
logging.info('Some information')
logging.warning('A shot across the bows')

出力は以下のようになります

Fri, 02 Jul 2004 13:06:18 DEBUG    A debug message
Fri, 02 Jul 2004 13:06:18 INFO     Some information
Fri, 02 Jul 2004 13:06:18 WARNING  A shot across the bows

日付を書式化する文字列は strftime() の要求に従います - time モジュールを参照してください。

コンソールやファイルではなく、別個に作成しておいたファイル風 (file-like) オブジェクトにログを出力したい場合には、 basicConfig()stream キーワード引数で渡します。 streamfilename の両方の引数を指定した場合、 stream は無視されるので注意してください。

状況に応じて変化する情報ももちろんログ出力できます。以下のように、単にメッセージを書式化文字列にして、その後ろに可変情報の引数を渡すだけです。

import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(levelname)-8s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    filename='/temp/myapp.log',
                    filemode='w')
logging.error('Pack my box with %d dozen %s', 5, 'liquor jugs')

出力は以下のようになります。

Wed, 21 Jul 2004 15:35:16 ERROR    Pack my box with 5 dozen liquor jugs

15.6.7. 複数の出力先にログを出力する

コンソールとファイルに、別々のメッセージ書式で、別々の状況に応じたログ出力を行わせたいとしましょう。例えば DEBUG よりも高いレベルのメッセージはファイルに記録し、 INFO 以上のレベルのメッセージはコンソールに出力したいという場合です。また、ファイルにはタイムスタンプを記録し、コンソールには出力しないとします。以下のようにすれば、こうした挙動を実現できます。

import logging

# set up logging to file - see previous section for more details
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
                    datefmt='%m-%d %H:%M',
                    filename='/temp/myapp.log',
                    filemode='w')
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# set a format which is simpler for console use
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
# tell the handler to use this format
console.setFormatter(formatter)
# add the handler to the root logger
logging.getLogger('').addHandler(console)

# Now, we can log to the root logger, or any other logger. First the root...
logging.info('Jackdaws love my big sphinx of quartz.')

# Now, define a couple of other loggers which might represent areas in your
# application:

logger1 = logging.getLogger('myapp.area1')
logger2 = logging.getLogger('myapp.area2')

logger1.debug('Quick zephyrs blow, vexing daft Jim.')
logger1.info('How quickly daft jumping zebras vex.')
logger2.warning('Jail zesty vixen who grabbed pay from quack.')
logger2.error('The five boxing wizards jump quickly.')

このスクリプトを実行すると、コンソールには以下のように表示されるでしょう。

root        : INFO     Jackdaws love my big sphinx of quartz.
myapp.area1 : INFO     How quickly daft jumping zebras vex.
myapp.area2 : WARNING  Jail zesty vixen who grabbed pay from quack.
myapp.area2 : ERROR    The five boxing wizards jump quickly.

そして、ファイルは以下のようになるはずです。

10-22 22:19 root         INFO     Jackdaws love my big sphinx of quartz.
10-22 22:19 myapp.area1  DEBUG    Quick zephyrs blow, vexing daft Jim.
10-22 22:19 myapp.area1  INFO     How quickly daft jumping zebras vex.
10-22 22:19 myapp.area2  WARNING  Jail zesty vixen who grabbed pay from quack.
10-22 22:19 myapp.area2  ERROR    The five boxing wizards jump quickly.

これを見て分かる通り、 DEBUG メッセージはファイルだけに出力され、その他のメッセージは両方に出力されます。

この例ではコンソールとファイルのハンドラだけを使っていますが、実際には任意の数のハンドラや組み合わせを使えます。

15.6.8. ログ記録中に発生する例外

logging パッケージは、ログを生成している間に起こる例外を飲み込むように設計されています。これは、ログ記録イベントを扱っている間に発生するエラー (ログ記録の設定ミス、ネットワークまたは他の同様のエラー) によってログ記録を使用するアプリケーションが早期に終了しないようにするためです。

SystemExitKeyboardInterrupt 例外は決して飲み込まれません。 Handler サブクラスの emit() メソッドの間に起こる他の例外は、 handleError() メソッドに渡されます。

HandlerhandleError() のデフォルト実装は、モジュールレベル変数 raiseExceptions が設定されているかどうかチェックします。設定されているなら、トレースバックが sys.stderr に出力されます。設定されていないなら、例外は飲み込まれます。

Note: raiseExceptions のデフォルト値は True です。これは、開発の間に起こるどんな例外についても通常は通知してほしいからです。実運用環境では raiseExceptionsFalse に設定することをお勧めします。

15.6.9. 文脈情報をログ記録出力に付加する

時にはログ記録出力にログ関数の呼び出し時に渡されたパラメータに加えて文脈情報を含めたいこともあるでしょう。たとえば、ネットワークアプリケーションで、クライアント固有の情報 (例: リモートクライアントの名前、 IP アドレス) もログ記録に残しておきたいと思ったとしましょう。 extra パラメータをこの目的に使うこともできますが、いつでもこの方法で情報を渡すのが便利なやり方とも限りません。また接続ごとに Logger インスタンスを生成する誘惑に駆られるかもしれませんが、生成した Logger インスタンスはガーベジコレクションで回収されないので、これは良いアイデアとは言えません。この例は現実的な問題ではないかもしれませんが、 Logger インスタンスの個数がアプリケーションの中でログ記録が行われるレベルの粒度に依存する場合、 Logger インスタンスの個数が事実上無制限にならないと、管理が難しくなります。

ログ記録イベントの情報と一緒に出力される文脈情報を渡す簡単な方法は、 LoggerAdapter を使うことです。このクラスは Logger のように見えるように設計されていて、 debug(), info(), warning(), error(), exception(), critical(), log() の各メソッドを呼び出せるようになっています。これらのメソッドは対応する Logger のメソッドと同じ引数を取るので、二つの型を取り替えて使うことができます。

LoggerAdapter のインスタンスを生成する際には、 Logger インスタンスと文脈情報を収めた辞書風 (dict-like) のオブジェクトを渡します。 LoggerAdapter のログ記録メソッドを呼び出すと、呼び出しをコンストラクタに渡された配下の Logger インスタンスに委譲し、その際文脈情報をその委譲された呼び出しに埋め込みます。 LoggerAdapter のコードから少し抜き出してみます。

def debug(self, msg, *args, **kwargs):
    """
    Delegate a debug call to the underlying logger, after adding
    contextual information from this adapter instance.
    """
    msg, kwargs = self.process(msg, kwargs)
    self.logger.debug(msg, *args, **kwargs)

LoggerAdapterprocess() メソッドが文脈情報をログ出力に加える舞台です。そこではログ記録呼び出しのメッセージとキーワード引数が渡され、加工された (可能性のある) それらの情報を配下のロガーへの呼び出しに渡し直します。このメソッドのデフォルト実装ではメッセージは元のままですが、キーワード引数にはコンストラクタに渡された辞書風オブジェクトを値として “extra” キーが挿入されます。もちろん、呼び出し時に “extra” キーワードを使った場合には何事もなかったかのように上書きされます。

“extra” を用いる利点は辞書風オブジェクトの中の値が LogRecord インスタンスの __dict__ にマージされることで、辞書風オブジェクトのキーを知っている Formatter を用意して文字列をカスタマイズするようにできることです。それ以外のメソッドが必要なとき、たとえば文脈情報をメッセージの前や後ろにつなげたい場合には、 LoggerAdapter から process() を望むようにオーバライドしたサブクラスを作ることが必要なだけです。次に挙げるのはこのクラスを使った例で、コンストラクタで使われる「辞書風」オブジェクトにどの振る舞いが必要なのかも示しています。

import logging

class ConnInfo:
    """
    An example class which shows how an arbitrary class can be used as
    the 'extra' context information repository passed to a LoggerAdapter.
    """

    def __getitem__(self, name):
        """
        To allow this instance to look like a dict.
        """
        from random import choice
        if name == "ip":
            result = choice(["127.0.0.1", "192.168.0.1"])
        elif name == "user":
            result = choice(["jim", "fred", "sheila"])
        else:
            result = self.__dict__.get(name, "?")
        return result

    def __iter__(self):
        """
        To allow iteration over keys, which will be merged into
        the LogRecord dict before formatting and output.
        """
        keys = ["ip", "user"]
        keys.extend(self.__dict__.keys())
        return keys.__iter__()

if __name__ == "__main__":
    from random import choice
    levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
    a1 = logging.LoggerAdapter(logging.getLogger("a.b.c"),
                               { "ip" : "123.231.231.123", "user" : "sheila" })
    logging.basicConfig(level=logging.DEBUG,
                        format="%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s")
    a1.debug("A debug message")
    a1.info("An info message with %s", "some parameters")
    a2 = logging.LoggerAdapter(logging.getLogger("d.e.f"), ConnInfo())
    for x in range(10):
        lvl = choice(levels)
        lvlname = logging.getLevelName(lvl)
        a2.log(lvl, "A message at %s level with %d %s", lvlname, 2, "parameters")

このスクリプトが実行されると、出力は以下のようになります。

2008-01-18 14:49:54,023 a.b.c DEBUG    IP: 123.231.231.123 User: sheila   A debug message
2008-01-18 14:49:54,023 a.b.c INFO     IP: 123.231.231.123 User: sheila   An info message with some parameters
2008-01-18 14:49:54,023 d.e.f CRITICAL IP: 192.168.0.1     User: jim      A message at CRITICAL level with 2 parameters
2008-01-18 14:49:54,033 d.e.f INFO     IP: 192.168.0.1     User: jim      A message at INFO level with 2 parameters
2008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: sheila   A message at WARNING level with 2 parameters
2008-01-18 14:49:54,033 d.e.f ERROR    IP: 127.0.0.1       User: fred     A message at ERROR level with 2 parameters
2008-01-18 14:49:54,033 d.e.f ERROR    IP: 127.0.0.1       User: sheila   A message at ERROR level with 2 parameters
2008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: sheila   A message at WARNING level with 2 parameters
2008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: jim      A message at WARNING level with 2 parameters
2008-01-18 14:49:54,033 d.e.f INFO     IP: 192.168.0.1     User: fred     A message at INFO level with 2 parameters
2008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: sheila   A message at WARNING level with 2 parameters
2008-01-18 14:49:54,033 d.e.f WARNING  IP: 127.0.0.1       User: jim      A message at WARNING level with 2 parameters

バージョン 2.6 で追加.

LoggerAdapter クラスは以前のバージョンにはありません。

15.6.10. 複数のプロセスからの単一ファイルへのログ記録

ログ記録はスレッドセーフであり、単一プロセスの複数のスレッドからの単一ファイルへのログ記録はサポート されています が、 複数プロセス からの単一ファイルへのログ記録はサポート されません 。なぜなら、複数のプロセスをまたいで単一のファイルへのアクセスを直列化する標準の方法が Python には存在しないからです。複数のプロセスから単一ファイルへログ記録しなければならないなら、最も良い方法は、すべてのプロセスが SocketHandler に対してログ記録を行い、独立したプロセスとしてソケットサーバを動かすことです。ソケットサーバはソケットから読み取ってファイルにログを書き出します。 (この機能を実行するために、既存のプロセスの 1 つのスレッドを割り当てることもできます) 以下の節では、このアプローチをさらに詳細に文書化しています。動作するソケット受信プログラムが含まれているので、アプリケーションに組み込むための出発点として使用できるでしょう。

multiprocessing モジュールを含む最近のバージョンの Python を使用しているなら、複数のプロセスからファイルへのアクセスを直列化するために multiprocessing モジュールの Lock クラスを使って独自のハンドラを書くことができます。既存の FileHandler とそのサブクラスは現在のところ multiprocessing を利用していませんが、将来は利用するようになるかもしれません。現在のところ multiprocessing モジュールが提供するロック機能はすべてのプラットホームで動作するわけではないことに注意してください。 (http://bugs.python.org/issue3770 参照)

15.6.11. ログイベントをネットワーク越しに送受信する

ログイベントをネットワーク越しに送信し、受信端でそれを処理したいとしましょう。 SocketHandler インスタンスを送信端のルートロガーに接続すれば、簡単に実現できます。

import logging, logging.handlers

rootLogger = logging.getLogger('')
rootLogger.setLevel(logging.DEBUG)
socketHandler = logging.handlers.SocketHandler('localhost',
                    logging.handlers.DEFAULT_TCP_LOGGING_PORT)
# don't bother with a formatter, since a socket handler sends the event as
# an unformatted pickle
rootLogger.addHandler(socketHandler)

# Now, we can log to the root logger, or any other logger. First the root...
logging.info('Jackdaws love my big sphinx of quartz.')

# Now, define a couple of other loggers which might represent areas in your
# application:

logger1 = logging.getLogger('myapp.area1')
logger2 = logging.getLogger('myapp.area2')

logger1.debug('Quick zephyrs blow, vexing daft Jim.')
logger1.info('How quickly daft jumping zebras vex.')
logger2.warning('Jail zesty vixen who grabbed pay from quack.')
logger2.error('The five boxing wizards jump quickly.')

受信端では、 SocketServer モジュールを使って受信プログラムを作成しておきます。簡単な実用プログラムを以下に示します。

import cPickle
import logging
import logging.handlers
import SocketServer
import struct


class LogRecordStreamHandler(SocketServer.StreamRequestHandler):
    """Handler for a streaming logging request.

    This basically logs the record using whatever logging policy is
    configured locally.
    """

    def handle(self):
        """
        Handle multiple requests - each expected to be a 4-byte length,
        followed by the LogRecord in pickle format. Logs the record
        according to whatever policy is configured locally.
        """
        while 1:
            chunk = self.connection.recv(4)
            if len(chunk) < 4:
                break
            slen = struct.unpack(">L", chunk)[0]
            chunk = self.connection.recv(slen)
            while len(chunk) < slen:
                chunk = chunk + self.connection.recv(slen - len(chunk))
            obj = self.unPickle(chunk)
            record = logging.makeLogRecord(obj)
            self.handleLogRecord(record)

    def unPickle(self, data):
        return cPickle.loads(data)

    def handleLogRecord(self, record):
        # if a name is specified, we use the named logger rather than the one
        # implied by the record.
        if self.server.logname is not None:
            name = self.server.logname
        else:
            name = record.name
        logger = logging.getLogger(name)
        # N.B. EVERY record gets logged. This is because Logger.handle
        # is normally called AFTER logger-level filtering. If you want
        # to do filtering, do it at the client end to save wasting
        # cycles and network bandwidth!
        logger.handle(record)

class LogRecordSocketReceiver(SocketServer.ThreadingTCPServer):
    """simple TCP socket-based logging receiver suitable for testing.
    """

    allow_reuse_address = 1

    def __init__(self, host='localhost',
                 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
                 handler=LogRecordStreamHandler):
        SocketServer.ThreadingTCPServer.__init__(self, (host, port), handler)
        self.abort = 0
        self.timeout = 1
        self.logname = None

    def serve_until_stopped(self):
        import select
        abort = 0
        while not abort:
            rd, wr, ex = select.select([self.socket.fileno()],
                                       [], [],
                                       self.timeout)
            if rd:
                self.handle_request()
            abort = self.abort

def main():
    logging.basicConfig(
        format="%(relativeCreated)5d %(name)-15s %(levelname)-8s %(message)s")
    tcpserver = LogRecordSocketReceiver()
    print "About to start TCP server..."
    tcpserver.serve_until_stopped()

if __name__ == "__main__":
    main()

先にサーバを起動しておき、次にクライアントを起動します。クライアント側では、コンソールには何も出力されません; サーバ側では以下のようなメッセージを目にするはずです。

About to start TCP server...
   59 root            INFO     Jackdaws love my big sphinx of quartz.
   59 myapp.area1     DEBUG    Quick zephyrs blow, vexing daft Jim.
   69 myapp.area1     INFO     How quickly daft jumping zebras vex.
   69 myapp.area2     WARNING  Jail zesty vixen who grabbed pay from quack.
   69 myapp.area2     ERROR    The five boxing wizards jump quickly.

特定のシナリオでは pickle にはいくつかのセキュリティ上の問題があることに注意してください。これが問題になる場合、 makePickle() メソッドをオーバーライドして代替手段を実装することで異なるシリアライズ手法を使用できます。代替シリアライズ手法を使うように上記のスクリプトを修正することもできます。

15.6.12. 任意のオブジェクトをメッセージに使用する

前の節とそこで挙げた例では、イベントを記録するときに渡されたメッセージが文字列であると仮定していました。しかし、これは唯一の可能性ではありません。メッセージとして任意のオブジェクトを渡すことができます。そして、ロギングシステムがそのオブジェクトを文字列表現に変換する必要があるとき、オブジェクトの __str__() メソッドが呼び出されます。実際、そうしたければ、文字列表現を計算することを完全に避けることができます - 例えば、 SocketHandler は、イベントを pickle してネットワーク上で送信することでログ出力します。

15.6.13. 最適化

message 引数の整形は、必要になるまで延期されます。しかしながら、ログ記録メソッドに渡す引数を計算するだけでもコストがかかる場合があります。ロガーが単にイベントを捨てるなら、その計算を避けたいと考えるかもしれません。どうするかを決定するために isEnabledFor() メソッドを呼ぶことができます。このメソッドは引数にレベルを取って、そのレベルの呼び出しに対して Logger がイベントを生成するなら true を返します。このようにコードを書くことができます:

if logger.isEnabledFor(logging.DEBUG):
    logger.debug("Message with %s, %s", expensive_func1(),
                                        expensive_func2())

このようにすると、ロガーの閾値が DEBUG より上に設定されている場合、 expensive_func1()expensive_func2() の呼び出しは行われません。

これ以外にも、どんなログ情報が集められるかについてより正確なコントロールを必要とする、特定のアプリケーションでできる最適化があります。これは、ログ記録の間の不要な処理を避けるためにできることのリストです:

不要な情報 それを避ける方法
呼び出しがどこから行われたかに関する情報 logging._srcfileNone にする
スレッド情報 logging.logThreads0 にする
プロセス情報 logging.logProcesses0 にする

また、コア logging モジュールが基本的なハンドラだけを含んでいることに注意してください。 logging.handlerslogging.config をインポートしなければ、余分なメモリを消費することはありません。

15.6.14. Handler オブジェクト

ハンドラは以下の属性とメソッドを持ちます。 Handler は直接インスタンス化されることはありません; このクラスはより便利なサブクラスの基底クラスとして働きます。しかしながら、サブクラスにおける __init__() メソッドでは、 Handler.__init__() を呼び出す必要があります。

Handler.__init__(level=NOTSET)

レベルを設定して、 Handler インスタンスを初期化します。空のリストを使ってフィルタを設定し、 I/O 機構へのアクセスを直列化するために (createLock() を使って) ロックを生成します。

Handler.createLock()

スレッドセーフでない背後の I/O 機能に対するアクセスを直列化するために用いられるスレッドロック (thread lock) を初期化します。

Handler.acquire()

createLock() で生成されたスレッドロックを獲得します。

Handler.release()

acquire() で獲得したスレッドロックを解放します。

Handler.setLevel(lvl)

このハンドラに対する閾値を lvl に設定します。ログ記録しようとするメッセージで、 lvl よりも深刻でないものは無視されます。ハンドラが生成された際、レベルは NOTSET (すべてのメッセージが処理される) に設定されます。

Handler.setFormatter(form)

このハンドラのフォーマッタを form に設定します。

Handler.addFilter(filt)

指定されたフィルタ filt をこのハンドラに追加します。

Handler.removeFilter(filt)

指定されたフィルタ filt をこのハンドラから除去します。

Handler.filter(record)

このハンドラのフィルタをレコードに適用し、レコードがフィルタを透過して処理されることになる場合には true 値を返します。

Handler.flush()

すべてのログ出力がフラッシュされるようにします。このクラスのバージョンではなにも行わず、サブクラスで実装するためのものです。

Handler.close()

ハンドラで使われているすべてのリソースの後始末を行います。このバージョンでは何も出力せず、 shutdown() が呼ばれたときに閉じられたハンドラを内部リストから削除します。サブクラスではオーバライドされた close() メソッドからこのメソッドが必ず呼ばれるようにしてください。

Handler.handle(record)

ハンドラに追加されたフィルタの条件に応じて、指定されたログレコードを出力します。このメソッドは I/O スレッドロックの獲得/解放を伴う実際のログ出力をラップします。

Handler.handleError(record)

このメソッドは emit() の呼び出し中に例外に遭遇した際にハンドラから呼び出されます。デフォルトではこのメソッドは何も行いません。すなわち、例外は暗黙のまま無視されます。ほとんどの場合、これがロギングシステムの望ましい動作です - というのは、ほとんどのユーザはロギングシステム自体のエラーは気にせず、むしろアプリケーションのエラーに興味があるからです。しかしながら、望むならこのメソッドを自作のハンドラと置き換えることもできます。 record には、例外発生時に処理されていたレコードが入ります。

Handler.format(record)

レコードに対する書式化を行います - フォーマッタが設定されていれば、それを使います。そうでない場合、モジュールにデフォルト指定されたフォーマッタを使います。

Handler.emit(record)

指定されたログ記録レコードを実際にログ記録する際のすべての処理を行います。このメソッドはサブクラスで実装されることを意図しており、そのためこのクラスのバージョンは NotImplementedError を送出します。

15.6.14.1. StreamHandler

logging コアパッケージに含まれる StreamHandler クラスは、ログ出力を sys.stdout, sys.stderr あるいは何らかのファイル風 (file-like) オブジェクト (あるいは、より正確に言えば write() および flush() メソッドをサポートする何らかのオブジェクト) といったストリームに送信します。

class logging.StreamHandler([strm])

StreamHandler クラスの新たなインスタンスを返します。 strm が指定された場合、インスタンスはログ出力先として指定されたストリームを使います; そうでない場合、 sys.stderr が使われます。

emit(record)

フォーマッタが指定されていれば、フォーマッタを使ってレコードを書式化します。次に、レコードがストリームに書き込まれ、末端に改行がつけられます。例外情報が存在する場合、 traceback.print_exception() を使って書式化され、ストリームの末尾につけられます。

flush()

ストリームの flush() メソッドを呼び出してバッファをフラッシュします。 close() メソッドは Handler から継承しているため何も出力を行わないので、 flush() 呼び出しを明示的に行う必要があるかもしれません。

15.6.14.2. FileHandler

logging コアパッケージに含まれる FileHandler クラスは、ログ出力をディスク上のファイルに送信します。このクラスは出力機能を StreamHandler から継承しています。

class logging.FileHandler(filename[, mode[, encoding[, delay]]])

FileHandler クラスの新たなインスタンスを返します。指定されたファイルが開かれ、ログ記録のためのストリームとして使われます。 mode が指定されなかった場合、 'a' が使われます。 encodingNone でない場合、その値はファイルを開くときのエンコーディングとして使われます。 delay が true ならば、ファイルを開くのは最初の emit() 呼び出しまで遅らせられます。デフォルトでは、ファイルは無制限に大きくなりつづけます。

バージョン 2.6 で変更: delay が追加されました。

close()

ファイルを閉じます。

emit(record)

record をファイルに出力します。

NullHandler の使い方について詳しくは ライブラリのためのログ記録方法の設定 を参照してください。

15.6.14.3. WatchedFileHandler

バージョン 2.6 で追加.

logging.handlers モジュールに含まれる WatchedFileHandler クラスは、ログ記録先のファイルを監視する FileHandler の一種です。ファイルが変更された場合、ファイルを閉じてからファイル名を使って開き直します。

ファイルはログファイルをローテーションさせる newsysloglogrotate のようなプログラムを使うことで変更されることがあります。このハンドラは、 Unix/Linux で使われることを意図していますが、ファイルが最後にログを出力してから変わったかどうかを監視します。 (ファイルはデバイスや inode が変わることで変わったと判断します。) ファイルが変わったら古いファイルのストリームは閉じて、現在のファイルを新しいストリームを取得するために開きます。

このハンドラを Windows で使うことは適切ではありません。というのも Windows では開いているログファイルを移動したり削除したりできないからです - logging はファイルを排他的ロックを掛けて開きます - そのためこうしたハンドラは必要ないのです。さらに、 Windows では ST_INO がサポートされていません (stat() はこの値として常に 0 を返します)。

class logging.WatchedFileHandler(filename[, mode[, encoding[, delay]]])

WatchedFileHandler クラスの新たなインスタンスを返します。指定されたファイルが開かれ、ログ記録のためのストリームとして使われます。 mode が指定されなかった場合、 "a" が使われます。 encodingNone でない場合、その値はファイルを開くときのエンコーディングとして使われます。 delay が true ならば、ファイルを開くのは最初の emit() 呼び出しまで遅らせられます。デフォルトでは、ファイルは無制限に大きくなりつづけます。

バージョン 2.6 で変更: delay が追加されました。

emit(record)

レコードをファイルに出力しますが、その前にファイルが変更されていないかチェックします。もし変更されていれば、レコードをファイルに出力する前に、既存のストリームはフラッシュして閉じられ、ファイルが再度開かれます。

15.6.14.4. RotatingFileHandler

logging.handlers モジュールに含まれる RotatingFileHandler クラスは、ディスク上のログファイルに対するローテーション処理をサポートします。

class logging.RotatingFileHandler(filename[, mode[, maxBytes[, backupCount[, encoding[, delay]]]]])

RotatingFileHandler クラスの新たなインスタンスを返します。指定されたファイルが開かれ、ログ記録のためのストリームとして使われます。 mode が指定されなかった場合、 "a" が使われます。 encodingNone でない場合、その値はファイルを開くときのエンコーディングとして使われます。 delay が true ならば、ファイルを開くのは最初の emit() 呼び出しまで遅らせられます。デフォルトでは、ファイルは無制限に大きくなりつづけます。

maxBytes および backupCount 値を指定することで、あらかじめ決められたサイズでファイルをロールオーバ (rollover) させることができます。指定サイズを超えそうになると、ファイルは閉じられ、暗黙のうちに新たなファイルが開かれます。ロールオーバは現在のログファイルの長さが maxBytes に近くなると常に起きます。 backupCount が非ゼロの場合、システムは古いログファイルをファイル名に ”.1”, ”.2” といった拡張子を追加して保存します。例えば、 backupCount が 5 で、基本のファイル名が app.log なら、 app.log, app.log.1, app.log.2 ... と続き、 app.log.5 までを得ることになります。ログの書き込み対象になるファイルは常に app.log です。このファイルが満杯になると、ファイルは閉じられ、 app.log.1 に名前が変更されます。 app.log.1, app.log.2 などが存在する場合、それらのファイルはそれぞれ app.log.2, app.log.3 といった具合に名前が変更されます。

バージョン 2.6 で変更: delay が追加されました。

doRollover()

上述のような方法でロールオーバを行います。

emit(record)

上述のようなロールオーバを行いながら、レコードをファイルに出力します。

15.6.14.5. TimedRotatingFileHandler

logging.handlers モジュールに含まれる TimedRotatingFileHandler クラスは、特定の時間間隔でのログローテーションをサポートしています。

class logging.TimedRotatingFileHandler(filename[, when[, interval[, backupCount[, encoding[, delay[, utc]]]]]])

TimedRotatingFileHandler クラスの新たなインスタンスを返します。 filename に指定したファイルを開き、ログ出力先のストリームとして使います。ログファイルのローテーション時には、ファイル名に拡張子 (suffix) をつけます。ログファイルのローテーションは when および interval の積に基づいて行います。

wheninterval の単位を指定するために使います。使える値は下表の通りです。大小文字の区別は行いません:

interval の単位
'S'
'M'
'H' 時間
'D'
'W' 曜日 (0=Monday)
'midnight' 深夜

古いログファイルを保存する際にロギングシステムは拡張子を付けます。拡張子は日付と時間に基づいて、 strftime の %Y-%m-%d_%H-%M-%S 形式かその前方の一部を、ロールオーバ間隔に依存した形で使います。

最初に次のロールオーバー時間を計算するとき (ハンドラが生成されるとき)、次のローテーションがいつ起こるかを計算するために、既存のログファイルの最終変更時刻または現在の時間が使用されます。

utc 引数が true の場合時刻は UTC になり、それ以外では現地時間が使われます。

backupCount がゼロでない場合、保存されるファイル数は高々 backupCount 個で、それ以上のファイルがロールオーバされる時に作られるならば、一番古いものが削除されます。削除のロジックは interval で決まるファイルを削除するので、 interval を変えると古いファイルが残ったままになることもあります。

delay が true なら、ファイルを開くのは emit() の最初の呼び出しまで延期されます。

バージョン 2.6 で変更: delay が追加されました。

doRollover()

上述の方法でロールオーバを行います。

emit(record)

setRollover() で説明した方法でロールオーバを行いながら、レコードをファイルに出力します。

15.6.14.6. SocketHandler

logging.handlers モジュールに含まれる SocketHandler クラスは、ログ出力をネットワークソケットに送信します。基底クラスでは TCP ソケットを用います。

class logging.SocketHandler(host, port)

アドレスが host および port で与えられた遠隔のマシンと通信するようにした SocketHandler クラスのインスタンスを生成して返します。

close()

ソケットを閉じます。

emit()

レコードの属性辞書を pickle して、バイナリ形式でソケットに書き込みます。ソケット操作でエラーが生じた場合、暗黙のうちにパケットは捨てられます。事前に接続が失われていた場合、接続を再度確立します。受信端でレコードを unpickle して LogRecord にするには、 makeLogRecord() 関数を使ってください。

handleError()

emit() の処理中に発生したエラーを処理します。よくある原因は接続の消失です。次のイベント発生時に再試行できるようにソケットを閉じます。

makeSocket()

サブクラスで必要なソケット形式を詳細に定義できるようにするためのファクトリメソッドです。デフォルトの実装では、 TCP ソケット (socket.SOCK_STREAM) を生成します。

makePickle(record)

レコードの属性辞書を pickle してから先頭に長さ情報を付けてバイナリ形式にして、ソケットを介して送信できるようにして返します。

pickle が完全に安全というわけではないことに注意してください。セキュリティに関して心配なら、より安全なメカニズムを実装するためにこのメソッドをオーバーライドすると良いでしょう。例えば、 HMAC を使って pickle に署名して、受け取る側ではそれを検証することができます。あるいはまた、受け取る側でグローバルなオブジェクトの unpickle を無効にすることができます。

send(packet)

pickle された文字列 packet をソケットに送信します。この関数はネットワークがビジーの時に発生する部分的送信に対応しています。

15.6.14.7. DatagramHandler

logging.handlers モジュールに含まれる DatagramHandler クラスは、 SocketHandler を継承しており、 UDP ソケットを介したログ記録メッセージの送信をサポートしています。

class logging.DatagramHandler(host, port)

アドレスが host および port で与えられた遠隔のマシンと通信するようにした DatagramHandler クラスのインスタンスを生成して返します。

emit()

レコードの属性辞書を pickle して、バイナリ形式でソケットに書き込みます。ソケット操作でエラーが生じた場合、暗黙のうちにパケットは捨てられます。事前に接続が失われていた場合、接続を再度確立します。受信端でレコードを unpickle して LogRecord にするには、 makeLogRecord() 関数を使ってください。

makeSocket()

ここで SocketHandler のファクトリメソッドをオーバライドして、 UDP ソケット (socket.SOCK_DGRAM) を生成しています。

send(s)

pickle された文字列をソケットに送信します。

15.6.14.8. SysLogHandler

logging.handlers モジュールに含まれる SysLogHandler クラスは、ログ記録メッセージを遠隔またはローカルの Unix syslog に送信する機能をサポートしています。

class logging.SysLogHandler([address[, facility]])

遠隔の Unix マシンと通信するための、 SysLogHandler クラスの新たなインスタンスを返します。マシンのアドレスは (host, port) のタプル形式をとる address で与えられます。 address が指定されない場合、 ('localhost', 514) が使われます。アドレスは UDP ソケットを使って開かれます。 (host, port) のタプル形式の代わりに文字列で “/dev/log” のように与えることもできます。この場合、 Unix ドメインソケットが syslog にメッセージを送るのに使われます。 facility が指定されない場合、 LOG_USER が使われます。

close()

遠隔ホストへのソケットを閉じます。

emit(record)

レコードは書式化された後、 syslog サーバに送信されます。例外情報が存在しても、サーバには 送信されません

encodePriority(facility, priority)

ファシリティおよび優先度を整数に符号化します。値は文字列でも整数でも渡すことができます。文字列が渡された場合、内部の対応付け辞書が使われ、整数に変換されます。

シンボリックな LOG_ 値は SysLogHandler で定義されています。これは sys/syslog.h ヘッダーファイルで定義された値を反映しています。

優先度

名前 (文字列) シンボル値
alert LOG_ALERT
crit or critical LOG_CRIT
debug LOG_DEBUG
emerg or panic LOG_EMERG
err or error LOG_ERR
info LOG_INFO
notice LOG_NOTICE
warn or warning LOG_WARNING

ファシリティ

名前 (文字列) シンボル値
auth LOG_AUTH
authpriv LOG_AUTHPRIV
cron LOG_CRON
daemon LOG_DAEMON
ftp LOG_FTP
kern LOG_KERN
lpr LOG_LPR
mail LOG_MAIL
news LOG_NEWS
syslog LOG_SYSLOG
user LOG_USER
uucp LOG_UUCP
local0 LOG_LOCAL0
local1 LOG_LOCAL1
local2 LOG_LOCAL2
local3 LOG_LOCAL3
local4 LOG_LOCAL4
local5 LOG_LOCAL5
local6 LOG_LOCAL6
local7 LOG_LOCAL7
mapPriority(levelname)

ログレベル名を syslog 優先度名に対応付けます。カスタムレベルを使用している場合や、デフォルトアルゴリズムがニーズに適していない場合には、このメソッドをオーバーライドする必要があるかもしれません。デフォルトアルゴリズムは、 DEBUG, INFO, WARNING, ERROR, CRITICAL を等価な syslog 名に、他のすべてのレベル名を “warning” に対応付けます。

15.6.14.9. NTEventLogHandler

logging.handlers モジュールに含まれる NTEventLogHandler クラスは、ログ記録メッセージをローカルな Windows NT, Windows 2000, または Windows XP のイベントログに送信する機能をサポートします。この機能を使えるようにするには、 Mark Hammond による Python 用 Win32 拡張パッケージをインストールする必要があります。

class logging.NTEventLogHandler(appname[, dllname[, logtype]])

NTEventLogHandler クラスの新たなインスタンスを返します。 appname はイベントログに表示する際のアプリケーション名を定義するために使われます。この名前を使って適切なレジストリエントリが生成されます。 dllname はログに保存するメッセージ定義の入った .dll または .exe ファイルへの完全修飾パス名を与えなければなりません (指定されない場合、 'win32service.pyd' が使われます - このライブラリは Win32 拡張とともにインストールされ、いくつかのプレースホルダとなるメッセージ定義を含んでいます)。これらのプレースホルダを利用すると、メッセージの発信源全体がログに記録されるため、イベントログは巨大になるので注意してください。 logtype'Application', 'System', 'Security' のいずれかであるか、デフォルトの 'Application' でなければなりません。

close()

現時点では、イベントログエントリの発信源としてのアプリケーション名をレジストリから除去することはできます。しかしこれを行うと、イベントログビューアで意図した通りにログが見えなくなるでしょう - これはイベントログが .dll 名を取得するためにレジストリにアクセスできなければならないからです。現在のバージョンではこの操作を行いません。

emit(record)

メッセージ ID、イベントカテゴリ、イベント型を決定し、メッセージを NT イベントログに記録します。

getEventCategory(record)

レコードに対するイベントカテゴリを返します。自作のカテゴリを指定したい場合、このメソッドをオーバライドしてください。このクラスのバージョンのメソッドは 0 を返します。

getEventType(record)

レコードのイベント型を返します。自作の型を指定したい場合、このメソッドをオーバライドしてください。このクラスのバージョンのメソッドは、ハンドラの typemap 属性を使って対応付けを行います。この属性は __init__() で初期化され、 DEBUG, INFO, WARNING, ERROR, CRITICAL が入っています。自作のレベルを使っているのなら、このメソッドをオーバライドするか、ハンドラの typemap 属性に適切な辞書を配置する必要があるでしょう。

getMessageID(record)

レコードのメッセージ ID を返します。自作のメッセージを使っているのなら、ロガーに渡される msg を書式化文字列ではなく ID にします。その上で、辞書参照を行ってメッセージ ID を得ます。このクラスのバージョンでは 1 を返します。この値は win32service.pyd における基本メッセージ ID です。

15.6.14.10. SMTPHandler

logging.handlers モジュールに含まれる SMTPHandler クラスは、 SMTP を介したログ記録メッセージの送信機能をサポートします。

class logging.SMTPHandler(mailhost, fromaddr, toaddrs, subject[, credentials])

新たな SMTPHandler クラスのインスタンスを返します。インスタンスは email の from および to アドレス行、および subject 行とともに初期化されます。 toaddrs は文字列からなるリストでなければなりません。非標準の SMTP ポートを指定するには、 mailhost 引数に (host, port) のタプル形式を指定します。文字列を使った場合、標準の SMTP ポートが使われます。もし SMTP サーバが認証を必要とするならば、 (username, password) のタプル形式を credentials 引数に指定することができます。

バージョン 2.6 で変更: credentials が追加されました。

emit(record)

レコードを書式化し、指定されたアドレスに送信します。

getSubject(record)

レコードに応じたサブジェクト行を指定したいなら、このメソッドをオーバライドしてください。

15.6.14.11. MemoryHandler

logging.handlers モジュールに含まれる MemoryHandler は、ログ記録するレコードをメモリ上にバッファリングし、定期的にその内容をターゲット (target) となるハンドラにフラッシュする機能をサポートしています。フラッシュ処理はバッファが一杯になるか、ある深刻度かそれ以上のレベルを持つイベントが観測された際に行われます。

MemoryHandler はより一般的な抽象クラス、 BufferingHandler のサブクラスです。この抽象クラスでは、ログ記録するレコードをメモリ上にバッファリングします。各レコードがバッファに追加される毎に、 shouldFlush() を呼び出してバッファをフラッシュすべきかどうか調べます。フラッシュする必要がある場合、 flush() が必要にして十分な処理を行うものと想定しています。

class logging.BufferingHandler(capacity)

指定した許容量のバッファでハンドラを初期化します。

emit(record)

レコードをバッファに追加します。 shouldFlush() が true を返す場合、バッファを処理するために flush() を呼び出します。

flush()

このメソッドをオーバライドして、自作のフラッシュ動作を実装することができます。このクラスのバージョンのメソッドでは、単にバッファの内容を削除して空にします。

shouldFlush(record)

バッファが許容量に達している場合に true を返します。このメソッドは自作のフラッシュ処理方針を実装するためにオーバライドすることができます。

class logging.MemoryHandler(capacity[, flushLevel[, target]])

MemoryHandler クラスの新たなインスタンスを返します。インスタンスはサイズ capacity のバッファとともに初期化されます。 flushLevel が指定されていない場合、 ERROR が使われます。 target が指定されていない場合、ハンドラが何らかの意味のある処理を行う前に setTarget() でターゲットを指定する必要があります。

close()

flush() を呼び出し、ターゲットを None に設定してバッファを消去します。

flush()

MemoryHandler の場合、フラッシュ処理は単に、バッファされたレコードをターゲットがあれば送信することを意味します。これと異なる動作を行いたい場合、オーバライドしてください。

setTarget(target)

ターゲットハンドラをこのハンドラに設定します。

shouldFlush(record)

バッファが一杯になっているか、 flushLevel またはそれ以上のレコードでないかを調べます。

15.6.14.12. HTTPHandler

logging.handlers モジュールに含まれる HTTPHandler クラスは、ログ記録メッセージを GET または POST セマンティクスを使って Web サーバに送信する機能をサポートしています。

class logging.HTTPHandler(host, url[, method])

HTTPHandler クラスの新たなインスタンスを返します。インスタンスはホストアドレス、 URL および HTTP メソッドとともに初期化されます。 host は特別なポートを使うことが必要な場合には、 host:port の形式で使うこともできます。 method が指定されなかった場合 GET が使われます。

emit(record)

レコードを URL エンコードされた辞書形式で Web サーバに送信します。

15.6.15. Formatter オブジェクト

Formatter は以下の属性とメソッドを持っています。 FormatterLogRecord を (通常は) 人間か外部のシステムで解釈できる文字列に変換する役割を担っています。基底クラスの Formatter では書式化文字列を指定することができます。何も指定されなかった場合、 '%(message)s' の値が使われます。

Formatter は LogRecord 属性の知識を利用できるような書式化文字列を用いて初期化することができます。例えば、上で言及したデフォルト値では、ユーザによるメッセージと引数はあらかじめ書式化されて、 LogRecordmessage 属性に入っていることを利用しています。この書式化文字列は、 Python 標準の % を使った変換文字列で構成されます。文字列整形に関する詳細は 文字列フォーマット操作 を参照してください。

現状では、 LogRecord の有用な属性は以下のようになっています:

Format 説明
%(name)s ロガー (ログ記録チャネル) の名前
%(levelno)s メッセージのログ記録レベルを表す数字 (DEBUG, INFO, WARNING, ERROR, CRITICAL)
%(levelname)s メッセージのログ記録レベルを表す文字列 (“DEBUG”, “INFO”, “WARNING”, “ERROR”, “CRITICAL”)
%(pathname)s ログ記録の呼び出しが行われたソースファイルの全パス名 (取得できる場合)
%(filename)s パス名中のファイル名部分
%(module)s モジュール名 (ファイル名の名前部分)
%(funcName)s ログ記録の呼び出しを含む関数の名前
%(lineno)d ログ記録の呼び出しが行われたソース行番号 (取得できる場合)
%(created)f LogRecord が生成された時刻 (time.time() の返した値)
%(relativeCreated)d LogRecord が生成された時刻の logging モジュールが読み込まれた時刻に対するミリ秒単位での相対的な値。
%(asctime)s LogRecord が生成された時刻を人間が読める書式で表したもの。デフォルトでは “2003-07-08 16:49:45,896” 形式 (コンマ以降の数字は時刻のミリ秒部分) です
%(msecs)d LogRecord が生成された時刻の、ミリ秒部分
%(thread)d スレッド ID (取得できる場合)
%(threadName)s スレッド名 (取得できる場合)
%(process)d プロセス ID (取得できる場合)
%(message)s レコードが発信された際に処理された msg % args の結果

バージョン 2.5 で変更: funcName が追加されました。

class logging.Formatter([fmt[, datefmt]])

Formatter クラスの新たなインスタンスを返します。インスタンスは全体としてのメッセージに対する書式化文字列と、メッセージの日付/時刻部分のための書式化文字列を伴って初期化されます。 fmt が指定されない場合、 '%(message)s' が使われます。 datefmt が指定されない場合、 ISO8601 日付書式が使われます。

format(record)

レコードの属性辞書が、文字列を書式化する演算で被演算子として使われます。書式化された結果の文字列を返します。辞書を書式化する前に、二つの準備段階を経ます。レコードの message 属性が msg % args を使って処理されます。書式化された文字列が '(asctime)' を含むなら、 formatTime() が呼び出され、イベントの発生時刻を書式化します。例外情報が存在する場合、 formatException() を使って書式化され、メッセージに追加されます。ここで注意していただきたいのは、書式化された例外情報は exc_text にキャッシュされるという点です。これが有用なのは例外情報がピックル化されて回線上を送ることができるからですが、しかし二つ以上の Formatter サブクラスで例外情報の書式化をカスタマイズしている場合には注意が必要になります。この場合、フォーマッタが書式化を終えるごとにキャッシュをクリアして、次のフォーマッタがキャッシュされた値を使わずに新鮮な状態で再計算するようにしなければならないことになります。

formatTime(record[, datefmt])

このメソッドは、フォーマッタが書式化された時間を利用したい際に、 format() から呼び出されます。このメソッドは特定の要求を提供するためにフォーマッタで上書きすることができますが、基本的な振る舞いは以下のようになります: datefmt (文字列) が指定された場合、レコードが生成された時刻を書式化するために time.strftime() で使われます。そうでない場合、 ISO8601 書式が使われます。結果の文字列が返されます。

formatException(exc_info)

指定された例外情報 (sys.exc_info() が返すような標準例外のタプル) を文字列として書式化します。デフォルトの実装は単に traceback.print_exception() を使います。結果の文字列が返されます。

15.6.16. Filter オブジェクト

フィルタは HandlerLogger によって利用され、レベルによる制御よりも洗練されたフィルタ処理を提供します。基底のフィルタクラスでは、ロガーの階層構造のある点よりも下層にあるイベントだけを通過させます。例えば、 “A.B” で初期化されたフィルタはロガー “A.B”, “A.B.C”, “A.B.C.D”, “A.B.D” などでログ記録されたイベントを通過させます。しかし、 “A.BB”, “B.A.B” などは通過させません。空の文字列で初期化された場合、すべてのイベントを通過させます。

class logging.Filter([name])

Filter クラスのインスタンスを返します。 name が指定されていれば、 name はロガーの名前を表します。指定されたロガーとその子ロガーのイベントがフィルタを通過できるようになります。 name が指定されなければ、すべてのイベントを通過させます。

filter(record)

指定されたレコードがログされているか?されていなければゼロを、されていればゼロでない値を返します。適切と判断されれば、このメソッドによってレコードはその場で修正されることがあります。

15.6.17. LogRecord オブジェクト

何かをログ記録する際には常に LogRecord インスタンスが生成されます。インスタンスにはログ記録されることになっているイベントに関係するすべての情報が入っています。インスタンスに渡される主要な情報は msg および args で、これらは msg % args を使って組み合わせられ、レコードのメッセージフィールドを生成します。レコードはまた、レコードがいつ生成されたか、ログ記録がソースコード行のどこで呼び出されたか、あるいはログ記録すべき何らかの例外情報といった情報も含んでいます。

class logging.LogRecord(name, lvl, pathname, lineno, msg, args, exc_info[, func])

関係のある情報とともに初期化された LogRecord のインスタンスを返します。 name はロガーの名前です; lvl は数字で表されたレベルです; pathname はログ記録呼び出しが見つかったソースファイルの絶対パス名です。 msg はユーザ定義のメッセージ (書式化文字列) です; args はタプルで、 msg と合わせて、ユーザメッセージを生成します; exc_info は例外情報のタプルで、 sys.exc_info() を呼び出して得られたもの (または、例外情報が取得できない場合には None) です。 func は logging 呼び出しを行った関数の名前です。指定されなければデフォルトは None です。

バージョン 2.5 で変更: func が追加されました。

getMessage()

ユーザが供給した引数をメッセージに交ぜた後、この LogRecord インスタンスへのメッセージを返します。

15.6.18. LoggerAdapter オブジェクト

バージョン 2.6 で追加.

LoggerAdapter インスタンスは文脈情報をログ記録呼び出しに渡すのを簡単にするために使われます。使い方の例は 文脈情報をログ記録出力に付加する を参照してください。

class logging.LoggerAdapter(logger, extra)

内部で使う Logger インスタンスと辞書風 (dict-like) オブジェクトで初期化した LoggerAdapter のインスタンスを返します。

process(meg, kwargs)

文脈情報を挿入するために、ログ記録呼び出しに渡されたメッセージおよび/またはキーワード引数に変更を加えます。ここでの実装は extra としてコンストラクタに渡されたオブジェクトを取り、 ‘extra’ キーを使って kwargs に加えます。返り値は (msg, kwargs) というタプルで、 (変更されているはずの) 渡された引数を含みます。

上のメソッドに加えて、 LoggerAdapterLogger にあるすべてのログ記録メソッド、すなわち debug(), info(), warning(), error(), exception(), critical(), log() をサポートします。これらのメソッドは対応する Logger のメソッドと同じ引数を取りますので、二つの型を取り替えて使うことができます。

15.6.19. スレッドセーフ性

logging モジュールは、クライアントで特殊な作業を必要としない限りスレッドセーフになっています。このスレッドセーフ性はスレッドロックによって達成されています; モジュールの共有データへのアクセスを直列化するためのロックが一つ存在し、各ハンドラでも背後にある I/O へのアクセスを直列化するためにロックを生成します。

signal モジュールを使用して非同期シグナルハンドラを実装している場合、そのようなハンドラからはログ記録を使用できないかもしれません。これは、 threading モジュールにおけるロック実装が常にリエントラントではなく、そのようなシグナルハンドラから呼び出すことができないからです。

15.6.20. 環境設定

15.6.20.1. 環境設定のための関数

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

logging.fileConfig(fname[, defaults])

ログ記録の環境設定をファイル名 fname の ConfigParser 形式ファイルから読み出します。この関数はアプリケーションから何度も呼び出すことができ、これによって、 (設定の選択と、選択された設定を読み出す機構をデベロッパが提供していれば) 複数の準備済みの設定からエンドユーザが選択するようにできます。 ConfigParser に渡すためのデフォルト値は defaults 引数で指定できます。

logging.listen([port])

指定されたポートでソケットサーバを開始し、新たな設定を待ち受けます。ポートが指定されなければ、モジュールのデフォルト設定である DEFAULT_LOGGING_CONFIG_PORT が使われます。ログ記録の環境設定は fileConfig() で処理できるようなファイルとして送信されます。 Thread インスタンスを返し、サーバを開始するために start() を呼び、適切な状況で join() を呼び出すことができます。サーバを停止するには stopListening() を呼んでください。

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

logging.stopListening()

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

15.6.20.2. 環境設定ファイルの書式

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

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

[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)” と解釈されます。

バージョン 2.6 で変更: ハンドラクラスのドット区切りモジュールおよびクラス名としての解決のサポートが追加された。

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

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

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

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

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

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

format エントリは全体を書式化する文字列で、 datefmt エントリは strftime() 互換の日付/時刻書式化文字列です。空文字列の場合、パッケージによって ISO8601 形式の日付/時刻に置き換えられ、日付書式化文字列 "%Y-%m-%d %H:%M:%S" を指定した場合とほとんど同じになります。 ISO8601 形式ではミリ秒も指定しており、上の書式化文字列の結果にカンマで区切って追加されます。 ISO8601 形式の時刻の例は 2003-01-23 00:29:50,411 です。

class エントリはオプションです。 class はフォーマッタのクラス名 (ドット区切りのモジュールとクラス名として) を示します。このオプションは Formatter のサブクラスをインスタンス化するのに有用です。 Formatter のサブクラスは例外トレースバックを展開された形式または圧縮された形式で表現することができます。

15.6.20.3. 設定サーバの例

ログ記録設定サーバを使うモジュールの例です。

import logging
import logging.config
import time
import os

# read initial config file
logging.config.fileConfig("logging.conf")

# create and start listener on port 9999
t = logging.config.listen(9999)
t.start()

logger = logging.getLogger("simpleExample")

try:
    # loop through logging calls to see the difference
    # new configurations make, until Ctrl+C is pressed
    while True:
        logger.debug("debug message")
        logger.info("info message")
        logger.warn("warn message")
        logger.error("error message")
        logger.critical("critical message")
        time.sleep(5)
except KeyboardInterrupt:
    # cleanup
    logging.config.stopListening()
    t.join()

そしてファイル名を受け取ってそのファイルをサーバに送るスクリプトですが、それに先だってバイナリエンコード長を新しいログ記録の設定として先に送っておきます:

#!/usr/bin/env python
import socket, sys, struct

data_to_send = open(sys.argv[1], "r").read()

HOST = 'localhost'
PORT = 9999
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "connecting..."
s.connect((HOST, PORT))
print "sending config..."
s.send(struct.pack(">L", len(data_to_send)))
s.send(data_to_send)
s.close()
print "complete"

15.6.21. さらなる例

15.6.22. 複数のハンドラおよびフォーマッタ

ロガーは通常の Python オブジェクトです。 addHandler() メソッドには追加されるハンドラの個数について最小値も最大値も定めていません。時にアプリケーションがすべての深刻度のすべてのメッセージをテキストファイルに記録しつつ、同時にエラーやそれ以上のものをコンソールに出力することが役に立ちます。これを実現する方法は、単に適切なハンドラを設定するだけです。アプリケーションコードの中のログ記録の呼び出しは変更されずに残ります。少し前に取り上げた単純なモジュール式の例を少し変えるとこうなります:

import logging

logger = logging.getLogger("simple_example")
logger.setLevel(logging.DEBUG)
# create file handler which logs even debug messages
fh = logging.FileHandler("spam.log")
fh.setLevel(logging.DEBUG)
# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
# create formatter and add it to the handlers
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
ch.setFormatter(formatter)
fh.setFormatter(formatter)
# add the handlers to logger
logger.addHandler(ch)
logger.addHandler(fh)

# "application" code
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")

「アプリケーション」のコードは複数のハンドラについて何も気にしていないことに注目してください。変更した箇所は新しい fh という名のハンドラを追加して設定したところがすべてです。

新しいハンドラを高い (もしくは低い) 深刻度に対するフィルタと共に生成できることは、アプリケーションを書いてテストを行うときとても助けになります。デバッグ用にたくさんの print 文を使う代わりに logger.debug を使いましょう。あとで消したりコメントアウトしたりしなければならない print 文と違って、 logger.debug 命令はソースコードの中にそのまま残しておいて再び必要になるまで休眠させておけます。その時必要になるのはただロガーおよび/またはハンドラの深刻度の設定をいじることだけです。

15.6.22.1. 複数のモジュールで logging を使う

上で述べたように logging.getLogger('someLogger') の複数回の呼び出しは同じロガーへの参照を返します。これは一つのモジュールの中からに限らず、同じ Python インタプリタプロセス上で動いている限りはモジュールをまたいでも当てはまります。同じオブジェクトへの参照という点でも正しいです。さらに、一つのモジュールの中で親ロガーを定義して設定し、別のモジュールで子ロガーを定義する (ただし設定はしない) ことが可能で、すべての子ロガーへの呼び出しは親にまで渡されます。まずはメインのモジュールです:

import logging
import auxiliary_module

# create logger with "spam_application"
logger = logging.getLogger("spam_application")
logger.setLevel(logging.DEBUG)
# create file handler which logs even debug messages
fh = logging.FileHandler("spam.log")
fh.setLevel(logging.DEBUG)
# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
# create formatter and add it to the handlers
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(fh)
logger.addHandler(ch)

logger.info("creating an instance of auxiliary_module.Auxiliary")
a = auxiliary_module.Auxiliary()
logger.info("created an instance of auxiliary_module.Auxiliary")
logger.info("calling auxiliary_module.Auxiliary.do_something")
a.do_something()
logger.info("finished auxiliary_module.Auxiliary.do_something")
logger.info("calling auxiliary_module.some_function()")
auxiliary_module.some_function()
logger.info("done with auxiliary_module.some_function()")

そして補助モジュール (auxiliary_module) がこちらです:

import logging

# create logger
module_logger = logging.getLogger("spam_application.auxiliary")

class Auxiliary:
    def __init__(self):
        self.logger = logging.getLogger("spam_application.auxiliary.Auxiliary")
        self.logger.info("creating an instance of Auxiliary")
    def do_something(self):
        self.logger.info("doing something")
        a = 1 + 1
        self.logger.info("done doing something")

def some_function():
    module_logger.info("received a call to \"some_function\"")

出力はこのようになります:

2005-03-23 23:47:11,663 - spam_application - INFO -
   creating an instance of auxiliary_module.Auxiliary
2005-03-23 23:47:11,665 - spam_application.auxiliary.Auxiliary - INFO -
   creating an instance of Auxiliary
2005-03-23 23:47:11,665 - spam_application - INFO -
   created an instance of auxiliary_module.Auxiliary
2005-03-23 23:47:11,668 - spam_application - INFO -
   calling auxiliary_module.Auxiliary.do_something
2005-03-23 23:47:11,668 - spam_application.auxiliary.Auxiliary - INFO -
   doing something
2005-03-23 23:47:11,669 - spam_application.auxiliary.Auxiliary - INFO -
   done doing something
2005-03-23 23:47:11,670 - spam_application - INFO -
   finished auxiliary_module.Auxiliary.do_something
2005-03-23 23:47:11,671 - spam_application - INFO -
   calling auxiliary_module.some_function()
2005-03-23 23:47:11,672 - spam_application.auxiliary - INFO -
   received a call to "some_function"
2005-03-23 23:47:11,673 - spam_application - INFO -
   done with auxiliary_module.some_function()