ロギング

Python プログラマは、素早く便利なデバッグツールとして、コード内で print() をよく使います。ロギングフレームワークを使用するのはわずかな手間がかかるだけですが、それよりはるかにエレガントで柔軟です。デバッギングに便利であることに加えて、ロギングは、アプリケーションの状態と健全性に対して、よりよく構造化されたより多くの情報も提供してくれます。

概要

Django は、システムのロギングを実行するために、Python 組み込みの logging モジュールを使用・拡張しています。このモジュールは、Python 自体のドキュメントで詳細に解説されています。このセクションでは、簡単な概要を説明します。

登場人物

Pythonのロギングは4つの部分で構成されます。

ロガー

ロガー は、ロギングシステムのエントリーポイントです。各ロガーは処理が記述されたメッセージの名前付けされたバケットです。

ロガーには ログレベル が設定されています。このログレベルはハンドリングされたログメッセージの深刻さを表しています。Pythonは下記のログレベルを定義しています。

  • DEBUG: デバッグのための低レベルシステム情報

  • INFO: 一般的なシステム情報

  • WARNING: 重要度の小さい問題が発生したことを示す情報

  • ERROR: 大きな問題が発生したことを示す情報

  • CRITICAL: 重大な問題が発生したことを示す情報

ロガーに記載された各メッセージは ログレコード です。各ログレコードは、特定のメッセージの重要性を表す ログレベル を持ちます。ログレコードには、記録されたイベントを説明するメタデータを含むことができます。これには、スタックトレースやエラーコードと言った詳細を含めることができます。

ロガーにメッセージが渡されたとき、メッセージのログレベルはロガーのログレベルと比較されます。メッセージのログレベルがロガー自体のログレベルと同等以上の場合、メッセージは次の処理に進みます。それ以外の場合、メッセージは無視されます。

ロガーによってメッセージの処理が必要だと判断された場合、メッセージは ハンドラ に渡されます。

ハンドラ

ハンドラ は、ロガー上で書くメッセージに何をするかを決定するための原動力です。ここで、メッセージを表示する、ファイルに書き込む、ネットワークソケットに送信する、といった特定のロギング動作を記述します。

ロガーと同様に、ハンドラもログレベルを持ちます。ログレコードのログレベルがハンドラのレベルに満たない場合、ハンドラはメッセージを無視します。

ロガーには複数のハンドラを設定でき、また各ハンドラは異なるログレベルを持つことができます。この方法により、メッセージの重要性に応じて通知方法を変えることができます。たとえば、ERRORCRITICAL メッセージを呼び出しサービスに転送するハンドラを設定する一方で、後で分析するために (ERRORCRITICAL も含む) すべてのメッセージをファイルに記録する 2 つめのハンドラを設定できます。

フィルタ

フィルタ は、どのログレコードがロガーからハンドラへ渡されるかについて、追加的なコントロールを提供します。

デフォルトでは、ログレベル要件を満たしたすべてのログメッセージが処理されます。しかし、フィルタを使用することで、ロギングのプロセスに追加的な要件を設定できます。たとえば、特定のソースからの ERROR のみを処理するようにフィルタを設定することができます。

フィルタは、処理前にログレコードを修正するためにも使えます。たとえば、特定の条件を満たした場合に ERROR ログレコードを WARNING レコードに格下げさせるようなフィルタを設定できます。

フィルタは、ロガーもしくはハンドラに対して設定できます; 複数のフィルタを使って複数のフィルタ動作を連鎖させることも可能です。

フォーマッタ

最終的には、ログレコードはテキストとして表現される必要があります。 フォーマッタ はこのテキストの書式を定義します。フォーマッタは、通常は Python の文字列フォーマットで、LogRecord 属性 を含みます; しかし、特定の書式設定を行うような独自のフォーマッタを記述することもできます。

セキュリティへの影響

ロギングシステムは潜在的にセンシティブな情報を取り扱うことになります。たとえば、ログレコードにはウェブリクエストやスタックトレースに関する情報が含まれることがありますが、ロガーで収集されたデータの一部にはセキュリティへの影響もあるかもしれません。あなたは次のことを理解しておく必要があります。

  • どの情報を収集するか

  • 最終的にその情報はどこに保存されることになるか

  • その情報の転送はどのように行われるか

  • その情報にアクセスする可能性があるのは誰か

センシティブな情報の収集をコントロールするために、特定のセンシティブな情報をエラーレポートからフィルタリングするように明示的に指定できます。その方法については エラーレポートをフィルタリングする を参照してください。

AdminEmailHandler

