Skip to content

Commit

Permalink
Merge pull request #100 from MEHRSHAD-MIRSHEKARY/update/conf
Browse files Browse the repository at this point in the history
⚡ 🔨 Update configs
  • Loading branch information
ARYAN-NIKNEZHAD authored Oct 4, 2024
2 parents 5e7a5cd + 055424a commit 69b0ecd
Show file tree
Hide file tree
Showing 13 changed files with 246 additions and 61 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"]
19 changes: 19 additions & 0 deletions django_logging/constants/ansi_colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class AnsiColors:
BRIGHT_MAGENTA: str = "\033[0;95m"
BRIGHT_CYAN: str = "\033[0;96m"
BRIGHT_WHITE: str = "\033[0;97m"
PINK: str = "\033[38;5;213m"
LIGHT_PURPLE = "\033[38;5;129m"
BLACK_BACKGROUND: str = "\033[40m"
RED_BACKGROUND: str = "\033[41m"
GREEN_BACKGROUND: str = "\033[42m"
Expand All @@ -30,6 +32,23 @@ class AnsiColors:
MAGENTA_BACKGROUND: str = "\033[45m"
CYAN_BACKGROUND: str = "\033[46m"
WHITE_BACKGROUND: str = "\033[47m"
BRIGHT_BLACK_BACKGROUND: str = "\033[100m"
BRIGHT_RED_BACKGROUND: str = "\033[101m"
BRIGHT_GREEN_BACKGROUND: str = "\033[102m"
BRIGHT_YELLOW_BACKGROUND: str = "\033[103m"
BRIGHT_BLUE_BACKGROUND: str = "\033[104m"
BRIGHT_MAGENTA_BACKGROUND: str = "\033[105m"
BRIGHT_CYAN_BACKGROUND: str = "\033[106m"
BRIGHT_WHITE_BACKGROUND: str = "\033[107m"

BOLD: str = "\033[1m"
DIM: str = "\033[2m"
ITALIC: str = "\033[3m"
BOLD_ITALIC: str = "\033[1;3m"
UNDERLINE: str = "\033[4m"
BLINK: str = "\033[5m"
INVERT: str = "\033[7m"
STRIKETHROUGH: str = "\033[9m"


# Mapping log levels to ANSI colors
Expand Down
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
33 changes: 20 additions & 13 deletions django_logging/constants/log_format_options.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
FORMAT_OPTIONS = {
1: "%(levelname)s | %(asctime)s | %(module)s | %(message)s",
2: "%(levelname)s | %(asctime)s | %(message)s",
3: "%(levelname)s | %(message)s",
4: "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
5: "%(levelname)s | %(message)s | [in %(pathname)s:%(lineno)d]",
6: "%(asctime)s | %(levelname)s | %(message)s",
7: "%(levelname)s | %(asctime)s | in %(module)s: %(message)s",
8: "%(levelname)s | %(message)s | [%(filename)s:%(lineno)d]",
9: "[%(asctime)s] | %(levelname)s | in %(module)s: %(message)s",
10: "%(asctime)s | %(processName)s | %(name)s | %(levelname)s | %(message)s",
11: "%(asctime)s | %(threadName)s | %(name)s | %(levelname)s | %(message)s",
12: "%(levelname)s | [%(asctime)s] | (%(filename)s:%(lineno)d) | %(message)s",
13: "%(levelname)s | [%(asctime)s] | {%(name)s} | (%(filename)s:%(lineno)d): %(message)s",
1: "%(levelname)s | %(asctime)s | %(module)s | %(message)s | %(context)s",
2: "%(levelname)s | %(asctime)s | %(context)s | %(message)s",
3: "%(levelname)s | %(context)s | %(message)s",
4: "%(context)s | %(asctime)s - %(name)s - %(levelname)s - %(message)s",
5: "%(levelname)s | %(message)s | %(context)s | [in %(pathname)s:%(lineno)d]",
6: "%(asctime)s | %(context)s | %(levelname)s | %(message)s",
7: "%(levelname)s | %(asctime)s | %(context)s | in %(module)s: %(message)s",
8: "%(levelname)s | %(context)s | %(message)s | [%(filename)s:%(lineno)d]",
9: "[%(asctime)s] | %(levelname)s | %(context)s | in %(module)s: %(message)s",
10: "%(asctime)s | %(processName)s | %(context)s | %(name)s | %(levelname)s | %(message)s",
11: "%(asctime)s | %(context)s | %(threadName)s | %(name)s | %(levelname)s | %(message)s",
12: "%(levelname)s | [%(asctime)s] | %(context)s | (%(filename)s:%(lineno)d) | %(message)s",
13: "%(levelname)s | [%(asctime)s] | %(context)s | {%(name)s} | (%(filename)s:%(lineno)d): %(message)s",
14: "[%(asctime)s] | %(levelname)s | %(context)s | %(name)s | %(module)s | %(message)s",
15: "%(levelname)s | %(context)s | %(asctime)s | %(filename)s:%(lineno)d | %(message)s",
16: "%(levelname)s | %(context)s | %(message)s | [%(asctime)s] | %(module)s",
17: "%(levelname)s | %(context)s | [%(asctime)s] | %(process)d | %(message)s",
18: "%(levelname)s | %(context)s | %(asctime)s | %(name)s | %(message)s",
19: "%(levelname)s | %(asctime)s | %(context)s | %(module)s:%(lineno)d | %(message)s",
20: "[%(asctime)s] | %(levelname)s | %(context)s | %(thread)d | %(message)s",
}
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",
]
8 changes: 4 additions & 4 deletions django_logging/decorators/execution_tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import os
import time
from functools import wraps
from typing import Callable, Optional
from typing import Any, Callable, Optional

from django.conf import settings
from django.db import connection

from django_logging.utils.time import format_elapsed_time
from django_logging.validators.config_validators import (
validate_boolean_setting,
validate_integer_setting,
Expand Down Expand Up @@ -69,7 +70,7 @@ def execution_tracker(

def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs):
def wrapper(*args: Any, **kwargs: Any) -> Any:
start_time = time.time()

# Check if DEBUG is True and log_queries is enabled; if not, ignore query tracking
Expand All @@ -82,15 +83,14 @@ def wrapper(*args, **kwargs):

# Calculate execution time
elapsed_time = time.time() - start_time
minutes, seconds = divmod(elapsed_time, 60)

# Get detailed function information
module_name = func.__module__
function_name = func.__qualname__
file_path = os.path.abspath(func.__code__.co_filename)
line_number = func.__code__.co_firstlineno

time_message = f"{minutes} minute(s) and {seconds:.4f} second(s)"
time_message = format_elapsed_time(elapsed_time)
log_message = (
f"Performance Metrics for Function: '{function_name}'\n"
f" Module: {module_name}\n"
Expand Down
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
Loading

0 comments on commit 69b0ecd

Please sign in to comment.