Skip to content

Commit

Permalink
Merge pull request #38 from ARYAN-NIKNEZHAD/develop
Browse files Browse the repository at this point in the history
Develop to Main
  • Loading branch information
ARYAN-NIKNEZHAD authored Aug 24, 2024
2 parents 0459616 + a385eb9 commit de2b375
Show file tree
Hide file tree
Showing 47 changed files with 3,044 additions and 160 deletions.
8 changes: 4 additions & 4 deletions django_logging/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ class DjangoLoggingConfig(AppConfig):

def ready(self) -> None:
from django_logging.settings import checks
from django_logging.utils.setup_logging import set_logging
from django_logging.utils.get_config import get_conf
conf = get_conf()
from django_logging.utils.set_conf import set_config
from django_logging.utils.get_conf import get_config
conf = get_config()

# Set the logging configuration
set_logging(*conf)
set_config(*conf)
6 changes: 3 additions & 3 deletions django_logging/constants/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .format_options import FORMAT_OPTIONS
from .defaults import DefaultLoggingSettings
from .format_specifiers import LOG_FORMAT_SPECIFIERS
from .log_format_options import FORMAT_OPTIONS
from .default_settings import DefaultLoggingSettings
from .log_format_specifiers import LOG_FORMAT_SPECIFIERS
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ class LogEmailNotifierType(TypedDict, total=False):
USE_TEMPLATE: bool


LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]


class LogFileFormatsType(TypedDict, total=False):
DEBUG: FormatOption
INFO: FormatOption
Expand All @@ -23,11 +20,8 @@ class LogFileFormatsType(TypedDict, total=False):


# Type Aliases for other configurations
LOG_DIR_TYPE = str
LOG_LEVELS_TYPE = List[str]
LOG_DATE_FORMAT_TYPE = str
AUTO_INITIALIZATION_ENABLE_TYPE = bool
INITIALIZATION_MESSAGE_ENABLE_TYPE = bool
LOG_CONSOLE_LEVEL_TYPE = LogLevel
LOG_CONSOLE_FORMAT_TYPE = FormatOption
LOG_CONSOLE_COLORIZE_TYPE = bool
LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
LogDir = str
LogLevels = List[LogLevel]
NotifierLogLevels = List[Literal["ERROR", "CRITICAL"]]
LogDateFormat = str
51 changes: 51 additions & 0 deletions django_logging/constants/default_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import os
from typing import cast
from dataclasses import dataclass, field

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


@dataclass(frozen=True)
class DefaultLoggingSettings:
log_dir: LogDir = field(default_factory=lambda: os.path.join(os.getcwd(), "logs"))
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(
default_factory=lambda: cast(
LogFileFormatsType,
{
"DEBUG": 1,
"INFO": 1,
"WARNING": 1,
"ERROR": 1,
"CRITICAL": 1,
},
)
)
log_console_level: LogLevel = "DEBUG"
log_console_format: FormatOption = 1
log_console_colorize: bool = True
log_email_notifier: LogEmailNotifierType = field(
default_factory=lambda: cast(
LogEmailNotifierType,
{
"ENABLE": False,
"NOTIFY_ERROR": False,
"NOTIFY_CRITICAL": False,
"LOG_FORMAT": 1,
"USE_TEMPLATE": True,
},
)
)
48 changes: 0 additions & 48 deletions django_logging/constants/defaults.py

This file was deleted.

File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
"EMAIL_HOST_PASSWORD",
"EMAIL_USE_TLS",
"DEFAULT_FROM_EMAIL",
"ADMIN_EMAIL"
]

NOTIFIER_EXTRA_REQUIRED_SETTING = "ADMIN_EMAIL"
1 change: 1 addition & 0 deletions django_logging/filters/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .log_level_filter import LoggingLevelFilter
File renamed without changes.
2 changes: 1 addition & 1 deletion django_logging/formatters/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .colorized_formatter import ColorizedFormatter
from .colored_formatter import ColoredFormatter
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import logging
from logging import LogRecord, Formatter
from django_logging.settings.conf import LogConfig
from django_logging.utils.colorizer import colorize_log_format
from django_logging.utils.console_colorizer import colorize_log_format