組み込みの AdminEmailHandler はセキュリティの文脈で言及に値します。もし include_html オプションが有効なら、送信するメールには、スタックの各レベルのローカル変数の名前と値、そして Django の設定値(言い換えれば、 DEBUGTrue のときに Web ページで公開されるのと同じレベルの詳細)が完全なトレースバックとして含まれます。

通常、このような潜在的にセンシティブな情報を電子メールで送信するのは良くないとされています。その代わりに、詳細なログを送信できる多くのサードパーティ・サービスを利用することで、完全なトレースバックによる豊富な情報、通知先や情報へのアクセス権の明確な管理など、複数の利点が得られます。

ロギングを設定する

Python のロギングライブラリは、実用的なインターフェースから設定ファイルまで、ロギング設定のテクニックを提供しています。デフォルトでは、Django は dictConfig フォーマット を使用します。

ロギングを設定するためには、LOGGING を使用してロギング設定の辞書を定義します。この設定には、ロギングセットアップで使いたいロガー、ハンドラ、フィルタ、フォーマッタを記述し、それらのコンポーネントで使用したいログレベルとその他のプロパティを記述します。

デフォルトでは、LOGGING 設定は、以下のスキームを使って Django のデフォルトロギング設定 に統合されています。

LOGGINGdisable_existing_loggers キーの値を True にすると、全てのデフォルトの設定が無効になります(キーが存在しない場合は dictConfig のデフォルトになります)。このため、 'disable_existing_loggers': True を使う場合は注意してください。 True を設定する必要は殆どないでしょう。 disable_existing_loggersFalse に設定して、デフォルトのロガーの一部、または全てを定義しなおすこともできます。あるいは、 LOGGING_CONFIGNone に設定して、 ロギングの設定を自分で行うこと も出来ます。

ロギングは、一般的な Django の setup() 関数の一部として設定されます。したがって、ロガーが常にプロジェクトコード内で使用準備ができていることが保証されています。

dictConfig フォーマット に関する完全なドキュメントが、ロギング設定ディクショナリの最高の教材です。とはいえ、どんなことが可能なのか知ってもらうため、以下にいくつかの例を示します。

最初に、全てのログメッセージをコンソールに出力するための最低限の設定がこちらです。

settings.py
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 という名前のロガーからより多くのメッセージを出力するようにする方法を示しています。

settings.py
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 という名前のロガーをファイルに書き込むには、次のように設定します。

settings.py
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 アプリケーションを実行しているユーザーが書き込み可能な場所に変更してください。

最後に、かなり複雑なロギングの設定の例です。

settings.py
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 をインスタンス化するときに、引数 foobar の値が与えられます。

    • django.utils.log.RequireDebugTrue は、DEBUGTrue のときにレコードの処理を進めます。

  • 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.customINFO もしくはそれ以上のすべてのメッセージを渡し、special フィルタを 2 つのハンドラに引き渡します -- consolemail_admins です。これは、INFO 以上のレベルのメッセージがコンソールで表示され、さらに ERRORCRITICAL のメッセージはメールでも出力されることを意味します。

カスタムのロギング設定

ロガーの設定に Python の dictConfig フォーマットを使用したくない場合は、あなた自身の設定スキームを定義できます。

LOGGING_CONFIG 設定は、Django のロガーを設定するために使われる呼び出し可能関数を定義します。デフォルトでは、Python の logging.config.dictConfig() 関数を指します。しかし、異なる設定処理を使いたい場合は、他の引数を1つ取る任意の呼び出し可能関数を使用できます。LOGGING のコンテンツは、ロギング設定時に引数の値として提供されます。

ロギング設定を無効化する

ロギングを全く設定したくない (あるいは、自分のやり方でロギング手動で設定したい) なら、 LOGGING_CONFIGNone に設定することもできます。これにより、Django のデフォルトのロギング設定 を無効化できます。

LOGGING_CONFIGNone に設定すると、自動でロギングの設定が行われなくなるだけです。ロギング自体が使えなくなるわけではありません。自動でのロギング設定を無効化しても、Django はロギングのメソッドを呼び、もしログ設定がされていれば、そこに出力されます。

Django の自動でのロギング設定を無効化し、手動で設定する場合はこのようにします。

settings.py
LOGGING_CONFIG = None

import logging.config

logging.config.dictConfig(...)

デフォルトの設定処理は、設定が完全にロードされた後にのみ LOGGING_CONFIG を呼ばないことに注意してください。対照的に、設定ファイル内で手動で設定するロギングは、ロギング設定を直ちにロードします。そのため、ロギング設定は、必ずロギングが依存しているすべての設定の 後に 書く必要があります。

Back to Top