Files
chunyu_project/logs/middleware.py
T
2026-08-05 23:59:15 +08:00

128 lines
4.0 KiB
Python

import time
import json
import threading
from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
SENSITIVE_FIELDS = {
'password', 'password1', 'password2', 'old_password', 'new_password',
'token', 'access_token', 'refresh_token', 'secret', 'api_key',
'authorization', 'credit_card', 'cvv', 'ssn',
}
EXCLUDE_PATHS = [
'/static/', '/media/', '/favicon.ico', '/admin/jsi18n/',
'/swagger/', '/redoc/',
]
def _get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
return x_forwarded_for.split(',')[0].strip()
return request.META.get('REMOTE_ADDR')
def _mask_sensitive_data(data):
if not data:
return data
try:
if isinstance(data, str):
try:
data = json.loads(data)
except (json.JSONDecodeError, TypeError):
return data[:2000]
if isinstance(data, dict):
masked = {}
for key, value in data.items():
if any(s in key.lower() for s in SENSITIVE_FIELDS):
masked[key] = '***'
elif isinstance(value, dict):
masked[key] = _mask_sensitive_data(value)
elif isinstance(value, list):
masked[key] = [_mask_sensitive_data(item) if isinstance(item, dict) else item for item in value[:10]]
else:
masked[key] = value
return json.dumps(masked, ensure_ascii=False)[:2000]
return str(data)[:2000]
except Exception:
return ''
def _extract_app_name(path):
parts = path.strip('/').split('/')
if parts and parts[0]:
return parts[0]
return 'root'
def _save_log_async(log_data):
def _save():
try:
from .models import ApiRequestLog
ApiRequestLog.objects.create(**log_data)
except Exception:
pass
thread = threading.Thread(target=_save)
thread.daemon = True
thread.start()
class RequestLoggingMiddleware(MiddlewareMixin):
def __init__(self, get_response):
super().__init__(get_response)
self.get_response = get_response
def __call__(self, request):
if any(request.path.startswith(path) for path in EXCLUDE_PATHS):
return self.get_response(request)
start_time = time.time()
try:
user = request.user if hasattr(request, 'user') and request.user.is_authenticated else None
except Exception:
user = None
response = self.get_response(request)
try:
duration_ms = (time.time() - start_time) * 1000
request_body = ''
if request.method in ('POST', 'PUT', 'PATCH'):
try:
if hasattr(request, 'body'):
body = request.body.decode('utf-8', errors='ignore')
request_body = _mask_sensitive_data(body)
except Exception:
request_body = ''
response_size = 0
if hasattr(response, 'content'):
response_size = len(response.content)
log_data = {
'user': user,
'username': user.username if user else (request.user.username if hasattr(request, 'user') and hasattr(request.user, 'username') else ''),
'ip_address': _get_client_ip(request),
'user_agent': request.META.get('HTTP_USER_AGENT', '')[:500],
'method': request.method,
'path': request.path[:255],
'query_string': request.META.get('QUERY_STRING', '')[:1000],
'request_body': request_body,
'status_code': response.status_code,
'response_size': response_size,
'duration_ms': round(duration_ms, 2),
'app_name': _extract_app_name(request.path),
'is_error': response.status_code >= 400,
}
_save_log_async(log_data)
except Exception:
pass
return response