From 0545ef571fc055f50c4c23598eb408c470d5a1fa Mon Sep 17 00:00:00 2001 From: Jerrie-Aries Date: Sat, 15 Oct 2022 01:14:47 +0000 Subject: [PATCH] Add `create_log_handler` function. Update imports: - Move `colorama` import to `models.py`. - Utilize `create_log_handler`. This function is mainly to pre-configure the default values for parameters to pass into the Handler class. --- bot.py | 30 ++++---------- core/models.py | 110 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 92 insertions(+), 48 deletions(-) diff --git a/bot.py b/bot.py index 340a024b7cf..ae32fa7c288 100644 --- a/bot.py +++ b/bot.py @@ -1,27 +1,18 @@ __version__ = "4.0.1" -try: - # noinspection PyUnresolvedReferences - from colorama import init - - init() -except ImportError: - pass - +# early import to initialize colorama from core.models import ( DMDisabled, HostingMethod, InvalidConfigError, PermissionLevel, SafeFormatter, + create_log_handler, configure_logging, getLogger, - log_file_formatter, - log_stream_formatter, ) - import asyncio import copy import hashlib @@ -192,8 +183,7 @@ def _configure_logging(self): logger.info("Logging level: %s", level_text) logger.info("Log file: %s", self.log_file_name) - handler_params = dict(mode="a+", maxBytes=48000, backupCount=1, encoding="utf-8") - configure_logging(self.log_file_name, log_level, **handler_params) + configure_logging(self.log_file_name, log_level) # Set up discord.py logging # repeat the step @@ -202,21 +192,17 @@ def _configure_logging(self): logger.info("Setting up discord logger, logging level: %s.", logging.getLevelName(log_level)) d_logger = logging.getLogger("discord") d_logger.setLevel(log_level) - stream = logging.StreamHandler(stream=sys.stdout) is_debug = not log_level > logging.DEBUG - stream.setLevel(log_level if not is_debug else logging.INFO) - stream.setFormatter(log_stream_formatter) + stream = create_log_handler(level=log_level if not is_debug else logging.INFO) d_logger.addHandler(stream) - file_handler = logging.handlers.RotatingFileHandler(self.log_file_name, **handler_params) - file_handler.setFormatter(log_file_formatter) - file_handler.setLevel(log_level if not is_debug else logging.INFO) + file_handler = create_log_handler( + self.log_file_name, rotating=True, level=log_level if not is_debug else logging.INFO + ) d_logger.addHandler(file_handler) # internal discord.py debug logging if is_debug: - debug_handler = logging.FileHandler(filename="discord.log", encoding="utf-8", mode="w") - debug_handler.setLevel(log_level) - debug_handler.setFormatter(log_file_formatter) + debug_handler = create_log_handler("discord.log", level=log_level, mode="w", encoding="utf-8") logger.info("Discord logger DEBUG level is enabled. Log file: %s", "discord.log") d_logger.addHandler(debug_handler) diff --git a/core/models.py b/core/models.py index 795a74e335a..ddf98db5e88 100644 --- a/core/models.py +++ b/core/models.py @@ -3,12 +3,17 @@ import re import sys +from logging import FileHandler, StreamHandler from logging.handlers import RotatingFileHandler +from typing import Optional, Union + try: - from colorama import Fore, Style + from colorama import Fore, Style, init as color_init except ImportError: Fore = Style = type("Dummy", (object,), {"__getattr__": lambda self, item: ""})() +else: + color_init() if ".heroku" in os.environ.get("PYTHONHOME", ""): @@ -64,18 +69,87 @@ def line(self, level="info"): ) -logging.setLoggerClass(ModmailLogger) -log_level = logging.INFO -loggers = set() +class FileFormatter(logging.Formatter): + ansi_escape = re.compile(r"\x1B\[[0-?]*[ -/]*[@-~]") + + def format(self, record): + record.msg = self.ansi_escape.sub("", record.msg) + return super().format(record) + -ch = logging.StreamHandler(stream=sys.stdout) -ch.setLevel(log_level) log_stream_formatter = logging.Formatter( "%(asctime)s %(name)s[%(lineno)d] - %(levelname)s: %(message)s", datefmt="%m/%d/%y %H:%M:%S" ) -ch.setFormatter(log_stream_formatter) +log_file_formatter = FileFormatter( + "%(asctime)s %(name)s[%(lineno)d] - %(levelname)s: %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) + + +def create_log_handler( + filename: Optional[str] = None, + *, + rotating: bool = False, + level: int = logging.DEBUG, + mode: str = "a+", + encoding: str = "utf-8", + maxBytes: int = 48000, + backupCount: int = 1, + **kwargs, +) -> Union[FileHandler, RotatingFileHandler, StreamHandler]: + """ + Return a pre-configured log handler. This function is made for consistency sake with + pre-defined default values for parameters and formatters to pass to handler class. + Additional keyword arguments also can be specified, just in case. + + Plugin developers should not use this and only use the `getLogger` instead to instantiate the ModmailLogger object. + + Parameters + ----------- + filename : Optional[Path] + Specifies that a `FileHandler` or `RotatingFileHandler` be created, using the specified filename, + rather than a `StreamHandler`. Defaults to `None`. + rotating : bool + Whether the file handler should be the `RotatingFileHandler`. Defaults to `False`. Note, this + argument only compatible if the `filename` is specified, otherwise `ValueError` will be raised. + level : int + The root logger level for the handler. Defaults to `logging.DEBUG`. + mode : str + If filename is specified, open the file in this mode. Defaults to 'a+'. + encoding : str + If this keyword argument is specified along with filename, its value is used when the `FileHandler` is created, + and thus used when opening the output file. Defaults to 'utf-8'. + maxBytes : int + The max file size before the rollover occurs. Defaults to 48000. Rollover occurs whenever the current log file + is nearly `maxBytes` in length; but if either of `maxBytes` or `backupCount` is zero, rollover never occurs, so you + generally want to set `backupCount` to at least 1. + backupCount : int + Max number of backup files. Defaults to 1. If this is set to zero, rollover will never occur. + """ + if filename is None and rotating: + raise ValueError("`filename` must be set to instantiate a `RotatingFileHandler`.") + + if filename is None: + handler = StreamHandler(stream=sys.stdout, **kwargs) + handler.setFormatter(log_stream_formatter) + elif not rotating: + handler = FileHandler(filename, mode=mode, encoding=encoding, **kwargs) + handler.setFormatter(log_file_formatter) + else: + handler = RotatingFileHandler( + filename, mode=mode, encoding=encoding, maxBytes=maxBytes, backupCount=backupCount, **kwargs + ) + handler.setFormatter(log_file_formatter) -ch_debug = None + handler.setLevel(level) + return handler + + +logging.setLoggerClass(ModmailLogger) +log_level = logging.INFO +loggers = set() +ch: StreamHandler = create_log_handler(level=log_level) +ch_debug: Optional[RotatingFileHandler] = None def getLogger(name=None) -> ModmailLogger: @@ -88,25 +162,9 @@ def getLogger(name=None) -> ModmailLogger: return logger -class FileFormatter(logging.Formatter): - ansi_escape = re.compile(r"\x1B\[[0-?]*[ -/]*[@-~]") - - def format(self, record): - record.msg = self.ansi_escape.sub("", record.msg) - return super().format(record) - - -log_file_formatter = FileFormatter( - "%(asctime)s %(name)s[%(lineno)d] - %(levelname)s: %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", -) - - -def configure_logging(name, level=None, **handler_params): +def configure_logging(name, level: Optional[int] = None): global ch_debug, log_level - ch_debug = RotatingFileHandler(name, **handler_params) - ch_debug.setFormatter(log_file_formatter) - ch_debug.setLevel(logging.DEBUG) + ch_debug = create_log_handler(name, rotating=True) if level is not None: log_level = level