Middleware для логирования
Кастомный middleware может логировать все входящие запросы и их обработку. Это полезно для мониторинга производительности, отладки и аналитики.
Базовый логирующий middleware
1import logging
2import time
3from django.utils.deprecation import MiddlewareMixin
4
5logger = logging.getLogger(__name__)
6
7class RequestLoggingMiddleware(MiddlewareMixin):
8 def __init__(self, get_response):
9 self.get_response = get_response
10
11 def __call__(self, request):
12 start_time = time.time()
13 response = self.get_response(request)
14 duration = time.time() - start_time
15
16 logger.info(f'{request.method} {request.path} - {response.status_code} - {duration:.2f}s')
17 return response
Расширенный middleware с детальной информацией
1import logging
2import time
3import json
4from django.utils.deprecation import MiddlewareMixin
5from django.conf import settings
6
7logger = logging.getLogger(__name__)
8
9class DetailedRequestLoggingMiddleware(MiddlewareMixin):
10 def __init__(self, get_response):
11 self.get_response = get_response
12
13 def __call__(self, request):
14 start_time = time.time()
15
16 # Логируем входящий запрос
17 self.log_request(request)
18
19 response = self.get_response(request)
20 duration = time.time() - start_time
21
22 # Логируем ответ
23 self.log_response(request, response, duration)
24
25 return response
26
27 def log_request(self, request):
28 log_data = {
29 'method': request.method,
30 'path': request.path,
31 'ip': self.get_client_ip(request),
32 'user_agent': request.META.get('HTTP_USER_AGENT', ''),
33 'referer': request.META.get('HTTP_REFERER', ''),
34 'query_params': dict(request.GET),
35 'timestamp': time.time()
36 }
37
38 if hasattr(request, 'user') and request.user.is_authenticated:
39 log_data['user_id'] = request.user.id
40 log_data['username'] = request.user.username
41
42 logger.info(f'Request: {json.dumps(log_data, ensure_ascii=False)}')
43
44 def log_response(self, request, response, duration):
45 log_data = {
46 'method': request.method,
47 'path': request.path,
48 'status_code': response.status_code,
49 'duration': round(duration, 3),
50 'content_length': len(response.content) if hasattr(response, 'content') else 0
51 }
52
53 logger.info(f'Response: {json.dumps(log_data, ensure_ascii=False)}')
54
55 def get_client_ip(self, request):
56 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
57 if x_forwarded_for:
58 ip = x_forwarded_for.split(',')[0]
59 else:
60 ip = request.META.get('REMOTE_ADDR')
61 return ip
Middleware с фильтрацией и настройками
1import logging
2import time
3from django.utils.deprecation import MiddlewareMixin
4from django.conf import settings
5
6logger = logging.getLogger(__name__)
7
8class ConfigurableRequestLoggingMiddleware(MiddlewareMixin):
9 def __init__(self, get_response):
10 self.get_response = get_response
11 # Настройки из Django settings
12 self.log_slow_requests = getattr(settings, 'LOG_SLOW_REQUESTS', True)
13 self.slow_request_threshold = getattr(settings, 'SLOW_REQUEST_THRESHOLD', 1.0)
14 self.exclude_paths = getattr(settings, 'LOG_EXCLUDE_PATHS', ['/health/', '/metrics/'])
15 self.log_level = getattr(settings, 'REQUEST_LOG_LEVEL', 'INFO')
16
17 def __call__(self, request):
18 # Пропускаем исключенные пути
19 if any(request.path.startswith(path) for path in self.exclude_paths):
20 return self.get_response(request)
21
22 start_time = time.time()
23 response = self.get_response(request)
24 duration = time.time() - start_time
25
26 # Определяем уровень логирования
27 if duration > self.slow_request_threshold:
28 log_level = 'WARNING'
29 message = f'SLOW REQUEST: {request.method} {request.path} - {response.status_code} - {duration:.2f}s'
30 else:
31 log_level = self.log_level
32 message = f'{request.method} {request.path} - {response.status_code} - {duration:.2f}s'
33
34 # Логируем с соответствующим уровнем
35 if log_level == 'DEBUG':
36 logger.debug(message)
37 elif log_level == 'INFO':
38 logger.info(message)
39 elif log_level == 'WARNING':
40 logger.warning(message)
41 elif log_level == 'ERROR':
42 logger.error(message)
43
44 return response
Настройка в Django settings.py
1# settings.py
2
3MIDDLEWARE = [
4 'django.middleware.security.SecurityMiddleware',
5 'django.contrib.sessions.middleware.SessionMiddleware',
6 'django.middleware.common.CommonMiddleware',
7 'django.middleware.csrf.CsrfViewMiddleware',
8 'django.contrib.auth.middleware.AuthenticationMiddleware',
9 'django.contrib.messages.middleware.MessageMiddleware',
10 'django.middleware.clickjacking.XFrameOptionsMiddleware',
11 # Добавляем наш middleware
12 'your_app.middleware.RequestLoggingMiddleware',
13]
14
15# Настройки для middleware логирования
16LOG_SLOW_REQUESTS = True
17SLOW_REQUEST_THRESHOLD = 1.0 # секунды
18LOG_EXCLUDE_PATHS = ['/health/', '/metrics/', '/static/', '/media/']
19REQUEST_LOG_LEVEL = 'INFO'
20
21# Настройка логирования
22LOGGING = {
23 'version': 1,
24 'disable_existing_loggers': False,
25 'formatters': {
26 'verbose': {
27 'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
28 'style': '{',
29 },
30 'simple': {
31 'format': '{levelname} {message}',
32 'style': '{',
33 },
34 },
35 'handlers': {
36 'file': {
37 'level': 'INFO',
38 'class': 'logging.FileHandler',
39 'filename': 'logs/requests.log',
40 'formatter': 'verbose',
41 },
42 'console': {
43 'level': 'INFO',
44 'class': 'logging.StreamHandler',
45 'formatter': 'simple',
46 },
47 },
48 'loggers': {
49 'your_app.middleware': {
50 'handlers': ['file', 'console'],
51 'level': 'INFO',
52 'propagate': False,
53 },
54 },
55}
Middleware для логирования ошибок
1import logging
2import traceback
3from django.utils.deprecation import MiddlewareMixin
4
5logger = logging.getLogger(__name__)
6
7class ErrorLoggingMiddleware(MiddlewareMixin):
8 def __init__(self, get_response):
9 self.get_response = get_response
10
11 def __call__(self, request):
12 return self.get_response(request)
13
14 def process_exception(self, request, exception):
15 logger.error(
16 f'Exception in {request.method} {request.path}: {str(exception)}',
17 extra={
18 'request': request,
19 'exception': exception,
20 'traceback': traceback.format_exc(),
21 'user_id': getattr(request.user, 'id', None),
22 'ip': self.get_client_ip(request),
23 }
24 )
25 return None
26
27 def get_client_ip(self, request):
28 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
29 if x_forwarded_for:
30 ip = x_forwarded_for.split(',')[0]
31 else:
32 ip = request.META.get('REMOTE_ADDR')
33 return ip
Middleware для статистики запросов
1import time
2from collections import defaultdict
3from django.utils.deprecation import MiddlewareMixin
4from django.core.cache import cache
5
6class RequestStatsMiddleware(MiddlewareMixin):
7 def __init__(self, get_response):
8 self.get_response = get_response
9
10 def __call__(self, request):
11 start_time = time.time()
12 response = self.get_response(request)
13 duration = time.time() - start_time
14
15 # Обновляем статистику
16 self.update_stats(request, response, duration)
17
18 return response
19
20 def update_stats(self, request, response, duration):
21 # Получаем текущую статистику из кеша
22 stats = cache.get('request_stats', defaultdict(int))
23
24 # Обновляем счетчики
25 stats['total_requests'] += 1
26 stats['total_duration'] += duration
27 stats[f'method_{request.method}'] += 1
28 stats[f'status_{response.status_code}'] += 1
29
30 # Обновляем среднее время
31 stats['avg_duration'] = stats['total_duration'] / stats['total_requests']
32
33 # Сохраняем в кеш на 1 час
34 cache.set('request_stats', dict(stats), 3600)
35
36 def get_stats(self):
37 """Получить текущую статистику"""
38 return cache.get('request_stats', {})
Использование в views
1from django.http import JsonResponse
2from django.views.decorators.csrf import csrf_exempt
3
4@csrf_exempt
5def get_request_stats(request):
6 """API endpoint для получения статистики запросов"""
7 from your_app.middleware import RequestStatsMiddleware
8
9 middleware = RequestStatsMiddleware(None)
10 stats = middleware.get_stats()
11
12 return JsonResponse({
13 'total_requests': stats.get('total_requests', 0),
14 'avg_duration': round(stats.get('avg_duration', 0), 3),
15 'methods': {k: v for k, v in stats.items() if k.startswith('method_')},
16 'status_codes': {k: v for k, v in stats.items() if k.startswith('status_')},
17 })
FAQ
Q: Как добавить middleware в Django?
A: Добавь класс middleware в MIDDLEWARE список в settings.py. Порядок важен - middleware выполняются сверху вниз для запросов и снизу вверх для ответов.
Q: Какой уровень логирования использовать?
A: Используй INFO для обычных запросов, WARNING для медленных запросов, ERROR для исключений. DEBUG может быть слишком детальным для продакшена.
Q: Как фильтровать логи по определенным путям?
A: Создай список исключений в settings.py и проверяй request.path в middleware перед логированием.
Q: Как логировать только медленные запросы?
A: Установи порог времени в настройках и используй условное логирование с разными уровнями.
Q: Как получить IP адрес клиента за прокси?
A: Проверяй HTTP_X_FORWARDED_FOR заголовок, но помни о безопасности - этот заголовок может быть подделан.
Q: Как логировать в разные файлы?
A: Настрой разные handlers в LOGGING конфигурации Django для разных типов логов.