Source code for bpc_utils.logging
"""Logging system for BPC."""
import logging
from .misc import current_time_with_tzinfo
from .typing import TYPE_CHECKING
if TYPE_CHECKING:
from .typing import Dict
[docs]class BPCLogHandler(logging.StreamHandler):
"""Handler used to format BPC logging records."""
format_templates = {
'DEBUG': '[%(levelname)s] %(asctime)s %(message)s',
'INFO': '%(message)s',
'WARNING': 'Warning: %(message)s',
'ERROR': 'Error: %(message)s',
'CRITICAL': 'Error: %(message)s',
} # type: Dict[str, str]
# An extension of the standard `%(asctime)s` to include local time zone.
time_format = '%Y-%m-%d %H:%M:%S.%f%z'
def __init__(self) -> None:
"""Initialize BPCLogHandler."""
super().__init__()
# Let `BPCLogHandler` instances equal to each other.
# This is to prevent `logger.addHandler(BPCLogHandler())` from adding multiple handlers when called multiple times.
def __eq__(self, other: object) -> bool:
return type(self) is type(other)
def __hash__(self) -> int:
return hash(())
[docs] def format(self, record: 'logging.LogRecord') -> str:
"""Format the specified record based on log level.
The record will be formatted based on its log level
in the following flavour:
+--------------+-----------------------------------------------+
| ``DEBUG`` | ``[%(levelname)s] %(asctime)s %(message)s`` |
+--------------+-----------------------------------------------+
| ``INFO`` | ``%(message)s`` |
+--------------+-----------------------------------------------+
| ``WARNING`` | ``Warning: %(message)s`` |
+--------------+-----------------------------------------------+
| ``ERROR`` | ``Error: %(message)s`` |
+--------------+-----------------------------------------------+
| ``CRITICAL`` | ``Error: %(message)s`` |
+--------------+-----------------------------------------------+
Args:
record: the log record
Returns:
the formatted log string
"""
level = record.levelname.upper()
# Only the 5 predefined log levels are supported
return self.format_templates[level] % {
'levelname': level,
'asctime': current_time_with_tzinfo().strftime(self.time_format),
'message': record.getMessage(),
}
[docs]def getLogger(name: str, level: int = logging.INFO) -> 'logging.Logger':
"""Create a BPC logger.
Args:
name: name for the logger
level: log level for the logger
Returns:
the created logger
"""
logger = logging.getLogger(name)
# We cannot create a single global `BPCLogHandler` instance on start
# because pytest cannot capture output from that.
# See: https://stackoverflow.com/questions/38594296/how-to-use-logging-pytest-fixture-and-capsys
logger.addHandler(BPCLogHandler())
logger.setLevel(level)
return logger
__all__ = ['getLogger']