Skip to content

Commit

Permalink
⚡🔨 refactor(constants): Add new config_types and context placeholder
Browse files Browse the repository at this point in the history
- Added new configs in default settings
- Added new type alias for configs in config types
- Added context placeholder in format specifiers
- Updated conf to handle new formatters & configs
  • Loading branch information
MEHRSHAD-MIRSHEKARY committed Oct 4, 2024
1 parent b76afa1 commit 7df78d6
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 44 deletions.
4 changes: 4 additions & 0 deletions django_logging/constants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
from .default_settings import DefaultConsoleSettings, DefaultLoggingSettings
from .log_format_options import FORMAT_OPTIONS
from .log_format_specifiers import LOG_FORMAT_SPECIFIERS

# Used in settings.conf
ALLOWED_EXTRA_FILE_TYPES = ["JSON", "XML"]
ALLOWED_FILE_FORMAT_TYPES = ["JSON", "XML", "FLAT"]
32 changes: 24 additions & 8 deletions django_logging/constants/config_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,42 @@

FormatOption = Union[int, str]

# Type Aliases for configurations
LogFileFormatType = Literal["JSON", "XML", "FLAT", "LOG"]
LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
LogDir = str
LogLevels = List[LogLevel]
NotifierLogLevels = List[Literal["ERROR", "CRITICAL"]]
LogDateFormat = str


class LogEmailNotifierType(TypedDict, total=False):
class LogEmailNotifier(TypedDict, total=False):
ENABLE: bool
NOTIFY_ERROR: bool
NOTIFY_CRITICAL: bool
LOG_FORMAT: FormatOption
USE_TEMPLATE: bool


class LogFileFormatsType(TypedDict, total=False):
class LogFileFormats(TypedDict, total=False):
DEBUG: FormatOption
INFO: FormatOption
WARNING: FormatOption
ERROR: FormatOption
CRITICAL: FormatOption


# Type Aliases for other configurations
LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
LogDir = str
LogLevels = List[LogLevel]
NotifierLogLevels = List[Literal["ERROR", "CRITICAL"]]
LogDateFormat = str
class LogFileFormatTypes(TypedDict, total=False):
DEBUG: LogFileFormatType
INFO: LogFileFormatType
WARNING: LogFileFormatType
ERROR: LogFileFormatType
CRITICAL: LogFileFormatType


class ExtraLogFiles(TypedDict, total=False):
DEBUG: bool
INFO: bool
WARNING: bool
ERROR: bool
CRITICAL: bool
42 changes: 36 additions & 6 deletions django_logging/constants/default_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,33 @@
from typing import cast

from django_logging.constants.config_types import (
ExtraLogFiles,
FormatOption,
LogDateFormat,
LogDir,
LogEmailNotifierType,
LogFileFormatsType,
LogEmailNotifier,
LogFileFormats,
LogFileFormatTypes,
LogLevel,
LogLevels,
)


