Source code for commandio.logutil

# -*- coding: utf-8 -*-
"""Logging utility module.

.. autosummary::
    :nosignatures:

    LogFile

.. autoclass:: LogFile
    :members:
"""
import logging
from datetime import datetime
from typing import Any, Dict, Union

from commandio.fileio import File, file
from commandio.enums import LogLevel


[docs]class LogFile(File): """Convenience class that creates a log file object for logging purposes. Attributes: log_file: Log filename. Usage examples: >>> log = LogFile("file.log",False) >>> log "file.log" Args: file: Log filename (need not exist at runtime). print_to_screen: If true, prints output to standard output (stdout) as well. format_log_str: If true, this formats the logging information with more detail. use_root_logger: If true, **ALL** information is written to a single log file. level: Logging level. Options include: * ``info`` * ``debug`` * ``critical`` * ``error`` * ``warning`` exc_info: Adds exception information to the logging message. stack_info: Adds stack information to the logging message. stacklevel: If greater than 1, the corresponding number of stack frames are skipped when computing the line number and function name set in the ``LogRecord`` for the logging event. """ def __init__( self, log_file: Union[file, str] = "", print_to_screen: bool = False, format_log_str: bool = False, use_root_logger: bool = False, level: str = "info", exc_info: bool = False, stack_info: bool = False, stacklevel: int = 1, ) -> None: """Initialization method for the LogFile class. Initiates logging and its associated methods (from the ``logging`` module). Usage examples: >>> log = LogFile("file.log",False) >>> log "file.log" Args: file: Log filename (need not exist at runtime). print_to_screen: If true, prints output to standard output (stdout) as well. format_log_str: If true, this formats the logging information with more detail. use_root_logger: If true, **ALL** information is written to a single log file. level: Logging level. Options include: * ``info`` * ``debug`` * ``critical`` * ``error`` * ``warning`` exc_info: Adds exception information to the logging message. stack_info: Adds stack information to the logging message. stacklevel: If greater than 1, the corresponding number of stack frames are skipped when computing the line number and function name set in the ``LogRecord`` for the logging event. """ self.log_file: str = log_file level: str = LogLevel(level.lower()).name # Define logging self.logger = logging.getLogger(__name__) super(LogFile, self).__init__(self.log_file) # Define logging parameters self.params: Dict[str, Any] = { "exc_info": bool(exc_info), "stack_info": bool(stack_info), "stacklevel": int(stacklevel), } if format_log_str and (level == "debug"): FORMAT: str = "%(asctime)s %(name)-12s %(levelname)-8s %(message)s" DATEFMT: str = "%m-%d-%y %H:%M:%S" elif format_log_str and (level == "info"): FORMAT: str = "%(asctime)s %(message)s" DATEFMT: str = "%m-%d-%y %H:%M:%S" elif format_log_str and (level != "debug"): FORMAT: str = "%(asctime)s %(name)s %(message)s" DATEFMT: str = "%m-%d-%y %H:%M:%S" else: FORMAT: str = None DATEFMT: str = None if level == "info": level: logging.INFO = logging.INFO elif level == "debug": level: logging.DEBUG = logging.DEBUG elif level == "critical": level: logging.CRITICAL = logging.CRITICAL elif level == "error": level: logging.ERROR = logging.ERROR elif level == "warning": level: logging.WARNING = logging.WARNING if use_root_logger: # Use Basic Config for root level logging logging.basicConfig( level=level, format=FORMAT, datefmt=DATEFMT, filename=self.log_file, filemode="a", ) else: # Define logging components self.logger.setLevel(level=level) file_handler: logging.FileHandler = logging.FileHandler( self.log_file ) formatter: logging.Formatter = logging.Formatter(FORMAT, DATEFMT) file_handler.setFormatter(formatter) self.logger.addHandler(file_handler) # Define a Handler which writes to the sys.stderr if print_to_screen: self.console: logging.StreamHandler = logging.StreamHandler() self.console.setLevel(level) logging.getLogger().addHandler(self.console)
[docs] def info(self, msg: str = "", use_header: bool = False) -> None: """Writes information to log file. Usage examples: >>> log = LogFile("file.log") >>> log.info("<str>") Args: msg: String to be printed to log file. use_header: Give log message a section header. """ if use_header: self.logger.info(self._section_header(msg), **self.params) else: self.logger.info(msg, **self.params)
[docs] def debug(self, msg: str = "", use_header: bool = False) -> None: """Writes debug information to file. Usage examples: >>> log = LogFile("file.log") >>> log.debug("<str>") Args: msg: String to be printed to log file. use_header: Give log message a section header. """ if use_header: self.logger.debug(self._section_header(msg), **self.params) else: self.logger.debug(msg, **self.params)
[docs] def critical(self, msg: str, use_header: bool = False) -> None: """Write critical messages/information to file. Usage example: >>> log = LogFile("file.log") >>> log.critical("<str>") Args: msg: String to be printed to log file. use_header: Give log message a section header. """ if use_header: self.logger.critical(self._section_header(msg), **self.params) else: self.logger.critical(msg, **self.params)
[docs] def error(self, msg: str = "", use_header: bool = False) -> None: """Writes error information to file. Usage examples: >>> log = LogFile("file.log") >>> log.error("<str>") Args: msg: String to be printed to log file. use_header: Give log message a section header. """ if use_header: self.logger.error(self._section_header(msg), **self.params) else: self.logger.error(msg, **self.params)
[docs] def warning(self, msg: str = "", use_header: bool = False) -> None: """Writes warnings to file. Usage examples: >>> log = LogFile("file.log") >>> log.warning("<str>") Args: msg: String to be printed to log file. use_header: Give log message a section header. """ if use_header: self.logger.warning(self._section_header(msg), **self.params) else: self.logger.warning(msg, **self.params)
[docs] def log(self, log_cmd: str = "", use_header: bool = False) -> None: """Log function for logging commands and messages to some log file. Usage examples: >>> log = LogFile("file.log") >>> log.log("<str>") Args: log_cmd: Message to be written to log file. use_header: Give log message a section header. """ if use_header: self.info(self._section_header(log_cmd)) else: self.info(log_cmd)
def _section_header(self, msg: str) -> str: """Helper function that adds a section header that consists of a line break, with the date, time, and message string. Usage example: >>> _section_header("INFO: This is a test") -------------------------------------------------------------------------------------- Mon Aug 23 13:34:21 2021: INFO: This is a test -------------------------------------------------------------------------------------- Args: msg: Message string to have section header. Returns: String that represents the message with header. """ header: str = f"""\n -------------------------------------------------------------------------------------- {datetime.now().ctime()}: {msg} -------------------------------------------------------------------------------------- """ return header