ロギング¶
Python プログラマは、素早く便利なデバッグツールとして、コード内で print()
をよく使います。ロギングフレームワークを使用するのはわずかな手間がかかるだけですが、それよりはるかにエレガントで柔軟です。デバッギングに便利であることに加えて、ロギングは、アプリケーションの状態と健全性に対して、よりよく構造化されたより多くの情報も提供してくれます。
概要¶
Django は、システムのロギングを実行するために、Python 組み込みの logging
モジュールを使用・拡張しています。このモジュールは、Python 自体のドキュメントで詳細に解説されています。このセクションでは、簡単な概要を説明します。
登場人物¶
Pythonのロギングは4つの部分で構成されます。
ロガー¶
ロガー は、ロギングシステムのエントリーポイントです。各ロガーは処理が記述されたメッセージの名前付けされたバケットです。
ロガーには ログレベル が設定されています。このログレベルはハンドリングされたログメッセージの深刻さを表しています。Pythonは下記のログレベルを定義しています。
DEBUG
: デバッグのための低レベルシステム情報INFO
: 一般的なシステム情報WARNING
: 重要度の小さい問題が発生したことを示す情報ERROR
: 大きな問題が発生したことを示す情報CRITICAL
: 重大な問題が発生したことを示す情報
ロガーに記載された各メッセージは ログレコード です。各ログレコードは、特定のメッセージの重要性を表す ログレベル を持ちます。ログレコードには、記録されたイベントを説明するメタデータを含むことができます。これには、スタックトレースやエラーコードと言った詳細を含めることができます。
ロガーにメッセージが渡されたとき、メッセージのログレベルはロガーのログレベルと比較されます。メッセージのログレベルがロガー自体のログレベルと同等以上の場合、メッセージは次の処理に進みます。それ以外の場合、メッセージは無視されます。
ロガーによってメッセージの処理が必要だと判断された場合、メッセージは ハンドラ に渡されます。
ハンドラ¶
ハンドラ は、ロガー上で書くメッセージに何をするかを決定するための原動力です。ここで、メッセージを表示する、ファイルに書き込む、ネットワークソケットに送信する、といった特定のロギング動作を記述します。
ロガーと同様に、ハンドラもログレベルを持ちます。ログレコードのログレベルがハンドラのレベルに満たない場合、ハンドラはメッセージを無視します。
ロガーには複数のハンドラを設定でき、また各ハンドラは異なるログレベルを持つことができます。この方法により、メッセージの重要性に応じて通知方法を変えることができます。たとえば、ERROR
と CRITICAL
メッセージを呼び出しサービスに転送するハンドラを設定する一方で、後で分析するために (ERROR
や CRITICAL
も含む) すべてのメッセージをファイルに記録する 2 つめのハンドラを設定できます。
フィルタ¶
フィルタ は、どのログレコードがロガーからハンドラへ渡されるかについて、追加的なコントロールを提供します。
デフォルトでは、ログレベル要件を満たしたすべてのログメッセージが処理されます。しかし、フィルタを使用することで、ロギングのプロセスに追加的な要件を設定できます。たとえば、特定のソースからの ERROR
のみを処理するようにフィルタを設定することができます。
フィルタは、処理前にログレコードを修正するためにも使えます。たとえば、特定の条件を満たした場合に ERROR
ログレコードを WARNING
レコードに格下げさせるようなフィルタを設定できます。
フィルタは、ロガーもしくはハンドラに対して設定できます; 複数のフィルタを使って複数のフィルタ動作を連鎖させることも可能です。
フォーマッタ¶
最終的には、ログレコードはテキストとして表現される必要があります。 フォーマッタ はこのテキストの書式を定義します。フォーマッタは、通常は Python の文字列フォーマットで、LogRecord 属性 を含みます; しかし、特定の書式設定を行うような独自のフォーマッタを記述することもできます。
セキュリティへの影響¶
ロギングシステムは潜在的にセンシティブな情報を取り扱うことになります。たとえば、ログレコードにはウェブリクエストやスタックトレースに関する情報が含まれることがありますが、ロガーで収集されたデータの一部にはセキュリティへの影響もあるかもしれません。あなたは次のことを理解しておく必要があります。
- どの情報を収集するか
- 最終的にその情報はどこに保存されることになるか
- その情報の転送はどのように行われるか
- その情報にアクセスする可能性があるのは誰か
センシティブな情報の収集をコントロールするために、特定のセンシティブな情報をエラーレポートからフィルタリングするように明示的に指定できます。その方法については エラーレポートをフィルタリングする を参照してください。
AdminEmailHandler
¶
組み込みの AdminEmailHandler
はセキュリティの文脈で言及に値します。もし include_html
オプションが有効なら、送信するメールには、スタックの各レベルのローカル変数の名前と値、そして Django の設定値(言い換えれば、 DEBUG
が True
のときに Web ページで公開されるのと同じレベルの詳細)が完全なトレースバックとして含まれます。
通常、このような潜在的にセンシティブな情報を電子メールで送信するのは良くないとされています。その代わりに、詳細なログを送信できる多くのサードパーティ・サービスを利用することで、完全なトレースバックによる豊富な情報、通知先や情報へのアクセス権の明確な管理など、複数の利点が得られます。
ロギングを設定する¶
Python のロギングライブラリは、実用的なインターフェースから設定ファイルまで、ロギング設定のテクニックを提供しています。デフォルトでは、Django は dictConfig フォーマット を使用します。
ロギングを設定するためには、LOGGING
を使用してロギング設定の辞書を定義します。この設定には、ロギングセットアップで使いたいロガー、ハンドラ、フィルタ、フォーマッタを記述し、それらのコンポーネントで使用したいログレベルとその他のプロパティを記述します。
デフォルトでは、LOGGING
設定は、以下のスキームを使って Django のデフォルトロギング設定 に統合されています。
LOGGING
の disable_existing_loggers
キーの値を True
にすると、全てのデフォルトの設定が無効になります(キーが存在しない場合は dictConfig
のデフォルトになります)。このため、 'disable_existing_loggers': True
を使う場合は注意してください。 True
を設定する必要は殆どないでしょう。 disable_existing_loggers
を False
に設定して、デフォルトのロガーの一部、または全てを定義しなおすこともできます。あるいは、 LOGGING_CONFIG
を None
に設定して、 ロギングの設定を自分で行うこと も出来ます。
ロギングは、一般的な Django の setup()
関数の一部として設定されます。したがって、ロガーが常にプロジェクトコード内で使用準備ができていることが保証されています。
例¶
dictConfig フォーマット に関する完全なドキュメントが、ロギング設定ディクショナリの最高の教材です。とはいえ、どんなことが可能なのか知ってもらうため、以下にいくつかの例を示します。
最初に、全てのログメッセージをコンソールに出力するための最低限の設定がこちらです。
import os
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"console": {
"class": "logging.StreamHandler",
},
},
"root": {
"handlers": ["console"],
"level": "WARNING",
},
}
この設定では、 root
という名前の親のロガーがあり、 WARNING
レベル以上のメッセージを受け取ったら、 console ハンドラに送るようになっています。 level を INFO
または DEBUG
にすると、より詳細なメッセージも表示されるようになります。この設定は開発中に役に立ちます。
次に、より詳細なロギングを追加します。次の例は、ロギングシステムが django という名前のロガーからより多くのメッセージを出力するようにする方法を示しています。
import os
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"console": {
"class": "logging.StreamHandler",
},
},
"root": {
"handlers": ["console"],
"level": "WARNING",
},
"loggers": {
"django": {
"handlers": ["console"],
"level": os.getenv("DJANGO_LOG_LEVEL", "INFO"),
"propagate": False,
},
},
}
デフォルトでは、この設定は、INFO
以上のメッセージを django
ロガーからコンソールに送信します。このレベルは、デフォルト設定ではログレコードを表示するのが DEBUG=True
のときだけであること以外、Django のデフォルトのロギング設定と同じです。Django はそのような INFO
レベルのログはそこまで多くログ出力しません。しかし、この設定では、環境変数 DJANGO_LOG_LEVEL=DEBUG
を設定することで、Django のすべての debug ログを表示できます。これにはすべてのデータベースクエリが含まれるため、非常に冗長です。
必ずしもコンソールに出力する必要はありません。django という名前のロガーをファイルに書き込むには、次のように設定します。
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"file": {
"level": "DEBUG",
"class": "logging.FileHandler",
"filename": "/path/to/django/debug.log",
},
},
"loggers": {
"django": {
"handlers": ["file"],
"level": "DEBUG",
"propagate": True,
},
},
}
この例を使用するときは、'filename'
のパスを Django アプリケーションを実行しているユーザーが書き込み可能な場所に変更してください。
最後に、かなり複雑なロギングの設定の例です。
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
"style": "{",
},
"simple": {
"format": "{levelname} {message}",
"style": "{",
},
},
"filters": {
"special": {
"()": "project.logging.SpecialFilter",
"foo": "bar",
},
"require_debug_true": {
"()": "django.utils.log.RequireDebugTrue",
},
},
"handlers": {
"console": {
"level": "INFO",
"filters": ["require_debug_true"],
"class": "logging.StreamHandler",
"formatter": "simple",
},
"mail_admins": {
"level": "ERROR",
"class": "django.utils.log.AdminEmailHandler",
"filters": ["special"],
},
},
"loggers": {
"django": {
"handlers": ["console"],
"propagate": True,
},
"django.request": {
"handlers": ["mail_admins"],
"level": "ERROR",
"propagate": False,
},
"myproject.custom": {
"handlers": ["console", "mail_admins"],
"level": "INFO",
"filters": ["special"],
},
},
}
このロギング設定は、以下のことを行います:
設定が「dictConfig バージョン 1」形式であることを明示します。 現在、これが唯一の dictConfig 形式のバージョンです。
2 つのフォーマッタを定義します:
simple
は、ログレベル名 (例:DEBUG
) とログメッセージを出力します。この
format
文字列は標準の Python 文字列フォーマットで、各ログ行で出力される詳細を定義します。出力可能な詳細の全リストは Formatter Objects で確認できます。verbose
は、ログレベルの名称とログメッセージに加えて、時刻、プロセス、スレッド、ログメッセージを生成したモジュールを出力します。
2 つのフィルタを定義します:
project.logging.SpecialFilter
は、エイリアスspecial
を使っています。もしこのフィルタが追加の引数を必要とする場合、フィルタ設定の辞書に追加のキーとして提供できます。今回の場合、SpecialFilter
をインスタンス化するときに、引数foo
にbar
の値が与えられます。django.utils.log.RequireDebugTrue
は、DEBUG
がTrue
のときにレコードの処理を進めます。
2 つのハンドラを定義します:
console
は、すべてのINFO
以上のメッセージをsys.stderr
に表示 (print) するStreamHandler
クラスです。このハンドラはsimple
出力フォーマットを使用します。mail_admins
は、ERROR
(またはそれより上) のすべてのメッセージをサイトのADMINS
にメール送信するAdminEmailHandler
クラスです。このハンドラはspecial
フィルタを使用します。
3 つのロガーを設定します:
django
は、すべてのメッセージをconsole
ハンドラに渡します。django.request
は、すべてのERROR
メッセージをmail_admins
ハンドラに渡します。加えて、このロガーはメッセージを親に 伝えない よう設定されています。これは、django.request
内に記述されたログメッセージはdjango
ロガーで処理されないことを意味します。myproject.custom
はINFO
もしくはそれ以上のすべてのメッセージを渡し、special
フィルタを 2 つのハンドラに引き渡します --console
とmail_admins
です。これは、INFO
以上のレベルのメッセージがコンソールで表示され、さらにERROR
とCRITICAL
のメッセージはメールでも出力されることを意味します。
カスタムのロギング設定¶
ロガーの設定に Python の dictConfig フォーマットを使用したくない場合は、あなた自身の設定スキームを定義できます。
LOGGING_CONFIG
設定は、Django のロガーを設定するために使われる呼び出し可能関数を定義します。デフォルトでは、Python の logging.config.dictConfig()
関数を指します。しかし、異なる設定処理を使いたい場合は、他の引数を1つ取る任意の呼び出し可能関数を使用できます。LOGGING
のコンテンツは、ロギング設定時に引数の値として提供されます。
ロギング設定を無効化する¶
ロギングを全く設定したくない (あるいは、自分のやり方でロギング手動で設定したい) なら、 LOGGING_CONFIG
を None
に設定することもできます。これにより、Django のデフォルトのロギング設定 を無効化できます。
LOGGING_CONFIG
を None
に設定すると、自動でロギングの設定が行われなくなるだけです。ロギング自体が使えなくなるわけではありません。自動でのロギング設定を無効化しても、Django はロギングのメソッドを呼び、もしログ設定がされていれば、そこに出力されます。
Django の自動でのロギング設定を無効化し、手動で設定する場合はこのようにします。
LOGGING_CONFIG = None
import logging.config
logging.config.dictConfig(...)
デフォルトの設定処理は、設定が完全にロードされた後にのみ LOGGING_CONFIG
を呼ばないことに注意してください。対照的に、設定ファイル内で手動で設定するロギングは、ロギング設定を直ちにロードします。そのため、ロギング設定は、必ずロギングが依存しているすべての設定の 後に 書く必要があります。