タイトル: ログ出力方法
SEOタイトル: djangoのログ出力方法
Djangoのアプリログは、名前・loggerの実効レベル・handlerのレベル・伝播先を分けて確認します。まずファイルを作らないstderrの最小例で経路を確認し、保存や収集の設定は別に設計します。
LOGGINGを読む順序と起動時の注意
DjangoはPython標準loggingを使います。通常はsettings.pyのLOGGINGをdictConfig形式で宣言し、django.setup()が既定設定、その後に独自設定を適用します。settings.pyの読み込み中にlogger.infoを呼んでも、まだこの設定が有効とは限りません。loggerの取得をモジュール先頭で行うことと、そこで起動ログを出すことは別です。リクエストごとにdictConfig/basicConfig/addHandlerを呼ばず、起動手順で一度設定します。開発autoreloadや複数ワーカーではプロセスが複数になるため、起動ログの複数行を直ちにhandler重複と判断しません。
- レベルはDEBUG → INFO → WARNING → ERROR → CRITICALの順です。発行元loggerがレコードを受け付けても、出力先handlerのしきい値やfilterで落ちることがあります。logger=DEBUG、handler=INFOならDEBUGはそのhandlerへ出ません。
- 名前はドット区切りの階層です。logging_demo.viewsはlogging_demoの子ですが、myapp.viewsやdjango.requestは別系統です。NOTSETのloggerは祖先のレベルを使い、rootは名前のない最上位loggerです。新規Pythonのroot既定レベルはWARNINGです。
- propagate=Trueなら祖先のhandlerへ渡ります。その際、祖先logger自身のレベル・filterは再判定されず、祖先handlerのレベル・filterは適用されます。同じ出力先を子と親の両方へ付けると二重になる場合があります。Falseはその先への伝播を止めるだけで、既に付けた複数handlerや収集側の重複を解消しません。
- version=1は設定辞書のスキーマ版で、Djangoの版ではありません。disable_existing_loggers=Falseは既存ロガーを一括無効化しない指定です。dictConfigで省略すると通常Trueになり、名前のない既存ロガー等を無効化する可能性があります。Falseだけで全handler・レベルが保持されるわけではなく、明示したloggerやその子の再設定は別です。
stderrへ出す最小プロジェクト
Python 3.12 / Django 5.2用の隔離例です。新しい作業ディレクトリのlogging_demo内に空の__init__.pyと、次の3ファイルを作ります。既存プロジェクトへsettings.py全体を上書きしません。認証・DB・業務処理を持たない検証用で、SECRET_KEYの固定値は本番に使えません。
logging_demo/settings.py
SECRET_KEY = "isolated-example-only-not-for-deployment"
DEBUG = False
ALLOWED_HOSTS = ["testserver", "127.0.0.1"]
ROOT_URLCONF = "logging_demo.urls"
INSTALLED_APPS = []
MIDDLEWARE = []
DATABASES = {"default": {"ENGINE": "django.db.backends.dummy"}}
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"app": {
"format": "%(levelname)s %(name)s %(event_code)s %(message)s",
"defaults": {"event_code": "-"},
},
},
"handlers": {
"app_stderr": {
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr",
"level": "INFO",
"formatter": "app",
},
},
"loggers": {
"logging_demo": {
"handlers": ["app_stderr"],
"level": "INFO",
"propagate": False,
},
},
}
この設定はlogging_demoだけにINFO以上のstderr出力を付け、propagate=Falseでrootへの重複を避けます。djangoやrootを明示的に変更せず、Djangoの既定通知を全面的に置換する例ではありません。StreamHandlerの既定もstderrですが、ここでは行先を明示しています。formatterのdefaultsはPython 3.12のdictConfigで扱え、event_codeを渡さない通常ログでも「-」で整形できます。必須のextraを全呼出元へ要求すると欠落時の整形エラーでログを失うため、既定値と異常時も検査します。
logging_demo/views.py
import logging
from django.http import JsonResponse
from django.views.decorators.http import require_GET
logger = logging.getLogger(__name__)
@require_GET
def health(request):
logger.info("health_check", extra={"event_code": "HEALTH_OK"})
return JsonResponse({"ok": True})
logging_demo/urls.py
from django.urls import path
from .views import health
urlpatterns = [path("health/", health)]
この作業ディレクトリで、Django導入済みの専用仮想環境から python -m django runserver 127.0.0.1:8080 --settings=logging_demo.settings --noreload を起動し、PC内の http://127.0.0.1:8080/health/ へGETします。開発用であり本番サーバーではありません。app側は INFO logging_demo.views HEALTH_OK health_check を1行出し、JSONのokがtrueになります。開発サーバーのアクセス行は別に出ます。確認後はCtrl+Cで停止します。DBへ接続せず、ファイルも作らない例です。
元のコードと設定の記録
旧コード表も完成例の近くに置いて比較します。色分けを保持していますが、そのまま実行せず各直前の訂正を確認してください。
| この記事の要点 |
|---|
|
ビューでログ出力
旧例の注意:この黒い旧例は省略断片です。logger.infoと省略記号のインデントがgetの本体になっておらず、そのまま実行できません。Viewのimport・HttpResponse等の戻り値も必要です。__name__が実際にmyappで始まるか確認してください。完成例は「stderrへ出す最小プロジェクト」にあります。
|
import logging
def get(self, request, *args, **kwargs): logger = logging.getLogger(__name__) logger.info('Hello World!') ... |
ログ設定
旧例の注意:旧本文のsetting.pyは通常settings.pyの誤記です。下の黒い旧設定にはosのimportとLOG_DIRの定義がなく、無指定のディレクトリを自動作成するものでもありません。BASE_DIRがPathなら文字列との加算はできません。ファイル出力先・権限・複数ワーカーの条件を満たさずに転用しないでください。myappのDEBUGでもconsoleはINFOなのでDEBUGはconsoleへ出ず、propagate=Trueでrootにもhandlerがあると重複する可能性があります。
設定ファイルは 「プロジェクト名/setting.py」 にあります。
|
LOGGING = { |
アプリ・リクエスト・開発サーバー・SQLを分ける
| ロガー | 用途と確認点 |
|---|---|
| logging_demo.views等 | 自分が呼んだlogger.info等です。settings.DEBUG=Falseでも、この例のINFOは出ます。DEBUG設定とlogging.DEBUGというレベルは別物です。 |
| django / django.request | django.requestはリクエスト処理の4xxをWARNING、5xxをERRORで記録します。全200アクセスを記録するものではありません。SuspiciousOperationの400等はdjango.security系になる場合があります。 |
| django.server | runserverの応答ログです。既定でINFO以上をconsoleへ出し、propagate=Falseです。本番WSGI/ASGIやリバースプロキシのアクセスログとは別で、後者はサーバー側の設定を確認します。 |
| django.db.backends | SQLとパラメータを含み得ます。通常のSQL診断はsettings.DEBUG=Trueと適切なログレベルが前提で、handlerをDEBUGにするだけでは有効になりません。これはアプリのINFOがDEBUG=Falseで出ないという意味ではありません。 |
Django既定のdjangoロガーはINFO以上で、DEBUG=True時はconsoleのfilterが通り、DEBUG=False時はERROR以上をAdminEmailHandlerへ渡します。実際の送信にはADMINSとメール設定等が必要です。django.serverはDEBUGと独立しています。この最小例は既定通知を残すため、本番で全Djangoエラーが必ずstderrへ残る設定ではありません。通知先と収集の要件を決め、既存handlerを確認して個別に設計します。調査のために本番DEBUGをTrueにしません。
秘密値を出さない設計と、例外ログの限界
このhealth例が出すのは固定メッセージと固定event_codeだけです。request.GET/POST/body、Cookie、Authorization、セッション、メールアドレス・ユーザーID、URLのクエリ、SQLの値をそのまま記録しません。外部入力をevent_codeや改行付きメッセージへ流し込まず、許可したイベント名・必要最小限の検証済みメタデータを選びます。引数形式のlogger.infoであっても内容の匿名化や改行対策は自動ではありません。
except内のlogger.exceptionやexc_info=Trueはトレースバックと例外メッセージを含み、そこで秘密値が漏れる可能性があります。Djangoのエラーレポート用sensitive_post_parameters/sensitive_variablesは、任意のloggingメッセージを一律に伏字化する機能ではありません。requestがextraに入るフレームワークログを別handlerが扱う場合もあります。この例や単体テストだけで全ログの秘匿を保証せず、アプリ・Django通知・Webサーバー・収集基盤を別々にレビューします。実秘密値を検証用ログへ流しません。
stderrとファイル保存・ローテートの選択
stderrは永続保存を保証しません。コンテナやサービス管理・収集基盤側で行の取得、アクセス制御、保持期限、容量、欠落/重複時の扱いを決めます。標準出力と標準エラーの収集経路が別なら、実行プロセスのstderrを確認します。ログ閲覧や共有も個人情報を扱う操作です。
FileHandler等を使う場合は、公開DocumentRootの外に専用ディレクトリを配備時に用意し、実行ユーザーのディレクトリ通過・書込権限、既存ファイル所有者、空き容量、文字コードを確認します。親ディレクトリは自動作成されず、既定ではhandler構築時にファイルを開くので、パス/権限不備でDjango起動自体が失敗し得ます。777や管理者起動で回避しません。コンテナのread-only filesystemや永続ボリュームも区別します。
標準RotatingFileHandler/TimedRotatingFileHandlerで複数ワーカーが同じファイルをローテートすると競合し得ます。stderrからの外部収集、またはQueueHandler等から単一の書込担当へ集約する構成を検討します。logrotateとWatchedFileHandlerを組み合わせてもプロセス間の書込直列化を保証せず、Unix向けの再open動作・所有権・切替時の欠落等を別途検証します。兄弟のローテート設定はパラメータの参考であり、「classを変えるだけ」「安全」という一般化はしません。SQLログの確認も秘密値・負荷・権限を別途審査し、掲載の本番DB設定変更をこの例の続きとして実行しません。
ログが出ない・二重になるときの確認
- 起動中のsettingsモジュールとプロセス、logger.name、disabled/isEnabledFor、実効レベルを確認します。設定の再読込が必要なプロセスと、開発autoreloadを分けます。
- 接続されたhandler、handler.level/filter、formatterの欠落項目、propagateと祖先/rootのhandlerを確認します。名前の不一致をファイル権限の変更で直そうとしません。
- 出力先stderr・収集側と、ファイルのパス/権限/容量を確認します。INFOが消えた理由をDEBUG設定だけで断定しません。
- 固定のテストイベントで1回/複数回を数えます。業務リクエストの再実行で外部副作用を重複させたり、ログ全文をそのまま共有したりしません。
内容確認:2026年10月26日。Python 3.12.10 / Django 5.2.18の隔離環境でdictConfig、Client、ログ捕捉を検査しています。実DB・本番ログ/設定・メール送信・本番収集・ファイル権限・複数プロセスのローテートは検証していません。親のDjango、本番配備も参照してください。
公式資料:Django loggingの設定と呼出し、Django既定設定とlogger一覧、Python loggingの階層とレベル、dictConfigのスキーマとdefaults、複数プロセスのログ設計、Djangoエラーレポートの機微情報、Pythonのファイルhandler。