Логирование в Django: полное руководство

Правильное логирование - это основа для отладки приложения и мониторинга работы в продакшене. Django предоставляет мощную систему логирования, основанную на стандартном модуле Python logging.

Что такое логирование в Django?

Логирование позволяет записывать информацию о работе приложения в различные места (консоль, файлы, база данных, внешние сервисы) с разными уровнями детализации. Это критически важно для:

  • Отладки ошибок в разработке
  • Мониторинга производительности в продакшене
  • Аудита действий пользователей
  • Анализа проблем в реальном времени

Уровни логирования

Django использует стандартные уровни логирования Python:

  1. DEBUG - детальная информация для отладки
  2. INFO - общая информация о работе приложения
  3. WARNING - предупреждения о потенциальных проблемах
  4. ERROR - ошибки, которые не прерывают работу
  5. CRITICAL - критические ошибки, требующие немедленного внимания

Базовая конфигурация логирования

Настройка логирования происходит в файле settings.py:

 1LOGGING = {
 2    'version': 1,
 3    'disable_existing_loggers': False,
 4    'formatters': {
 5        'verbose': {
 6            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
 7            'style': '{',
 8        },
 9        'simple': {
10            'format': '{levelname} {message}',
11            'style': '{',
12        },
13    },
14    'handlers': {
15        'console': {
16            'class': 'logging.StreamHandler',
17            'formatter': 'verbose',
18        },
19    },
20    'loggers': {
21        'django': {
22            'handlers': ['console'],
23            'level': 'INFO',
24            'propagate': False,
25        },
26        'myapp': {
27            'handlers': ['console'],
28            'level': 'DEBUG',
29            'propagate': False,
30        },
31    },
32}

Практические примеры конфигурации

1. Логирование в файл

 1LOGGING = {
 2    'version': 1,
 3    'disable_existing_loggers': False,
 4    'formatters': {
 5        'verbose': {
 6            'format': '{levelname} {asctime} {module} {funcName} {lineno:d} {message}',
 7            'style': '{',
 8        },
 9    },
10    'handlers': {
11        'file': {
12            'class': 'logging.FileHandler',
13            'filename': '/var/log/django/app.log',
14            'formatter': 'verbose',
15            'encoding': 'utf-8',
16        },
17        'rotating_file': {
18            'class': 'logging.handlers.RotatingFileHandler',
19            'filename': '/var/log/django/app.log',
20            'maxBytes': 1024 * 1024,  # 1MB
21            'backupCount': 5,
22            'formatter': 'verbose',
23            'encoding': 'utf-8',
24        },
25    },
26    'loggers': {
27        'myapp': {
28            'handlers': ['file', 'rotating_file'],
29            'level': 'DEBUG',
30            'propagate': False,
31        },
32    },
33}

2. Логирование в базу данных

 1LOGGING = {
 2    'version': 1,
 3    'disable_existing_loggers': False,
 4    'formatters': {
 5        'db_formatter': {
 6            'format': '{levelname} {asctime} {message}',
 7            'style': '{',
 8        },
 9    },
10    'handlers': {
11        'database': {
12            'class': 'django.utils.log.ServerFormatter',
13            'formatter': 'db_formatter',
14        },
15    },
16    'loggers': {
17        'django.db.backends': {
18            'handlers': ['database'],
19            'level': 'DEBUG',
20            'propagate': False,
21        },
22    },
23}

3. Логирование с ротацией по времени

 1LOGGING = {
 2    'version': 1,
 3    'disable_existing_loggers': False,
 4    'formatters': {
 5        'detailed': {
 6            'format': '{levelname} {asctime} {name} {process:d} {thread:d} {message}',
 7            'style': '{',
 8        },
 9    },
10    'handlers': {
11        'timed_rotating': {
12            'class': 'logging.handlers.TimedRotatingFileHandler',
13            'filename': '/var/log/django/app.log',
14            'when': 'midnight',  # Ротация в полночь
15            'interval': 1,  # Каждый день
16            'backupCount': 30,  # Хранить 30 файлов
17            'formatter': 'detailed',
18            'encoding': 'utf-8',
19        },
20    },
21    'loggers': {
22        'myapp': {
23            'handlers': ['timed_rotating'],
24            'level': 'INFO',
25            'propagate': False,
26        },
27    },
28}

