import logging
import sys
from logging.handlers import TimedRotatingFileHandler
disabled_logger = False
[docs]class Logger(metaclass=SingletonMeta):
def __init__(self, file_path: str = "deepmol.log", level: int = logging.DEBUG) -> None:
"""
Uses the logger from the logging module.
Parameters
----------
file_path: str
The path to the log file.
level: int
The level of the logger.
"""
self.file_path = file_path
self.level = level
self.formatter = logging.Formatter("%(asctime)s — %(levelname)s — %(message)s")
self.file_path_changed = False
self.logger = None
self.console_handler = None
self.file_handler = None
if not disabled_logger:
self.create_handlers()
[docs] @staticmethod
def disable():
"""
Disables the logger.
"""
# disable all levels of logging
logging.disable(logging.DEBUG)
logging.disable(logging.INFO)
logging.disable(logging.CRITICAL)
logging.disable(logging.ERROR)
logging.disable(logging.WARNING)
global disabled_logger
disabled_logger = True
[docs] def enable(self):
"""
Enables the logger.
"""
# logging.disable(logging.NOTSET)
global disabled_logger
disabled_logger = False
if self.file_path_changed:
self.logger.removeHandler(self.file_handler)
self.file_path_changed = False
file_handler = TimedRotatingFileHandler(self.file_path, when='midnight')
self.file_handler = file_handler
self.file_handler.setFormatter(self.formatter)
self.logger.addHandler(self.file_handler)
self.logger.setLevel(self.level)
self.create_handlers()
[docs] def create_handlers(self):
"""
Creates the handlers for the logger.
"""
if not self.logger:
self.logger = logging.getLogger(self.file_path)
self.console_handler = logging.StreamHandler(sys.stdout)
self.console_handler.setFormatter(self.formatter)
self.file_handler = TimedRotatingFileHandler(self.file_path, when='midnight')
self.file_handler.setFormatter(self.formatter)
if not self.logger.hasHandlers():
self.logger.addHandler(self.file_handler)
self.logger.addHandler(self.console_handler)
self.logger.setLevel(self.level)
[docs] def set_file_path(self, file_path: str):
"""
Sets the file path of the logger.
Parameters
----------
file_path: str
The path to the log file.
"""
self.file_path = file_path
self.file_path_changed = True
if not disabled_logger:
self.logger = logging.getLogger(self.file_path)
self.logger.removeHandler(self.file_handler)
file_handler = TimedRotatingFileHandler(file_path, when='midnight')
self.file_handler = file_handler
self.file_handler.setFormatter(self.formatter)
self.logger.addHandler(self.file_handler)
self.logger.addHandler(self.console_handler)
self.logger.setLevel(self.level)
[docs] def set_level(self, level: int):
"""
Sets the level of the logger.
Parameters
----------
level: int
The level of the logger.
"""
self.logger.setLevel(level)
[docs] def info(self, msg: str, **kwargs):
"""
Log a message with severity 'INFO' on the root logger.
Parameters
----------
msg: str
The message to log.
kwargs: dict
The keyword arguments to pass to the logger.
Returns
-------
"""
self.logger.info(msg, **kwargs)
[docs] def warning(self, msg: str, **kwargs):
"""
Log a message with severity 'WARNING' on the root logger.
Parameters
----------
msg: str
The message to log.
kwargs: dict
The keyword arguments to pass to the logger.
"""
self.logger.warning(msg, **kwargs)
[docs] def error(self, msg: str, **kwargs):
"""
Log a message with severity 'ERROR' on the root logger.
Parameters
----------
msg: str
The message to log.
kwargs: dict
The keyword arguments to pass to the logger.
"""
self.logger.error(msg, **kwargs)
[docs] def critical(self, msg: str, **kwargs):
"""
Log a message with severity 'CRITICAL' on the root logger.
Parameters
----------
msg: str
The message to log.
kwargs: dict
The keyword arguments to pass to the logger.
"""
self.logger.critical(msg, **kwargs)
[docs] def debug(self, msg: str, **kwargs):
"""
Log a message with severity 'DEBUG' on the root logger.
Parameters
----------
msg: str
The message to log.
kwargs: dict
The keyword arguments to pass to the logger.
"""
self.logger.debug(msg, **kwargs)
[docs] def close_handlers(self):
"""
Closes the handlers of the logger.
"""
if self.console_handler:
self.console_handler.close()
if self.file_handler:
self.file_handler.close()
def __getstate__(self) -> dict:
"""
Returns the state of the logger.
It replaces the logger, console_handler and file_handler with their names to be picklezable, otherwise,
it would not be possible to pickle the logger for multiprocessing.
Returns
-------
d: dict
The state of the logger.
"""
d = self.__dict__.copy()
d['logger'] = d['logger'].name
if 'console_handler' in d:
d['console_handler'] = 'console_handler'
if 'file_handler' in d:
d['file_handler'] = 'file_handler'
return d
def __setstate__(self, d: dict):
"""
Sets the state of the logger.
Parameters
----------
d: dict
The dictionary to set the state of the logger.
"""
d['logger'] = logging.getLogger(d['file_path'])
self.__dict__.update(d)