概要
ログを出力する際に各Viewごとに一つずつ設定するのは手間なのでMiddlewareとして共通化させる方法について解説します
前提
- loggerの基本的な知識を有している
- システムユーザのModelを作成済み
UserのModelについてはDjangoのAbstractUserを継承して作成します
詳細に知りたい方は以下の記事を参考にしてください
ディレクトリ構成
今回作成していくのは以下のファイルです
- application/pyproject.toml
- project/settings.py
- application/logs.py
- application/middleware.py
tree
・
└── application
├── application
│ └── utils
│ ├── logs.py
│ └── middleware.py
├── manage.py
├── poetry.lock
├── pyproject.toml
└── project
└── settings.py
では一つずつ解説していきます
ログの設定
toml parserを使ったログの設定方法について詳細に知りたい方は以下の記事を参照してください
pyproject.toml
pyproject.tomlにログを設定していきます
# logの設定
[logging]
version = 1
[logging.formatters.simple]
format = "[%(levelname)s] %(name)s - %(message)s "
[logging.handlers.applicationHandler]
class = "logging.StreamHandler"
level = "INFO"
formatter = "simple"
stream = "ext://sys.stdout"
[logging.handlers.errorHandler]
class = "logging.StreamHandler"
level = "ERROR"
formatter = "simple"
stream = "ext://sys.stdout"
[logging.loggers.application]
level = "DEBUG"
handlers = ["applicationHandler"]
propagate = "no"
[logging.loggers.emergency]
level = "DEBUG"
handlers = ["errorHandler"]
propagate = "no"
今回はpyproject.toml内にLoggerの設定を記載しています
settings.py
dictConfigを使ってtoml内のLoggerの設定を取得していきます
from logging.config import dictConfig
# ログ設定
dictConfig(ConfFile.get()["logging"])
application/utils/logs.py
Loggerの設定を行います
今回はエラーを含めた想定内のログは
- Application
想定外のエラーのログは
- Emergency
として定義しています
import tomllib
from enum import Enum
from typing import Any, Optional
class LoggerName(Enum):
"""ロガー名"""
APPLICATION = "application"
EMERGENCY = "emergency"
class ConfFile:
"""confファイル取得用クラス
Attributes:
_conf_file (Optional[dict[Any, Any]]): pyproject.tomlのデータを辞書形式に変換した内容<br>
最初の1回だけ読み込まれる
"""
_conf_file: Optional[dict[Any, Any]] = None
@classmethod
def get(cls) -> dict[Any, Any]:
"""pyproject.tomlのデータを辞書形式で返す
2回目以降はファイルの読み込みは実施しない
Returns:
dict[Any, Any]: pyproject.tomlの設定データの辞書形式
"""
if cls._conf_file is None:
with open("pyproject.toml", mode="rb") as file:
cls._conf_file = tomllib.load(file)
return cls._conf_file
middlewareの設定
ログを出力する用のMiddlewareを作成します
"""ミドルウェア用のモジュール"""
import datetime
from logging import getLogger
from rest_framework import status
from application.utils.logs import LoggerName
application_logger = getLogger(LoggerName.APPLICATION.value)
class LoggingMiddleware:
"""APIの開始と終了をロギングするミドルウェア"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# API呼び出し以外はログに出さない
if request.path.endswith(".js") or request.path in (
# Swaggerのパス
"/api/docs/",
"/api/schema/",
):
return self.get_response(request)
method = request.method
ip = get_client_ip(request)
path = request.path
user = request.user
user_info = "未ログイン"
if user.is_authenticated:
user_info = f"{user.employee_number} {user.username}"
start_time = datetime.datetime.now()
response = self.get_response(request)
status_code = response.status_code
end_time = datetime.datetime.now()
duration_time = end_time - start_time
message = f"{ip} {user_info} {method} {path} 実行時間: {duration_time} {status_code} "
if status.is_success(response.status_code):
application_logger.info(message)
else:
application_logger.warning(message)
return response
def get_client_ip(request):
"""クライアントのIPアドレスを取得"""
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
if x_forwarded_for:
ip = x_forwarded_for.split(",")[0]
else:
ip = request.META.get("REMOTE_ADDR")
return ip
get_response
response = self.get_response(request)
からAPIを実行した際のresponseを変数に格納します
ログのフォーマット
API実行時のログの
- IPアドレス
- ユーザ情報
- HTTPメソッド
- APIのパス
- 実行時間
- ステータスコード
を出力したいので以下のフォーマットにします
message = f"{ip} {user_info} {method} {path} 実行時間: {duration_time} {status_code} "
settings.py
MIDDLEWAREにLoggingMiddlewareを追加することでログの出力を有効化します
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
# 先ほど作成したLoggingMiddlewareを追加
"application.utils.middleware.LoggingMiddleware",
]
ログの出力
試しに私の方でいくつかAPIを作成
以下のようにログが出力されたら成功です
[INFO] application 2023-07-04 16:43:01,753 - 127.0.0.1 00000001 test01 POST /api/users/send_invite_user_mail/ 200 実行時間:0:00:00.070526
[INFO] application 2023-07-04 16:43:02,402 - 127.0.0.1 未ログイン POST /api/login/ 200 実行時間:0:00:00.213168
参考