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 для разных типов логов.