class ColorizedFormatter(logging.Formatter):
def format(self, record):
class ColoredFormatter(Formatter):
def format(self, record: LogRecord) -> str:
original_format = self._style._fmt

# checks that the format does not have any color it's self
Expand Down
22 changes: 10 additions & 12 deletions django_logging/handlers/email_handler.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import logging
from logging import Handler, LogRecord
from typing import Optional

from django.conf import settings
from django.http import HttpRequest
from django.template import engines
from django.utils.timezone import now
from django_logging.utils.email_notifier import send_email_async
from django_logging.utils.get_config import use_email_notifier_template
from django_logging.utils.log_email_notifier.notifier import send_email_async
from django_logging.utils.get_conf import use_email_notifier_template
from django_logging.middleware import RequestLogMiddleware


class EmailHandler(logging.Handler):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.include_html = use_email_notifier_template()

def emit(self, record):
class EmailHandler(Handler):
def emit(self, record: LogRecord) -> None:
try:
request = getattr(record, "request", None)
log_entry = self.format(record)

if self.include_html:
if use_email_notifier_template():
email_body = self.render_template(log_entry, request)
else:
email_body = log_entry
Expand All @@ -31,8 +29,8 @@ def emit(self, record):

@staticmethod
def render_template(
log_entry, request=None, template_path="email_notifier_template.html"
):
log_entry: str, request: Optional[HttpRequest] = None, template_path: str = "email_notifier_template.html"
) -> str:
django_engine = engines["django"]
template = django_engine.get_template(template_path)

Expand Down
25 changes: 16 additions & 9 deletions django_logging/management/commands/send_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
import shutil
import tempfile
import logging
from argparse import ArgumentParser
from typing import Dict, Tuple

from django.core.exceptions import ImproperlyConfigured
from django.core.mail import EmailMessage
from django.core.management.base import BaseCommand
from django.conf import settings

from django_logging.constants.config_types import LogDir
from django_logging.validators.email_settings_validator import check_email_settings
from django_logging.constants import DefaultLoggingSettings

logger = logging.getLogger(__name__)

Expand All @@ -25,7 +29,7 @@ class Command(BaseCommand):

help = "Send log folder to the specified email address"

