Skip to content

Commit

Permalink
Merge pull request #42 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 28, 2024
2 parents ea4c3ad + f1ca2bc commit 7774375
Show file tree
Hide file tree
Showing 37 changed files with 372 additions and 267 deletions.
5 changes: 3 additions & 2 deletions django_logging/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ class DjangoLoggingConfig(AppConfig):

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

conf = get_config()

# Set the logging configuration
set_config(*conf)
set_config(**conf)
2 changes: 1 addition & 1 deletion django_logging/constants/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .default_settings import DefaultConsoleSettings, DefaultLoggingSettings
from .log_format_options import FORMAT_OPTIONS
from .default_settings import DefaultLoggingSettings
from .log_format_specifiers import LOG_FORMAT_SPECIFIERS
2 changes: 1 addition & 1 deletion django_logging/constants/config_types.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TypedDict, Union, List, Literal
from typing import List, Literal, TypedDict, Union

FormatOption = Union[int, str]

Expand Down
21 changes: 13 additions & 8 deletions django_logging/constants/default_settings.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import os
from typing import cast
from dataclasses import dataclass, field
from typing import cast

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


Expand All @@ -34,9 +34,7 @@ class DefaultLoggingSettings:
},
)
)
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,
Expand All @@ -49,3 +47,10 @@ class DefaultLoggingSettings:
},
)
)


@dataclass
class DefaultConsoleSettings:
log_console_level: LogLevel = "DEBUG"
log_console_format: FormatOption = 1
log_console_colorize: bool = True
3 changes: 2 additions & 1 deletion django_logging/formatters/colored_formatter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from logging import LogRecord, Formatter
from logging import Formatter, LogRecord

from django_logging.settings.conf import LogConfig
from django_logging.utils.console_colorizer import colorize_log_format

Expand Down
11 changes: 7 additions & 4 deletions django_logging/handlers/email_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
from django.http import HttpRequest
from django.template import engines
from django.utils.timezone import now
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
from django_logging.utils.get_conf import use_email_notifier_template
from django_logging.utils.log_email_notifier.notifier import send_email_async


class EmailHandler(Handler):
Expand All @@ -24,12 +25,14 @@ def emit(self, record: LogRecord) -> None:
subject = f"New Log Record: {record.levelname}"
send_email_async(subject, email_body, [settings.ADMIN_EMAIL])

except Exception as e:
except Exception: # pylint: disable=W0718
self.handleError(record)