# pylint: disable=too-many-instance-attributes
@dataclass(frozen=True)
class DefaultLoggingSettings:
log_dir: LogDir = field(default_factory=lambda: os.path.join(os.getcwd(), "logs"))
log_dir_size_limit: int = 1024 # MB
log_levels: LogLevels = field(
default_factory=lambda: ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
)
log_date_format: LogDateFormat = "%Y-%m-%d %H:%M:%S"
auto_initialization_enable: bool = True
initialization_message_enable: bool = True
log_file_formats: LogFileFormatsType = field(
log_sql_queries_enable: bool = False
log_file_formats: LogFileFormats = field(
default_factory=lambda: cast(
LogFileFormatsType,
LogFileFormats,
{
"DEBUG": 1,
"INFO": 1,
Expand All @@ -34,10 +39,35 @@ class DefaultLoggingSettings:
},
)
)
log_file_format_types: LogFileFormatTypes = field(
default_factory=lambda: cast(
LogFileFormatTypes,
{
"DEBUG": "normal",
"INFO": "normal",
"WARNING": "normal",
"ERROR": "normal",
"CRITICAL": "normal",
},
)
)

extra_log_files: ExtraLogFiles = field(
default_factory=lambda: cast(
ExtraLogFiles,
{
"DEBUG": False,
"INFO": False,
"WARNING": False,
"ERROR": False,
"CRITICAL": False,
},
)
)

log_email_notifier: LogEmailNotifierType = field(
log_email_notifier: LogEmailNotifier = field(
default_factory=lambda: cast(
LogEmailNotifierType,
LogEmailNotifier,
{
"ENABLE": False,
"NOTIFY_ERROR": False,
Expand Down
1 change: 1 addition & 0 deletions django_logging/constants/log_format_specifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@
"thread",
"threadName",
"message",
"context",
]
68 changes: 53 additions & 15 deletions django_logging/settings/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
import os
from typing import Dict, List, Optional

from django_logging.constants import FORMAT_OPTIONS, DefaultLoggingSettings
from django_logging.constants import (
ALLOWED_EXTRA_FILE_TYPES,
ALLOWED_FILE_FORMAT_TYPES,
FORMAT_OPTIONS,
DefaultLoggingSettings,
)
from django_logging.constants.config_types import (
ExtraLogFiles,
FormatOption,
LogDateFormat,
LogDir,
LogFileFormatsType,
LogFileFormats,
LogFileFormatTypes,
LogLevel,
LogLevels,
NotifierLogLevels,
Expand All @@ -30,7 +37,9 @@ def __init__(
self,
log_levels: LogLevels,
log_dir: LogDir,
log_file_formats: LogFileFormatsType,
log_file_formats: LogFileFormats,
log_file_format_types: LogFileFormatTypes,
extra_log_files: ExtraLogFiles,
console_level: LogLevel,
console_format: FormatOption,
colorize_console: bool,
Expand All @@ -53,8 +62,10 @@ def __init__(
self.email_notifier_log_format = self.resolve_format(
log_email_notifier_log_format
)
self.log_file_format_types = log_file_format_types
self.extra_log_files = extra_log_files

def _resolve_file_formats(self, log_file_formats: LogFileFormatsType) -> Dict:
def _resolve_file_formats(self, log_file_formats: LogFileFormats) -> Dict:
resolved_formats = {}
for level in self.log_levels:
format_option = log_file_formats.get(level, None)
Expand Down Expand Up @@ -116,9 +127,22 @@ def __init__(self, log_config: LogConfig) -> None:
def create_log_files(self) -> None:
"""Creates log files based on the log levels in the configuration."""
for log_level in self.log_config.log_levels:
log_file_path = os.path.join(
self.log_config.log_dir, f"{log_level.lower()}.log"
)
fmt_type = self.log_config.log_file_format_types.get(log_level, "").lower()
extra_file = self.log_config.extra_log_files.get(log_level, False)

if extra_file and fmt_type.upper() in ALLOWED_EXTRA_FILE_TYPES:
# Use separate files for extra file format structure
log_file_path = os.path.join(
self.log_config.log_dir,
fmt_type,
f"{log_level.lower()}.{fmt_type}",
)
else:
# Use regular log file for normal, JSON, or XML
log_file_path = os.path.join(
self.log_config.log_dir, f"{log_level.lower()}.log"
)

os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
if not os.path.exists(log_file_path):
with open(log_file_path, "w", encoding="utf-8"):
Expand All @@ -139,28 +163,30 @@ def get_log_file(self, log_level: LogLevel) -> Optional[str]:

def set_conf(self) -> None:
"""Sets the logging configuration using the generated log files."""
formatters = {}
default_settings = DefaultLoggingSettings()
handlers = {
level.lower(): {
"class": "logging.FileHandler",
"filename": log_file,
"formatter": f"{level.lower()}",
"level": level,
"filters": [level.lower()],
"filters": [level.lower(), "context_var_filter"],
}
for level, log_file in self.log_files.items()
}
handlers["console"] = {
"class": "logging.StreamHandler",
"formatter": "console",
"level": self.log_config.console_level,
"filters": ["context_var_filter"],
}
email_handler = {
f"email_{level.lower()}": {
"class": "django_logging.handlers.EmailHandler",
"formatter": "email",
"level": level,
"filters": [level.lower()],
"filters": [level.lower(), "context_var_filter"],
}
for level in self.log_config.email_notifier_log_levels
if level
Expand All @@ -176,13 +202,25 @@ def set_conf(self) -> None:
for level in default_settings.log_levels
}

formatters = {
level.lower(): {
"format": self.log_config.log_file_formats[level],
"datefmt": self.log_config.log_date_format,
}
for level in self.log_config.log_levels
# ContextVarFilter for context variables
filters["context_var_filter"] = {
"()": "django_logging.filters.ContextVarFilter",
}

for level in self.log_config.log_levels:
formatter = {
level.lower(): {
"format": self.log_config.log_file_formats[level],
"datefmt": self.log_config.log_date_format,
}
}
fmt_type = self.log_config.log_file_format_types.get(level, "None").upper()
if fmt_type in ALLOWED_FILE_FORMAT_TYPES:
formatter[level.lower()].update(
{"()": f"django_logging.formatters.{fmt_type}Formatter"}
)
formatters.update(formatter)

formatters["console"] = {
"format": self.log_config.console_format,
"datefmt": self.log_config.log_date_format,
Expand Down
44 changes: 43 additions & 1 deletion django_logging/utils/get_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,22 @@ def get_config(extra_info: bool = False) -> Dict:
logging_defaults = DefaultLoggingSettings()
console_defaults = DefaultConsoleSettings()

if not isinstance(log_settings, dict):
raise ValueError("DJANGO_LOGGING must be a dictionary with configs as keys")

log_levels = log_settings.get("LOG_FILE_LEVELS", logging_defaults.log_levels)
log_dir = log_settings.get(
"LOG_DIR", os.path.join(os.getcwd(), logging_defaults.log_dir)
)
log_file_formats = log_settings.get(
"LOG_FILE_FORMATS", logging_defaults.log_file_formats
)
log_file_format_types = log_settings.get(
"LOG_FILE_FORMAT_TYPES", logging_defaults.log_file_format_types
)
extra_log_files = log_settings.get(
"EXTRA_LOG_FILES", logging_defaults.extra_log_files
)
console_level = log_settings.get(
"LOG_CONSOLE_LEVEL", console_defaults.log_console_level
)
Expand Down Expand Up @@ -52,6 +61,8 @@ def get_config(extra_info: bool = False) -> Dict:
"log_levels": log_levels,
"log_dir": log_dir,
"log_file_formats": log_file_formats,
"log_file_format_types": log_file_format_types,
"extra_log_files": extra_log_files,
"console_level": console_level,
"console_format": console_format,
"colorize_console": colorize_console,
Expand All @@ -61,7 +72,9 @@ def get_config(extra_info: bool = False) -> Dict:
"log_email_notifier_log_format": log_email_notifier_log_format,
}
if extra_info:
config.update({"log_email_notifier": log_email_notifier})
config.update(
{"log_email_notifier": log_email_notifier, "log_settings": log_settings}
)

return config

Expand Down Expand Up @@ -115,3 +128,32 @@ def is_initialization_message_enabled() -> bool:
return log_settings.get(
"INITIALIZATION_MESSAGE_ENABLE", defaults.initialization_message_enable
)


def is_log_sql_queries_enabled() -> bool:
"""Check if the LOG_SQL_QUERIES_ENABLE for the logging system is set to
True in Django settings.
Returns:
bool: True if LOG_SQL_QUERIES_ENABLE, False otherwise.
Defaults to False if not specified.
"""
log_settings = getattr(settings, "DJANGO_LOGGING", {})
defaults = DefaultLoggingSettings()

return log_settings.get("LOG_SQL_QUERIES_ENABLE", defaults.log_sql_queries_enable)


def get_log_dir_size_limit() -> int:
"""Check for the LOG_DIR_SIZE_LIMIT for managing the log dir size.
Returns:
int: the limit of log directory size.
Defaults to 1024 MB if not specified.
"""
log_settings = getattr(settings, "DJANGO_LOGGING", {})
defaults = DefaultLoggingSettings()

return log_settings.get("LOG_DIR_SIZE_LIMIT", defaults.log_dir_size_limit)
Loading

0 comments on commit 7df78d6

Please sign in to comment.