Как настроить дублирование логов в Python logging для Django проекта?
В моем Django проекте в файле views.py настроено базовое логирование:
from django.shortcuts import render
from models import ORM_Model
logging.basicConfig(
format="%(time)s - %(name)s %(lineno)d - %(levelname)s - %(message)s", level=logging.INFO,
#datefmt='%H:%M:%S',
fmt='%(asctime)s.%(msecs)03d',
)
logger = logging.getLogger(__name__)
#### НАЧАЛО ВЬЮХ
def view_info_app(request,*args, **kwargs):
"""
а все что тут выводится - очень важно!
"""
logger.info("APP: view_info_app")
info_objects = ORM_Model.objects.get(id=123)
logger.info("Очень нужная информация - %s",info_objects)
return render("templates/index.html",{})
def view_index_app(request,*args, **kwargs):
"""
это не важно
"""
logger.info("APP: view_index_app")
info_objects = ORM_Model.objects.filter()
logger.info("Не нужная информация - %s",info_objects)
return render("templates/index.html",{})
Появилась необходимость некоторую часть логов помимо вывода в консоль сохранить в базу данных. Как это реализовать только для нужных вьюх?
Основная проблема дублирования логов в Django возникает из-за конфликта между basicConfig() и встроенной системой логирования Django. Для избежания дублирования и сохранения логов в базу данных только для нужных вьюх следует использовать конфигурацию через LOGGING в settings.py.
Содержание
- Основные причины дублирования логов
- Настройка избежания дублирования
- Сохранение логов в базу данных
- Выборочное логирование для конкретных вьюх
- Полный пример конфигурации
- Рекомендации по оптимизации
Основные причины дублирования логов
Дублирование логов в Django проекте обычно вызвано следующими факторами:
-
Конфликт между
basicConfig()и Django LOGGING
Ваша текущая настройкаbasicConfig()конфликтует с Django-системой логирования, которая уже настроена по умолчанию. -
Наследование логгеров (propagate=True)
При использовании нескольких логгеров сpropagate=Trueлоги могут дублироваться, так как они обрабатываются несколькими обработчиками. -
Повторная настройка корневого логгера
Если в Django settings есть настройкаrootлоггера, это может привести к дублированию при использованииbasicConfig().
Настройка избежания дублирования
Чтобы избежать дублирования, полностью удалите basicConfig() из вашего views.py и настройте логирование через settings.py:
# УДАЛИТЕ ЭТИ СТРОКИ views.py:
# logging.basicConfig(...)
# logger = logging.getLogger(__name__)
Вместо этого используйте в views.py:
import logging
logger = logging.getLogger(__name__) # Только эта строка нужна
Сохранение логов в базу данных
Вариант 1: Использование django-logdb
Добавьте в requirements.txt:
django-logdb
В settings.py:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'verbose',
'level': 'INFO',
},
'database': {
'class': 'django_logdb.handlers.DatabaseHandler',
'formatter': 'verbose',
'level': 'DEBUG',
},
},
'loggers': {
'my_app': {
'handlers': ['console', 'database'],
'level': 'DEBUG',
'propagate': False,
},
'django': {
'handlers': ['console'],
'level': 'INFO',
'propagate': True,
},
},
}
Вариант 2: Кастомный обработчик
Создайте файл logger/handlers.py:
import logging
from django.db import models
class DBHandler(logging.Handler):
def emit(self, record):
logger_entry = LogEntry(
level=record.levelname,
message=record.getMessage(),
module=record.module,
function_name=record.funcName,
line_number=record.lineno,
timestamp=record.created
)
logger_entry.save()
class LogEntry(models.Model):
level = models.CharField(max_length=20)
message = models.TextField()
module = models.CharField(max_length=100)
function_name = models.CharField(max_length=100)
line_number = models.IntegerField()
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
app_label = 'logger'
Выборочное логирование для конкретных вьюх
Метод 1: Разные логгеры для разных вьюх
# views.py
import logging
logger_important = logging.getLogger('important_logs')
logger_regular = logging.getLogger('regular_logs')
def view_info_app(request, *args, **kwargs):
logger_important.info("APP: view_info_app")
info_objects = ORM_Model.objects.get(id=123)
logger_important.info("Очень нужная информация - %s", info_objects)
return render(request, "templates/index.html", {})
def view_index_app(request, *args, **kwargs):
logger_regular.info("APP: view_index_app")
info_objects = ORM_Model.objects.filter()
logger_regular.info("Не нужная информация - %s", info_objects)
return render(request, "templates/index.html", {})
Метод 2: Использование фильтров
# settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'simple': {
'format': '[%(asctime)s] %(levelname)s|%(name)s|%(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
},
},
'filters': {
'important_only': {
'()': 'django_filters.logging.MessageFilter',
'message': 'Очень нужная информация|APP: view_info_app',
},
},
'handlers': {
'applogfile': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_DIR, 'django_blend.log'),
'backupCount': 10,
'formatter': 'simple',
},
'database_important': {
'level': 'INFO',
'class': 'logger.handlers.DBHandler',
'formatter': 'simple',
'filters': ['important_only'],
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
},
'loggers': {
'important_logs': {
'handlers': ['database_important'],
'level': 'INFO',
'propagate': False,
},
'regular_logs': {
'handlers': ['applogfile', 'console'],
'level': 'DEBUG',
'propagate': False,
},
'django': {
'handlers': ['console'],
'level': 'INFO',
'propagate': True,
},
},
}
Метод 3: Декораторы для выборочного логирования
# decorators.py
import logging
import functools
def log_to_database(view_func):
@functools.wraps(view_func)
def wrapper(request, *args, **kwargs):
db_logger = logging.getLogger('database_logs')
db_logger.info(f"VIEW: {view_func.__name__} started")
result = view_func(request, *args, **kwargs)
db_logger.info(f"VIEW: {view_func.__name__} completed")
return result
return wrapper
# views.py
@log_to_database
def view_info_app(request, *args, **kwargs):
logger = logging.getLogger(__name__)
logger.info("APP: view_info_app")
info_objects = ORM_Model.objects.get(id=123)
logger.info("Очень нужная информация - %s", info_objects)
return render(request, "templates/index.html", {})
def view_index_app(request, *args, **kwargs):
logger = logging.getLogger(__name__)
logger.info("APP: view_index_app")
info_objects = ORM_Model.objects.filter()
logger.info("Не нужная информация - %s", info_objects)
return render(request, "templates/index.html", {})
Полный пример конфигурации
# settings.py
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'simple': {
'format': '[%(asctime)s] %(levelname)s|%(name)s|%(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
},
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple',
},
'file': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_DIR, 'django.log'),
'maxBytes': 10485760, # 10MB
'backupCount': 5,
'formatter': 'simple',
},
'database': {
'level': 'INFO',
'class': 'django_logdb.handlers.DatabaseHandler',
'formatter': 'verbose',
},
},
'loggers': {
'myapp.views.important': {
'handlers': ['database'],
'level': 'INFO',
'propagate': False,
},
'myapp.views': {
'handlers': ['file', 'console'],
'level': 'DEBUG',
'propagate': False,
},
'django': {
'handlers': ['console'],
'level': 'WARNING',
'propagate': True,
},
},
}
# views.py
import logging
from django.shortcuts import render
from .models import ORM_Model
logger = logging.getLogger(__name__)
logger_important = logging.getLogger('myapp.views.important')
def view_info_app(request, *args, **kwargs):
"""
а все что тут выводится - очень важно!
"""
logger_important.info("APP: view_info_app")
info_objects = ORM_Model.objects.get(id=123)
logger_important.info("Очень нужная информация - %s", info_objects)
logger.info("Regular log for view_info_app")
return render(request, "templates/index.html", {})
def view_index_app(request, *args, **kwargs):
"""
это не важно
"""
logger.info("APP: view_index_app")
info_objects = ORM_Model.objects.filter()
logger.info("Не нужная информация - %s", info_objects)
return render(request, "templates/index.html", {})
Рекомендации по оптимизации
-
Используйте разные имена логгеров для разных частей приложения
pythonlogger = logging.getLogger(__name__) # Автоматически создает иерархию -
Настройте
propagate=Falseдля избежания дублированияpython'myapp': { 'handlers': ['database'], 'level': 'INFO', 'propagate': False, # Важно! }, -
Используйте уровни логирования для фильтрации
python'handlers': { 'important_only': { 'level': 'INFO', # Только INFO и выше 'class': 'django_logdb.handlers.DatabaseHandler', }, }, -
Для больших проектов рассмотрите использование специализированных пакетов:
- django-logdb для простого логирования в БД
- django-automated-logging для автоматического логирования моделей и вьюх
- django-db-logger для продвинутых возможностей
-
Тестируйте конфигурацию в разных окружениях
python# Пример окружения LOGGING['loggers']['django']['level'] = os.getenv('DJANGO_LOG_LEVEL', 'INFO')
Источники
- Django Documentation - Logging
- Stack Overflow - Django duplicate log output
- django-logdb Package
- SigNoz - Django Logging Guide
- GitHub - django-db-logger
- Medium - Comprehensive Django Logging Guide
- James Lin - Custom Django Log Handler
Заключение
- Удалите
basicConfig()из вашего кода и используйте толькоLOGGINGвsettings.py - Настройте разные логгеры с разными обработчиками для избежания дублирования
- Используйте
propagate=Falseдля предотвращения наследования логов - Для логирования в базу данных используйте специализированные обработчики или пакеты
- Для выборочного логирования применяйте разные имена логгеров, фильтры или декораторы
Эти решения позволят вам эффективно управлять логированием в Django проекте, избегать дублирования и сохранять важные логи только для нужных вьюх в базу данных.