4. Логирование с фильтрацией

 1LOGGING = {
 2    'version': 1,
 3    'disable_existing_loggers': False,
 4    'filters': {
 5        'require_debug_true': {
 6            '()': 'django.utils.log.RequireDebugTrue',
 7        },
 8        'require_debug_false': {
 9            '()': 'django.utils.log.RequireDebugFalse',
10        },
11    },
12    'formatters': {
13        'verbose': {
14            'format': '{levelname} {asctime} {module} {message}',
15            'style': '{',
16        },
17    },
18    'handlers': {
19        'console_debug': {
20            'class': 'logging.StreamHandler',
21            'filters': ['require_debug_true'],
22            'formatter': 'verbose',
23        },
24        'file_production': {
25            'class': 'logging.FileHandler',
26            'filename': '/var/log/django/production.log',
27            'filters': ['require_debug_false'],
28            'formatter': 'verbose',
29        },
30    },
31    'loggers': {
32        'myapp': {
33            'handlers': ['console_debug', 'file_production'],
34            'level': 'INFO',
35            'propagate': False,
36        },
37    },
38}

Использование логирования в коде

Вот как использовать логирование в твоих views, models и других компонентах:

В views

 1import logging
 2from django.shortcuts import render
 3from django.http import JsonResponse
 4
 5logger = logging.getLogger(__name__)
 6
 7def user_profile(request, user_id):
 8    """Отображает профиль пользователя"""
 9    try:
10        logger.info(f"Запрос профиля пользователя {user_id}")
11
12        # Получаем пользователя
13        user = User.objects.get(id=user_id)
14
15        logger.debug(f"Пользователь {user.username} найден")
16
17        return render(request, 'profile.html', {'user': user})
18
19    except User.DoesNotExist:
20        logger.warning(f"Пользователь {user_id} не найден")
21        return JsonResponse({'error': 'Пользователь не найден'}, status=404)
22
23    except Exception as e:
24        logger.error(f"Ошибка при получении профиля пользователя {user_id}: {str(e)}", exc_info=True)
25        return JsonResponse({'error': 'Внутренняя ошибка сервера'}, status=500)

В models

 1import logging
 2from django.db import models
 3from django.contrib.auth.models import User
 4
 5logger = logging.getLogger(__name__)
 6
 7class Order(models.Model):
 8    user = models.ForeignKey(User, on_delete=models.CASCADE)
 9    amount = models.DecimalField(max_digits=10, decimal_places=2)
10    created_at = models.DateTimeField(auto_now_add=True)
11
12    def save(self, *args, **kwargs):
13        if self.pk is None:
14            logger.info(f"Создание нового заказа для пользователя {self.user.username}")
15        else:
16            logger.info(f"Обновление заказа {self.pk}")
17
18        super().save(*args, **kwargs)
19
20        logger.debug(f"Заказ {self.pk} сохранен, сумма: {self.amount}")
21
22    def delete(self, *args, **kwargs):
23        logger.warning(f"Удаление заказа {self.pk} пользователя {self.user.username}")
24        super().delete(*args, **kwargs)

