Source code for jmbuilder.utils.logger

"""Custom Logger Module for JMBuilder

This module provides a custom logger utility that initializes and creates a new
`Logger` object for logging information or errors to the console or a log file.

To use the custom logger in your project, you can import the `init_logger` function
from this module and create a new `Logger` object with desired settings.

Copyright (c) 2023-2024 Ryuu Mitsuki.


Available Functions
-------------------
init_logger
    Initialize and create new `Logger` object for logging any
    information or errors to file, if specified, otherwise the output
    will be written to console standard error.

Available Constants
-------------------
BASIC_FORMAT : str
    The basic (default) format for `logging.Formatter`.
    This is alias for `logging.BASIC_FORMAT`.

CUSTOM_FORMAT : str
    The custom format for `logging.Formatter`.

CRITICAL : int
    The integer value representation of logging level 50.
    This is alias for `logging.CRITICAL`.

DEBUG : int
    The integer value representation of logging level 10.
    This is alias for `logging.DEBUG`.

ERROR : int
    The integer value representation of logging level 40.
    This is alias for `logging.ERROR`.

FATAL : int
    The integer value representation of logging level 50.
    This is alias for `logging.FATAL`.

INFO : int
    The integer value representation of logging level 20.
    This is alias for `logging.INFO`.

NOTSET : int
    The integer value of representation of logging level 0.
    This is alias for `logging.NOTSET`.

WARN : int
    The integer value of representation of logging level 30.
    This is alias for `logging.WARN`.

WARNING : int
    The integer value of representation of logging level 30.
    This is alias for `logging.WARNING`.

Example
-------
# Import the module
>>> from jmbuilder import logger

# Create a new logger object
>>> log = logger.init_logger(fmt=logger.CUSTOM_FORMAT,
...                          level=logger.INFO)
>>> log
<RootLogger root (INFO)>
>>> type(log).__name__
'RootLogger'

# Log some information message
# The output will be printed to console standard error (stderr),
# because the `filename` are not defined on `init_logger`.
>>> log.info('This is an information message.')
This is an information message.

# Log some exception
# To trace the error, pass any true value to `exc_info` argument.
>>> try:
...     x = 3 / 0
... except ZeroDivisionError as div_err:
...     log.error('An error occurred.', exc_info=1)
An error occurred.
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero
"""

import os as _os
import sys as _sys
import logging as _log
from typing import Union

from .._globals import AUTHOR, VERSION, VERSION_INFO, STDERR
from ..exception import JMUnknownTypeError as _JMTypeError


__all__ = ['init_logger']
__author__ = AUTHOR
__version__ = VERSION
__version_info__ = VERSION_INFO


# References of formatter
BASIC_FORMAT  = _log.BASIC_FORMAT
CUSTOM_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'

# References of logging levels
NOTSET   = _log.NOTSET      # 0
DEBUG    = _log.DEBUG       # 10
INFO     = _log.INFO        # 20
WARN     = _log.WARN        # 30
WARNING  = _log.WARNING     # 30
ERROR    = _log.ERROR       # 40
CRITICAL = _log.CRITICAL    # 50
FATAL    = _log.FATAL       # 50


[docs]def init_logger(filename: str = None, *, fmt: Union[str, _log.Formatter] = None, level: int = DEBUG) -> _log.Logger: """ Initializes and creates a new `Logger` object. Parameters ---------- filename : str or None, optional A string representing the name of the logger file. If specified, logs will be written to the specified file, otherwise logs will be printed to `stderr` (standard error). Default is ``None``. fmt : str or logging.Formatter, optional A string representation of the log formatter or an object of `logging.Formatter` class. If not specified, a customized formatter will be used. See ``CUSTOM_FORMAT`` constant variable. level : int, optional An integer value that specifies the logging level for the logger. Default is ``logger.DEBUG`` (equal to 10). Returns ------- logging.Logger : A new `Logger` object for logging any information or errors. Raises ------ JMUnknownTypeError : If the 'fmt' are not instance of `str` or `logging.Formatter` class. """ handler: _log.Handler = None # Check whether the 'filename' are specified if not filename: handler = _log.StreamHandler(STDERR) else: if not isinstance(filename, str): filename = str(filename) if isinstance(filename, str) and not filename.endswith('.log'): filename += '.log' # Check whether the parent directory of 'filename' exist if not _os.path.exists(_os.path.dirname(filename)): # Create the directory if not exist _os.mkdir(_os.path.dirname(filename)) handler = _log.FileHandler(filename) # Check whether the 'fmt' as log formatter are specified if not fmt: handler.setFormatter(_log.Formatter(CUSTOM_FORMAT)) elif fmt and isinstance(fmt, (str, _log.Formatter)): if isinstance(fmt, str): handler.setFormatter(_log.Formatter(fmt)) else: handler.setFormatter(fmt) else: raise _JMTypeError( f'Invalid type of `fmt`: "{type(fmt).__name__}". ' + \ 'Expected "str" and "logging.Formatter"') logger = _log.getLogger( _os.path.basename(filename) ) if filename else _log.getLogger('JMBuilder log') logger.setLevel(level) # set the logger level, default is DEBUG logger.addHandler(handler) # set the handler return logger
# Remove unnecessary variables del AUTHOR, VERSION, VERSION_INFO, Union