ロギングの設定と利用

Django はそのままで動作する デフォルトのロギング設定 を持っており、すぐに拡張可能です。

基本的なロギング呼び出しを作成する

ログメッセージをコード内から送信するには、コードにロギングの呼び出しを書く必要があります。

settings.py でログ呼び出しを使用しようとしないでください。

Django のロギングは setup() 関数の一部として設定されるため、 settings.py でロギングを呼び出しても期待通りに動作しないかもしれません。ロギングを調べるには、以下の例で推奨されているようにビュー関数を使用してください。

まず、Python の logging ライブラリをインポートし、logging.getLogger() でロガーのインスタンスを取得します。getLogger() メソッドに、ロガーとロガーが出力するレコードを識別するための名前を指定します。そのための良い方法は __name__ を使用することです (これについては以下の ロガーの名前空間を使う を参照してください)。これは、次のように、現在の Python モジュールの名前をドット区切りのパスで返します。

import logging

logger = logging.getLogger(__name__)

この宣言はモジュール・レベルで行うのがよいとされています。

そして、ビューなどの関数内で、logger にレコードを送信します。

def some_view(request):
    ...
    if some_risky_state:
        logger.warning("Platform is running at risk")

このコードが実行されると、そのメッセージを含む LogRecord が logger に送られます。Django のデフォルトの logging 設定を使っている場合、メッセージはコンソールに表示されます。

上の例で使われている WARNING レベルは、いくつかある ログの重大度レベル: DEBUG, INFO, WARNING, ERROR, CRITICAL の一つです。したがって、別の例は次のようになります。

logger.critical("Payment system is not responding")

重要

WARNING より低いレベルのレコードはデフォルトではコンソールに表示されません。この動作を変更するには追加の設定が必要です。

ロギングの設定をカスタマイズする

Djangoのロギング設定は初期状態で動作しますが、追加の設定を行うことで、ログを様々な宛先(ログファイル、外部サービス、メールなど)に送信するのを正確にコントロールできます。

以下のものを設定できます。

  • どのレコードがどのハンドラに送られるかを決定するためのロガーマッピング
  • 受け取ったレコードをどう処理するかを決めるハンドラー
  • レコードの転送をさらにコントロールし、レコードをその場で変更することもできるフィルタ
  • LogRecord オブジェクトを、人間や他のシステムで利用できるように文字列や他の形式に変換するためのフォーマッタ

ロギングの設定には様々な方法があります。Django では、 LOGGING 設定が最もよく使われます。この設定は dictConfig フォーマット を使い、 デフォルトのロギング設定 を拡張します。

カスタム設定が Django のデフォルトとどのようにマージされるかについては ロギングを設定する を参照してください。

その他のロギングの設定方法の詳細については Python logging documentation を参照してください。簡単にするために、このドキュメントでは LOGGING 設定による設定のみを考えます。

ロギングの基本的な設定

ロギングを設定する場合、以下のようにします。

LOGGING 辞書を作成する

settings.py に以下を追加します:

LOGGING = {
    "version": 1,  # the dictConfig format version
    "disable_existing_loggers": False,  # retain the default loggers
}

ほとんどの場合、 disable_existing_loggersFalse に設定することで、デフォルトのロギング設定を保持し、拡張できます。

ハンドラを設定する

この例では、Python の FileHandler を使って、レベル DEBUG 以上のログを (プロジェクトルート内の) general.log ファイルに保存する file という名前のハンドラを設定します:

LOGGING = {
    # ...
    "handlers": {
        "file": {
            "class": "logging.FileHandler",
            "filename": "general.log",
        },
    },
}

異なるハンドラクラスは異なる設定オプションを取ります。利用可能なハンドラクラスの詳細については、 Django が提供する AdminEmailHandler や Python が提供する様々な handler class を参照してください。

ログレベルはハンドラで設定することもできます(デフォルトでは、ハンドラはすべてのレベルのログメッセージを受け取ります)。上の例を使うと、次のように書けます:

{
    "class": "logging.FileHandler",
    "filename": "general.log",
    "level": "DEBUG",
}

これで、レベル DEBUG 以上のレコードだけを受け付けるようにハンドラを設定できます。

ロガーマッピングを設定する

このハンドラにレコードを送信するには、ロガーマッピングをそのハンドラを使うように設定します。例えば:

LOGGING = {
    # ...
    "loggers": {
        "": {
            "level": "DEBUG",
            "handlers": ["file"],
        },
    },
}

