Features: 1) Introduced LoggingError exception for invalid log level handling in log method; 2) Updated logging framework to dynamically initialize loggers using __name__ instead of hardcoded "django".

Fixes: 1) Fixed missing `exc_info` flag in critical and error logs to provide richer error context; 2) Addressed redundant code and unused imports in the logging logic for cleaner execution.

Extra: Refactored `LOGGING` configuration for improved readability and runtime adaptability; optimized related log-handling logic throughout the codebase.
This commit is contained in:
Egor Pavlovich Gorbunov 2025-11-04 14:40:50 +03:00
parent 38b22704b1
commit 9898212855
28 changed files with 72 additions and 102 deletions

View file

@ -1,3 +1,3 @@
import logging import logging
logger = logging.getLogger("django") logger = logging.getLogger(__name__)

View file

@ -11,7 +11,7 @@ from core.crm.exceptions import CRMException
from core.models import CustomerRelationshipManagementProvider, Order, OrderCrmLink from core.models import CustomerRelationshipManagementProvider, Order, OrderCrmLink
from core.utils import is_status_code_success from core.utils import is_status_code_success
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class AmoCRM: class AmoCRM:

View file

@ -39,7 +39,7 @@ from rest_framework.request import Request
from core.elasticsearch import process_query from core.elasticsearch import process_query
from core.models import Address, Brand, Category, Feedback, Order, Product, Stock, Wishlist from core.models import Address, Brand, Category, Feedback, Order, Product, Stock, Wishlist
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class CaseInsensitiveListFilter(BaseInFilter, CharFilter): # type: ignore [misc] class CaseInsensitiveListFilter(BaseInFilter, CharFilter): # type: ignore [misc]

View file

@ -29,7 +29,7 @@ from core.utils.messages import permission_denied_message
from core.utils.nominatim import fetch_address_suggestions from core.utils.nominatim import fetch_address_suggestions
from payments.graphene.object_types import TransactionType from payments.graphene.object_types import TransactionType
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
# noinspection PyUnusedLocal # noinspection PyUnusedLocal

View file

@ -57,7 +57,7 @@ from core.utils.seo_builders import (
) )
from payments.graphene.object_types import TransactionType from payments.graphene.object_types import TransactionType
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class SEOMetaType(ObjectType): # type: ignore [misc] class SEOMetaType(ObjectType): # type: ignore [misc]

View file