@staticmethod
def render_template(
log_entry: str, request: Optional[HttpRequest] = None, template_path: str = "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
14 changes: 7 additions & 7 deletions django_logging/management/commands/send_logs.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import logging
import os
import shutil
import tempfile
import logging
from argparse import ArgumentParser
from typing import Dict, Tuple

from django.conf import settings
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 import DefaultLoggingSettings
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 Down Expand Up @@ -60,7 +60,7 @@ def handle(self, *args: Tuple, **kwargs: Dict) -> None:
self.stdout.write(
self.style.ERROR(f'Log directory "{log_dir}" does not exist.')
)
logger.error(f'Log directory "{log_dir}" does not exist.')
logger.error("Log directory '%s' does not exist.", log_dir)
return

self.validate_email_settings()
Expand All @@ -87,10 +87,10 @@ def handle(self, *args: Tuple, **kwargs: Dict) -> None:
try:
email_message.send()
self.stdout.write(self.style.SUCCESS(f"Logs sent successfully to {email}."))
logger.info(f"Logs sent successfully to {email}.")
except Exception as e:
logger.info("Logs sent successfully to %s.", email)
except Exception as e: # pylint: disable=W0718
self.stdout.write(self.style.ERROR(f"Failed to send logs: {e}"))
logger.error(f"Failed to send logs: {e}")
logger.error("Failed to send logs: %s", e)
finally:
# Clean up the temporary file if exists
if os.path.exists(zip_path):
Expand Down
12 changes: 8 additions & 4 deletions django_logging/middleware/request_middleware.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import logging
from django.contrib.auth import get_user_model
from django.http import HttpResponse, HttpRequest
from typing import Callable

from django.contrib.auth import get_user_model
from django.http import HttpRequest, HttpResponse

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -55,8 +56,11 @@ def __call__(self, request: HttpRequest) -> HttpResponse:
request.browser_type = user_agent

logger.info(
f"Request Info: (request_path: {request.path}, user: {user},"
f"\nIP: {ip_address}, user_agent: {user_agent})"
"Request Info: (request_path: %s, user: %s," "\nIP: %s, user_agent: %s)",
request.path,
user,
ip_address,
user_agent,
)

return response
Expand Down
77 changes: 34 additions & 43 deletions django_logging/settings/checks.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
from django.conf import settings
from typing import Any, Dict, List

from django.core.checks import Error, register
from typing import Dict, Any, List

from django_logging.constants import DefaultLoggingSettings

from django_logging.utils.get_conf import (
get_config,
is_auto_initialization_enabled,
is_initialization_message_enabled,
)
from django_logging.validators.config_validators import (
validate_directory,
validate_log_levels,
validate_boolean_setting,
validate_date_format,
validate_format_option,
validate_directory,
validate_email_notifier,
validate_boolean_setting,
validate_format_option,
validate_log_levels,
)
from django_logging.validators.email_settings_validator import check_email_settings

Expand All @@ -19,30 +23,31 @@
def check_logging_settings(app_configs: Dict[str, Any], **kwargs: Any) -> List[Error]:
errors: List[Error] = []

log_settings = getattr(settings, "DJANGO_LOGGING", {})
defaults = DefaultLoggingSettings()
log_settings = get_config(extra_info=True)
logging_defaults = DefaultLoggingSettings()

# Validate LOG_DIR
log_dir = log_settings.get("LOG_DIR", defaults.log_dir)
errors.extend(validate_directory(log_dir, "LOG_DIR"))
errors.extend(validate_directory(log_settings.get("log_dir"), "LOG_DIR")) # type: ignore

# Validate LOG_FILE_LEVELS
log_file_levels = log_settings.get("LOG_FILE_LEVELS", defaults.log_levels)
log_file_levels = log_settings.get("log_levels")
errors.extend(
validate_log_levels(
log_file_levels, "LOG_FILE_LEVELS", defaults.log_levels
log_file_levels, "LOG_FILE_LEVELS", logging_defaults.log_levels # type: ignore
)
)

# Validate LOG_FILE_FORMATS
log_file_formats = log_settings.get("LOG_FILE_FORMATS", defaults.log_file_formats)
log_file_formats = log_settings.get(
"log_file_formats", logging_defaults.log_file_formats
)
if isinstance(log_file_formats, dict):
for level, format_option in log_file_formats.items():
if level not in defaults.log_levels:
if level not in logging_defaults.log_levels:
errors.append(
Error(
f"Invalid log level '{level}' in LOG_FILE_FORMATS.",
hint=f"Valid log levels are: {defaults.log_levels}.",
hint=f"Valid log levels are: {logging_defaults.log_levels}.",
id="django_logging.E019_LOG_FILE_FORMATS",
)
)
Expand All @@ -59,60 +64,46 @@ def check_logging_settings(app_configs: Dict[str, Any], **kwargs: Any) -> List[E
)

# Validate LOG_CONSOLE_FORMAT
log_console_format = log_settings.get(
"LOG_CONSOLE_FORMAT", defaults.log_console_format
)
errors.extend(validate_format_option(log_console_format, "LOG_CONSOLE_FORMAT"))
log_console_format = log_settings.get("console_format")
errors.extend(validate_format_option(log_console_format, "LOG_CONSOLE_FORMAT")) # type: ignore

# Validate LOG_CONSOLE_LEVEL
log_console_level = log_settings.get(
"LOG_CONSOLE_LEVEL", defaults.log_console_level
)
log_console_level = log_settings.get("console_level")
errors.extend(
validate_log_levels(
[log_console_level], "LOG_CONSOLE_LEVEL", defaults.log_levels
[log_console_level], "LOG_CONSOLE_LEVEL", logging_defaults.log_levels # type: ignore
)
)

# Validate LOG_CONSOLE_COLORIZE
log_console_colorize = log_settings.get(
"LOG_CONSOLE_COLORIZE", defaults.log_console_colorize
)
log_console_colorize = log_settings.get("colorize_console")
errors.extend(
validate_boolean_setting(log_console_colorize, "LOG_CONSOLE_COLORIZE")
validate_boolean_setting(log_console_colorize, "LOG_CONSOLE_COLORIZE") # type: ignore
)

# Validate LOG_DATE_FORMAT
log_date_format = log_settings.get("LOG_DATE_FORMAT", defaults.log_date_format)
errors.extend(validate_date_format(log_date_format, "LOG_DATE_FORMAT"))
log_date_format = log_settings.get("log_date_format")
errors.extend(validate_date_format(log_date_format, "LOG_DATE_FORMAT")) # type: ignore

# Validate AUTO_INITIALIZATION_ENABLE
auto_initialization_enable = log_settings.get(
"AUTO_INITIALIZATION_ENABLE", defaults.auto_initialization_enable
)
errors.extend(
validate_boolean_setting(
auto_initialization_enable, "AUTO_INITIALIZATION_ENABLE"
is_auto_initialization_enabled(), "AUTO_INITIALIZATION_ENABLE"
)
)

# Validate INITIALIZATION_MESSAGE_ENABLE
initialization_message_enable = log_settings.get(
"INITIALIZATION_MESSAGE_ENABLE", defaults.initialization_message_enable
)
errors.extend(
validate_boolean_setting(
initialization_message_enable, "INITIALIZATION_MESSAGE_ENABLE"
is_initialization_message_enabled(), "INITIALIZATION_MESSAGE_ENABLE"
)
)

# Validate LOG_EMAIL_NOTIFIER
log_email_notifier = log_settings.get(
"LOG_EMAIL_NOTIFIER", defaults.log_email_notifier
)
errors.extend(validate_email_notifier(log_email_notifier))
log_email_notifier = log_settings.get("log_email_notifier")
errors.extend(validate_email_notifier(log_email_notifier)) # type: ignore

if log_email_notifier.get("ENABLE", False):
if log_email_notifier.get("ENABLE", False): # type: ignore
errors.extend(check_email_settings())

return errors
18 changes: 11 additions & 7 deletions django_logging/settings/conf.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import logging
import logging.config
import os
from typing import List, Dict, Optional
from typing import Dict, List, Optional

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


# pylint: disable=too-many-instance-attributes, too-many-arguments
class LogConfig:
"""
Configuration class for django_logging.
Expand Down Expand Up @@ -85,11 +86,13 @@ def remove_ansi_escape_sequences(log_message: str) -> str:

@staticmethod
def resolve_format(_format: FormatOption, use_colors: bool = False) -> str:
resolved_format: str = ""
if _format:
if isinstance(_format, int):
resolved_format = FORMAT_OPTIONS.get(_format, FORMAT_OPTIONS[1])
else:
elif isinstance(_format, str):
resolved_format = _format

else:
resolved_format = FORMAT_OPTIONS[1]

Expand Down Expand Up @@ -122,7 +125,8 @@ def create_log_files(self) -> None:
)
os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
if not os.path.exists(log_file_path):
open(log_file_path, "w").close()
with open(log_file_path, "w", encoding="utf-8"):
pass
self.log_files[log_level] = log_file_path

def get_log_file(self, log_level: LogLevel) -> Optional[str]:
Expand Down
5 changes: 2 additions & 3 deletions django_logging/utils/context_manager.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from contextlib import contextmanager
from logging import getLogger, Logger, PlaceHolder
from logging import Logger, PlaceHolder, getLogger
from typing import Dict, Iterator, Union
from django.conf import settings

from django_logging.settings.conf import LogConfig, LogManager
from django_logging.utils.get_conf import get_config, is_auto_initialization_enabled
Expand Down Expand Up @@ -30,7 +29,7 @@ def config_setup() -> Iterator[LogManager]:

try:
conf = get_config()
log_config = LogConfig(*conf)
log_config = LogConfig(**conf)
log_manager = LogManager(log_config)
log_manager.create_log_files()
log_manager.set_conf()
Expand Down
Loading

0 comments on commit 7774375

Please sign in to comment.