マッピングの名前はどのログレコードを処理するかを決定します。この設定 ("") は 無名 です。つまり、 すべての ロガーからのレコードを処理します(レコードを処理するロガーを決定するためにマッピング名を使用する方法については、以下の ロガーの名前空間を使う を参照してください)。

これは DEBUG レベル以上のメッセージを file というハンドラーに転送します。

ロガーは複数のハンドラーにメッセージを転送できるので、ロガーとハンドラーの関係は多対多であることに注意してください。

このコードを実行したら、:

logger.debug("Attempting to connect to API")

プロジェクトルートの general.log ファイルにそのメッセージが保存されるでしょう。

フォーマッタを設定する

デフォルトでは、最終的なログ出力には各 log record のメッセージ部分が含まれます。追加データを含めたい場合はフォーマッタを使います。まずフォーマッタに名前を付けて定義します。この例では verbosesimple という名前のフォーマッタを定義しています:

LOGGING = {
    # ...
    "formatters": {
        "verbose": {
            "format": "{name} {levelname} {asctime} {module} {process:d} {thread:d} {message}",
            "style": "{",
        },
        "simple": {
            "format": "{levelname} {message}",
            "style": "{",
        },
    },
}

style キーワードを使用すると、 str.format() 用の { または string.Template フォーマット用の $ を指定できます。デフォルトは $ です。

含めることができる LogRecord 属性については LogRecord attributes を参照してください。

ハンドラにフォーマッタを適用するには、ハンドラの辞書に formatter エントリを追加します:

"handlers": {
    "file": {
        "class": "logging.FileHandler",
        "filename": "general.log",
        "formatter": "verbose",
    },
}

ロガーの名前空間を使う

無名のロギング設定 "" は任意の Python アプリケーションからログを取り込みます。名前付きロギング設定は、一致する名前のロガーからだけログを取得します。

logger インスタンスの名前空間は getLogger() を使って定義します。例えば、 my_appviews.py で次のように定義します:

logger = logging.getLogger(__name__)

これは my_app.views 名前空間にロガーを作成します。 __name__ を使うと、プロジェクト内のアプリケーションにおけるログメッセージの出所に基づいて、自動的にそれらを整理できます。また、それによって名前の衝突が起こらなくなります。

my_app.views という名前のロガーマッピングは、このロガーからレコードを取得します:

LOGGING = {
    # ...
    "loggers": {
        "my_app.views": {...},
    },
}

my_app という名前のロガーマッピングはより広く、 my_app 名前空間内のロガー(my_app.viewsmy_app.utils などを含む)からレコードを取得します:

LOGGING = {
    # ...
    "loggers": {
        "my_app": {...},
    },
}

ロガーの名前空間を明示的に定義することもできます:

logger = logging.getLogger("project.payment")

そして、それに応じてロガーマッピングを設定することもできます。

ロガーの階層と伝搬(propagation)を使う

ロガーの命名は 階層的 です。 my_appmy_app.views の親であり、 my_app.viewsmy_app.views.private の親です。特に指定がない限り、ロガーのマッピングは処理したレコードを親に伝播(propagate)します―― my_app.views.private 名前空間のロガーからのレコードは、 my_appmy_app.views の両方のマッピングで処理されます。

この動作を管理するには、定義したマッピングに "propagate" キーを設定します:

LOGGING = {
    # ...
    "loggers": {
        "my_app": {
            # ...
        },
        "my_app.views": {
            # ...
        },
        "my_app.views.private": {
            # ...
            "propagate": False,
        },
    },
}

propagate のデフォルトは True です。この例では、my_app.views.private のログは親プロセスでは処理されませんが、 my_app.views のログは親プロセスで処理されます。

レスポンシブなロギングを設定する

ロギングは、必要な情報ができるだけ多く含まれているときに最も有用であり、目的のために必要な情報だけが含まれているとなお嬉しいです。そして、必要な情報量は、あなたが何をしているかによって異なります。デバッグ中には、本番環境では過剰で役に立たないようなレベルの情報が必要になります。

必要な時に必要な詳細レベルを提供するように、ロギングを設定できます。これを実現するために手動で設定を変更するよりも良い方法は、環境に応じて自動的に設定を適用することです。

例えば、開発環境とステージング環境で環境変数 DJANGO_LOG_LEVEL を適切に設定し、ロガーマッピングで次のように使用できます:

"level": os.getenv("DJANGO_LOG_LEVEL", "WARNING")

- 環境がより低いログレベルを指定しない限り、このコンフィギュレーションは重大度 WARNING 以上のレコードだけをハンドラに転送します。

設定の他のオプション(ハンドラの levelformatter オプションなど)も同様に管理できます。

Back to Top