@ -102,7 +102,7 @@ from vibes_auth.graphene.mutations import (
from vibes_auth.graphene.object_types import UserType from vibes_auth.graphene.object_types import UserType
from vibes_auth.models import User from vibes_auth.models import User
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class Query(ObjectType): class Query(ObjectType):

View file

@ -6,7 +6,7 @@ from django.core.management.base import BaseCommand
from core.models import Product from core.models import Product
from core.vendors import AbstractVendor from core.vendors import AbstractVendor
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class Command(BaseCommand): class Command(BaseCommand):

View file

@ -6,7 +6,7 @@ from django.contrib.gis.geos import Point
from django.db import models from django.db import models
from modeltranslation.manager import MultilingualManager from modeltranslation.manager import MultilingualManager
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class AddressManager(models.Manager): class AddressManager(models.Manager):

View file

@ -68,7 +68,7 @@ from evibes.settings import CURRENCY_CODE
from evibes.utils.misc import create_object from evibes.utils.misc import create_object
from payments.models import Transaction from payments.models import Transaction
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class AttributeGroup(ExportModelOperationsMixin("attribute_group"), NiceModel): # type: ignore [misc] class AttributeGroup(ExportModelOperationsMixin("attribute_group"), NiceModel): # type: ignore [misc]

View file

@ -28,7 +28,7 @@ from core.serializers.simple import CategorySimpleSerializer, ProductSimpleSeria
from core.typing import FilterableAttribute from core.typing import FilterableAttribute
from core.serializers.utility import AddressSerializer from core.serializers.utility import AddressSerializer
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class AttributeGroupDetailSerializer(ModelSerializer): class AttributeGroupDetailSerializer(ModelSerializer):

View file

@ -23,7 +23,7 @@ from core.utils.emailing import send_order_created_email, send_order_finished_em
from evibes.utils.misc import create_object from evibes.utils.misc import create_object
from vibes_auth.models import User from vibes_auth.models import User
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
# noinspection PyUnusedLocal # noinspection PyUnusedLocal

View file

@ -17,7 +17,7 @@ from rest_framework.request import Request
from evibes.settings import DEBUG, EXPOSABLE_KEYS, LANGUAGE_CODE from evibes.settings import DEBUG, EXPOSABLE_KEYS, LANGUAGE_CODE
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
def graphene_current_lang() -> str: def graphene_current_lang() -> str:

View file

@ -12,7 +12,7 @@ from rest_framework.request import Request
from evibes.settings import UNSAFE_CACHE_KEYS from evibes.settings import UNSAFE_CACHE_KEYS
from vibes_auth.models import User from vibes_auth.models import User
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
def is_safe_cache_key(key: str) -> bool: def is_safe_cache_key(key: str) -> bool:

View file

@ -6,7 +6,7 @@ from django.db.models.constants import LOOKUP_SEP
from django_extensions.db.fields import AutoSlugField from django_extensions.db.fields import AutoSlugField
from slugify import slugify from slugify import slugify
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
def unicode_slugify_function(content: Any) -> str: def unicode_slugify_function(content: Any) -> str:

View file

@ -9,7 +9,7 @@ from core.models import Vendor
from core.vendors import AbstractVendor from core.vendors import AbstractVendor
from evibes.utils.misc import create_object from evibes.utils.misc import create_object
sync_logger = logging.getLogger("django") sync_logger = logging.getLogger(__name__)
async_logger = get_task_logger(__name__) async_logger = get_task_logger(__name__)

View file

@ -9,8 +9,6 @@ from io import BytesIO
from math import ceil, log10 from math import ceil, log10
from typing import Any from typing import Any
from celery import current_task
from celery.utils.log import get_task_logger
from constance import config from constance import config
from django.conf import settings from django.conf import settings
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
@ -30,12 +28,11 @@ from core.models import (
Stock, Stock,
Vendor, Vendor,
) )
from evibes.utils.misc import LogLevel from evibes.utils.misc import LoggingError, LogLevel
from payments.errors import RatesError from payments.errors import RatesError
from payments.utils import get_rates from payments.utils import get_rates
async_logger = get_task_logger("vendors") logger = logging.getLogger(__name__)
logger = logging.getLogger("django")
class NotEnoughBalanceError(Exception): class NotEnoughBalanceError(Exception):
@ -111,29 +108,25 @@ class AbstractVendor:
return self.get_vendor_instance(safe=True).name if self.get_vendor_instance() else self.vendor_name return self.get_vendor_instance(safe=True).name if self.get_vendor_instance() else self.vendor_name
def log(self, level: LogLevel, message: str) -> None: def log(self, level: LogLevel, message: str) -> None:
is_celery_runtime = False
with suppress(Exception):
is_celery_runtime = bool(getattr(current_task, "request", None))
current_logger = async_logger if is_celery_runtime else logger
match level: match level:
case LogLevel.DEBUG: case LogLevel.DEBUG:
if settings.DEBUG: if settings.DEBUG:
current_logger.debug(message) logger.debug(message)
case LogLevel.TRACE: case LogLevel.TRACE:
if settings.DEBUG: logger.debug(f"[TRACE] {message}")
current_logger.debug(f"[TRACE] {message}")
case LogLevel.INFO: case LogLevel.INFO:
current_logger.info(message) logger.info(message)
case LogLevel.WARNING: case LogLevel.WARNING:
current_logger.warning(message) logger.warning(message)
case LogLevel.ERROR: case LogLevel.ERROR:
current_logger.error(message) if settings.DEBUG:
logger.error(message, exc_info=True)
else:
logger.error(message)
case LogLevel.CRITICAL: case LogLevel.CRITICAL:
current_logger.critical(message) logger.critical(message, exc_info=True)
case _: case _:
current_logger.info(message) raise LoggingError("Wrong type of logging level passed: %s", level)
def save_response(self, data: dict[Any, Any] | list[Any]) -> None: def save_response(self, data: dict[Any, Any] | list[Any]) -> None:
with suppress(Exception): with suppress(Exception):

