Логирование в Django: полное руководство
Правильное логирование - это основа для отладки приложения и мониторинга работы в продакшене. Django предоставляет мощную систему логирования, основанную на стандартном модуле Python logging.
Что такое логирование в Django?
Логирование позволяет записывать информацию о работе приложения в различные места (консоль, файлы, база данных, внешние сервисы) с разными уровнями детализации. Это критически важно для:
- Отладки ошибок в разработке
- Мониторинга производительности в продакшене
- Аудита действий пользователей
- Анализа проблем в реальном времени
Уровни логирования
Django использует стандартные уровни логирования Python:
- DEBUG - детальная информация для отладки
- INFO - общая информация о работе приложения
- WARNING - предупреждения о потенциальных проблемах
- ERROR - ошибки, которые не прерывают работу
- 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 запросов
Для отладки производительности базы данных:
Логирование с контекстом
Используй контекстные процессоры для добавления дополнительной информации:
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}
Лучшие практики
- Используй разные уровни логирования для разных сред
- Логируй контекст - пользователь, IP, время выполнения
- Не логируй чувствительную информацию - пароли, токены
- Используй ротацию логов для управления размером файлов
- Тестируй логирование в разных средах
Тестирование логирования
Вот пример теста для проверки логирования:
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 приложений. Используй разные уровни логирования, настраивай ротацию файлов и следуй лучшим практикам. Хорошо настроенное логирование сэкономит много времени при решении проблем в продакшене.