def add_arguments(self, parser):
def add_arguments(self, parser: ArgumentParser) -> None:
"""
Add custom command arguments.
Expand All @@ -36,7 +40,7 @@ def add_arguments(self, parser):
"email", type=str, help="The email address to send the logs to"
)

def handle(self, *args, **kwargs):
def handle(self, *args: Tuple, **kwargs: Dict) -> None:
"""
The main entry point for the command.
Expand All @@ -46,8 +50,10 @@ def handle(self, *args, **kwargs):
"""
email = kwargs["email"]

log_dir = settings.DJANGO_LOGGING.get(
"LOG_DIR", os.path.join(os.getcwd(), "logs")
default_settings = DefaultLoggingSettings()

log_dir: LogDir = settings.DJANGO_LOGGING.get(
"LOG_DIR", os.path.join(os.getcwd(), default_settings.log_dir)
)

if not os.path.exists(log_dir):
Expand Down Expand Up @@ -86,17 +92,18 @@ def handle(self, *args, **kwargs):
self.stdout.write(self.style.ERROR(f"Failed to send logs: {e}"))
logger.error(f"Failed to send logs: {e}")
finally:
# Clean up the temporary file
os.remove(zip_path)
logger.info("Temporary zip file cleaned up successfully.")
# Clean up the temporary file if exists
if os.path.exists(zip_path):
os.remove(zip_path)
logger.info("Temporary zip file cleaned up successfully.")

def validate_email_settings(self):
def validate_email_settings(self) -> None:
"""
Check if all required email settings are present in the settings file.
Raises ImproperlyConfigured if any of the required email settings are missing.
"""
errors = check_email_settings()
errors = check_email_settings(require_admin_email=False)
if errors:
logger.error(errors)
raise ImproperlyConfigured(errors)
43 changes: 25 additions & 18 deletions django_logging/settings/conf.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import logging
import logging.config
import os
from typing import List, Dict, Optional, Union
from typing import List, Dict, Optional

from django_logging.constants import FORMAT_OPTIONS, DefaultLoggingSettings
from django_logging.filters.level_filter import LoggingLevelFilter
from django_logging.constants.config_types import (
LogLevels,
LogDir,
LogFileFormatsType,
LogDateFormat,
FormatOption,
LogLevel,
NotifierLogLevels
)
from django_logging.filters.log_level_filter import LoggingLevelFilter


class LogConfig:
Expand All @@ -18,16 +27,16 @@ class LogConfig:

def __init__(
self,
log_levels: List[str],
log_dir: str,
log_file_formats: Dict[str, Union[int, str]],
console_level: str,
console_format: Optional[Union[int, str]],
log_levels: LogLevels,
log_dir: LogDir,
log_file_formats: LogFileFormatsType,
console_level: LogLevel,
console_format: FormatOption,
colorize_console: bool,
log_date_format: str,
log_date_format: LogDateFormat,
log_email_notifier_enable: bool,
log_email_notifier_log_levels: List[str],
log_email_notifier_log_format: Union[int, str],
log_email_notifier_log_levels: NotifierLogLevels,
log_email_notifier_log_format: FormatOption,
) -> None:

self.log_levels = log_levels
Expand All @@ -45,9 +54,7 @@ def __init__(
log_email_notifier_log_format
)

def _resolve_file_formats(
self, log_file_formats: Dict[str, Union[int, str]]
) -> Dict:
def _resolve_file_formats(self, log_file_formats: LogFileFormatsType) -> Dict:
resolved_formats = {}
for level in self.log_levels:
format_option = log_file_formats.get(level, None)
Expand Down Expand Up @@ -77,7 +84,7 @@ def remove_ansi_escape_sequences(log_message: str) -> str:
return ansi_escape.sub("", log_message)

@staticmethod
def resolve_format(_format: Union[int, str], use_colors: bool = False) -> str:
def resolve_format(_format: FormatOption, use_colors: bool = False) -> str:
if _format:
if isinstance(_format, int):
resolved_format = FORMAT_OPTIONS.get(_format, FORMAT_OPTIONS[1])
Expand Down Expand Up @@ -118,7 +125,7 @@ def create_log_files(self) -> None:
open(log_file_path, "w").close()
self.log_files[log_level] = log_file_path

def get_log_file(self, log_level: str) -> Optional[str]:
def get_log_file(self, log_level: LogLevel) -> Optional[str]:
"""
Retrieves the file path for a given log level.
Expand All @@ -132,7 +139,7 @@ def get_log_file(self, log_level: str) -> Optional[str]:

def set_conf(self) -> None:
"""Sets the logging configuration using the generated log files."""
defaults = DefaultLoggingSettings()
default_settings = DefaultLoggingSettings()
handlers = {
level.lower(): {
"class": "logging.FileHandler",
Expand Down Expand Up @@ -166,7 +173,7 @@ def set_conf(self) -> None:
"()": LoggingLevelFilter,
"logging_level": getattr(logging, level),
}
for level in defaults.log_levels
for level in default_settings.log_levels
}

formatters = {
Expand All @@ -182,7 +189,7 @@ def set_conf(self) -> None:
}
if self.log_config.colorize_console:
formatters["console"].update(
{"()": "django_logging.formatters.ColorizedFormatter"}
{"()": "django_logging.formatters.ColoredFormatter"}
)

formatters["email"] = {
Expand Down
Empty file.
Empty file.
Loading

0 comments on commit de2b375

Please sign in to comment.