View file

@ -56,7 +56,7 @@ from evibes import settings
from evibes.settings import LANGUAGES from evibes.settings import LANGUAGES
from payments.serializers import TransactionProcessSerializer from payments.serializers import TransactionProcessSerializer
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
@cache_page(60 * 60 * 12) @cache_page(60 * 60 * 12)

View file

@ -129,7 +129,7 @@ from core.utils.seo_builders import (
) )
from payments.serializers import TransactionProcessSerializer from payments.serializers import TransactionProcessSerializer
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class EvibesViewSet(ModelViewSet): class EvibesViewSet(ModelViewSet):

View file

@ -16,7 +16,7 @@ from sentry_sdk import capture_exception
from evibes.settings import DEBUG from evibes.settings import DEBUG
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class CustomCommonMiddleware(CommonMiddleware): class CustomCommonMiddleware(CommonMiddleware):

View file

@ -1,25 +1,22 @@
import logging import logging
from evibes.settings.base import DEBUG from evibes.settings.base import DEBUG, EVIBES_VERSION
class SkipVariableDoesNotExistFilter(logging.Filter): class SkipVariableDoesNotExistFilter(logging.Filter):
def filter(self, record: logging.LogRecord) -> bool: def filter(self, record: logging.LogRecord) -> bool:
if record.exc_info: if record.exc_info:
exc_type, exc_instance, _ = record.exc_info exc_type, _, _ = record.exc_info
try: try:
if exc_type is not None: if exc_type is not None and exc_type.__name__ == "VariableDoesNotExist":
if exc_type.__name__ == "VariableDoesNotExist": return False
return False
else:
return True
except AttributeError: except AttributeError:
return True pass
return "VariableDoesNotExist" not in record.getMessage() return "VariableDoesNotExist" not in record.getMessage()
LOGGING = { LOGGING = {
"version": 1, "version": EVIBES_VERSION,
"disable_existing_loggers": False, "disable_existing_loggers": False,
"formatters": { "formatters": {
"color": { "color": {
@ -40,15 +37,9 @@ LOGGING = {
}, },
}, },
"filters": { "filters": {
"require_debug_false": { "require_debug_false": {"()": "django.utils.log.RequireDebugFalse"},
"()": "django.utils.log.RequireDebugFalse", "require_debug_true": {"()": "django.utils.log.RequireDebugTrue"},
}, "skip_variable_doesnotexist": {"()": "evibes.settings.logconfig.SkipVariableDoesNotExistFilter"},
"require_debug_true": {
"()": "django.utils.log.RequireDebugTrue",
},
"skip_variable_doesnotexist": {
"()": "evibes.settings.logconfig.SkipVariableDoesNotExistFilter",
},
}, },
"handlers": { "handlers": {
"console": { "console": {
@ -61,77 +52,59 @@ LOGGING = {
"class": "django.utils.log.AdminEmailHandler", "class": "django.utils.log.AdminEmailHandler",
}, },
}, },
"root": {
"handlers": ["console"],
"level": "DEBUG" if DEBUG else "INFO",
},
"loggers": { "loggers": {
"django": { "django": {
"handlers": [ "level": "INFO",
"console",
"mail_admins",
],
"level": "DEBUG" if DEBUG else "INFO",
"propagate": True, "propagate": True,
}, },
"django.request": {
"handlers": ["mail_admins"],
"level": "ERROR",
"propagate": False,
},
"django.server": { "django.server": {
"handlers": ["console"],
"level": "INFO", "level": "INFO",
"propagate": False, "propagate": False,
}, },
"django.db.backends": { "django.db.backends": {
"handlers": [
"console",
"mail_admins",
],
"level": "WARNING", "level": "WARNING",
"propagate": False, "propagate": True,
}, },
"django.template": { "django.template": {
"handlers": [
"console",
"mail_admins",
],
"level": "DEBUG" if DEBUG else "ERROR", "level": "DEBUG" if DEBUG else "ERROR",
"propagate": True,
"filters": ["skip_variable_doesnotexist"], "filters": ["skip_variable_doesnotexist"],
"propagate": True,
}, },
"uvicorn.access": { "uvicorn.access": {
"handlers": [ "handlers": ["console"],
"console",
],
"level": "DEBUG" if DEBUG else "INFO", "level": "DEBUG" if DEBUG else "INFO",
"propagate": False, "propagate": False,
}, },
"uvicorn.error": { "uvicorn.error": {
"handlers": [ "handlers": ["console"],
"console",
],
"level": "WARNING",
"propagate": False,
},
"django_elasticsearch_dsl": {
"handlers": [
"console",
],
"level": "WARNING", "level": "WARNING",
"propagate": False, "propagate": False,
}, },
"celery.app.trace": { "celery.app.trace": {
"handlers": [
"console",
],
"level": "DEBUG" if DEBUG else "INFO", "level": "DEBUG" if DEBUG else "INFO",
"propagate": False, "propagate": True,
}, },
"celery.worker.strategy": { "celery.worker.strategy": {
"handlers": [
"console",
],
"level": "DEBUG" if DEBUG else "INFO", "level": "DEBUG" if DEBUG else "INFO",
"propagate": False, "propagate": True,
},
"django_elasticsearch_dsl": {
"level": "WARNING",
"propagate": True,
}, },
"elastic_transport.transport": { "elastic_transport.transport": {
"handlers": [
"console",
],
"level": "ERROR", "level": "ERROR",
"propagate": False, "propagate": True,
}, },
}, },
} }

View file

@ -11,6 +11,10 @@ def create_object(module_name: str, class_name: str, *args: list[Any], **kwargs:
return cls(*args, **kwargs) return cls(*args, **kwargs)
class LoggingError(Exception):
pass
class LogLevel(Enum): class LogLevel(Enum):
DEBUG = "debug" DEBUG = "debug"
INFO = "info" INFO = "info"

View file

@ -9,7 +9,7 @@ from payments.models import Balance, Transaction, Gateway
from payments.utils.emailing import balance_deposit_email from payments.utils.emailing import balance_deposit_email
from vibes_auth.models import User from vibes_auth.models import User
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
# noinspection PyUnusedLocal # noinspection PyUnusedLocal

View file

@ -13,7 +13,7 @@ from payments.gateways import UnknownGatewayError
from payments.models import Transaction from payments.models import Transaction
from payments.serializers import DepositSerializer, TransactionProcessSerializer from payments.serializers import DepositSerializer, TransactionProcessSerializer
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
@extend_schema_view(**DEPOSIT_SCHEMA) @extend_schema_view(**DEPOSIT_SCHEMA)

View file

@ -25,7 +25,7 @@ from vibes_auth.serializers import (
from vibes_auth.utils.emailing import send_reset_password_email_task from vibes_auth.utils.emailing import send_reset_password_email_task
from vibes_auth.validators import is_valid_email, is_valid_phone_number from vibes_auth.validators import is_valid_email, is_valid_phone_number
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class CreateUser(BaseMutation): class CreateUser(BaseMutation):

View file

@ -7,7 +7,7 @@ from django.contrib.auth.hashers import make_password
from core.models import Address, Order from core.models import Address, Order
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class UserManager(BaseUserManager): class UserManager(BaseUserManager):

View file

@ -34,7 +34,7 @@ from evibes import settings
from vibes_auth.models import User from vibes_auth.models import User
from vibes_auth.validators import validate_phone_number from vibes_auth.validators import validate_phone_number
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
class UserSerializer(ModelSerializer): class UserSerializer(ModelSerializer):

View file

@ -21,7 +21,7 @@ from vibes_auth.serializers import (
TokenVerifySerializer, TokenVerifySerializer,
) )
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
@extend_schema_view(**TOKEN_OBTAIN_SCHEMA) @extend_schema_view(**TOKEN_OBTAIN_SCHEMA)

View file

@ -29,7 +29,7 @@ from vibes_auth.serializers import (
) )
from vibes_auth.utils.emailing import send_reset_password_email_task from vibes_auth.utils.emailing import send_reset_password_email_task
logger = logging.getLogger("django") logger = logging.getLogger(__name__)
# noinspection PyUnusedLocal # noinspection PyUnusedLocal