В middleware

 1import logging
 2import time
 3from django.utils.deprecation import MiddlewareMixin
 4
 5logger = logging.getLogger(__name__)
 6
 7class PerformanceLoggingMiddleware(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
14        logger.debug(f"Начало обработки запроса: {request.method} {request.path}")
15
16        response = self.get_response(request)
17
18        duration = time.time() - start_time
19
20        if duration > 1.0:  # Логируем медленные запросы
21            logger.warning(f"Медленный запрос: {request.method} {request.path} - {duration:.2f}с")
22        else:
23            logger.debug(f"Запрос {request.method} {request.path} обработан за {duration:.2f}с")
24
25        return response

Логирование SQL запросов

Для отладки производительности базы данных:

 1LOGGING = {
 2    'version': 1,
 3    'disable_existing_loggers': False,
 4    'handlers': {
 5        'console': {
 6            'class': 'logging.StreamHandler',
 7        },
 8    },
 9    'loggers': {
10        'django.db.backends': {
11            'handlers': ['console'],
12            'level': 'DEBUG',
13            'propagate': False,
14        },
15    },
16}

Логирование с контекстом

Используй контекстные процессоры для добавления дополнительной информации:

 1import logging
 2from django.utils.deprecation import MiddlewareMixin
 3
 4class ContextLoggingMiddleware(MiddlewareMixin):
 5    def __call__(self, request):
 6        # Добавляем контекст к логам
 7        extra = {
 8            'user_id': getattr(request.user, 'id', 'anonymous'),
 9            'ip': self._get_client_ip(request),
10            'user_agent': request.META.get('HTTP_USER_AGENT', ''),
11        }
12
13        # Создаем logger с контекстом
14        logger = logging.getLogger(__name__)
15        logger = logger.bind(**extra)
16
17        # Сохраняем в request для использования в views
18        request.logger = logger
19
20        return self.get_response(request)
21
22    def _get_client_ip(self, request):
23        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
24        if x_forwarded_for:
25            return x_forwarded_for.split(',')[0]
26        return request.META.get('REMOTE_ADDR')

Логирование в продакшене

Для продакшена используй более строгие настройки:

 1LOGGING = {
 2    'version': 1,
 3    'disable_existing_loggers': False,
 4    'formatters': {
 5        'production': {
 6            'format': '{levelname} {asctime} {name} {message}',
 7            'style': '{',
 8        },
 9    },
10    'handlers': {
11        'file': {
12            'class': 'logging.handlers.RotatingFileHandler',
13            'filename': '/var/log/django/production.log',
14            'maxBytes': 10 * 1024 * 1024,  # 10MB
15            'backupCount': 10,
16            'formatter': 'production',
17        },
18        'mail_admins': {
19            'class': 'django.utils.log.AdminEmailHandler',
20            'level': 'ERROR',
21        },
22    },
23    'loggers': {
24        'django': {
25            'handlers': ['file'],
26            'level': 'WARNING',
27            'propagate': False,
28        },
29        'myapp': {
30            'handlers': ['file', 'mail_admins'],
31            'level': 'ERROR',
32            'propagate': False,
33        },
34    },
35}

Логирование с внешними сервисами

Для интеграции с системами мониторинга:

 1LOGGING = {
 2    'version': 1,
 3    'disable_existing_loggers': False,
 4    'formatters': {
 5        'json': {
 6            'class': 'pythonjsonlogger.jsonlogger.JsonFormatter',
 7            'format': '%(timestamp)s %(level)s %(name)s %(message)s',
 8        },
 9    },
10    'handlers': {
11        'sentry': {
12            'class': 'raven.contrib.django.handlers.SentryHandler',
13            'level': 'ERROR',
14        },
15        'elastic': {
16            'class': 'logging.handlers.HTTPHandler',
17            'host': 'localhost:9200',
18            'url': '/logs',
19            'method': 'POST',
20            'formatter': 'json',
21        },
22    },
23    'loggers': {
24        'myapp': {
25            'handlers': ['sentry', 'elastic'],
26            'level': 'INFO',
27            'propagate': False,
28        },
29    },
30}

Лучшие практики

  1. Используй разные уровни логирования для разных сред
  2. Логируй контекст - пользователь, IP, время выполнения
  3. Не логируй чувствительную информацию - пароли, токены
  4. Используй ротацию логов для управления размером файлов
  5. Тестируй логирование в разных средах

Тестирование логирования

Вот пример теста для проверки логирования:

 1from django.test import TestCase
 2from django.test.utils import override_settings
 3from unittest.mock import patch
 4import logging
 5
 6class LoggingTestCase(TestCase):
 7    @override_settings(LOGGING={
 8        'version': 1,
 9        'handlers': {
10            'test': {
11                'class': 'logging.NullHandler',
12            },
13        },
14        'loggers': {
15            'myapp': {
16                'handlers': ['test'],
17                'level': 'DEBUG',
18            },
19        },
20    })
21    def test_logging_configuration(self):
22        """Тестирует конфигурацию логирования"""
23        logger = logging.getLogger('myapp')
24
25        # Проверяем, что logger настроен правильно
26        self.assertEqual(logger.level, logging.DEBUG)
27        self.assertTrue(logger.handlers)

FAQ

Q: Как настроить логирование в файл?
A: Используй FileHandler и укажи путь к файлу логов. Для ротации используй RotatingFileHandler.

Q: Как логировать SQL запросы?
A: Настрой logger 'django.db.backends' на уровень DEBUG.

Q: Можно ли логировать в базу данных?
A: Да, используй AdminEmailHandler или создай собственный handler.

Q: Как настроить ротацию логов?
A: Используй RotatingFileHandler для размера или TimedRotatingFileHandler для времени.

Q: Как логировать с контекстом?
A: Используй middleware для добавления дополнительной информации в логи.

Заключение

Правильное логирование - это основа для отладки и мониторинга Django приложений. Используй разные уровни логирования, настраивай ротацию файлов и следуй лучшим практикам. Хорошо настроенное логирование сэкономит много времени при решении проблем в продакшене.