Module ambianic.logger
Logging functionalities wrapper
Expand source code
"""Logging functionalities wrapper"""
import logging
import logging.handlers
import os
import pathlib
DEFAULT_FILE_LOG_LEVEL = logging.INFO
DEFAULT_CONSOLE_LOG_LEVEL = logging.WARN
log = logging.getLogger(__name__)
def _get_log_level(log_level, default_log_level):
numeric_level = default_log_level
if log_level:
try:
numeric_level = getattr(logging, log_level.upper(), default_log_level)
except AttributeError as e:
log.warning("Invalid log level: %s . Error: %s", log_level, e)
log.warning("Defaulting log level to %s", default_log_level)
fmt = None
if numeric_level <= logging.INFO:
format_cfg = (
"%(asctime)s %(levelname)-4s "
"%(pathname)s.%(funcName)s(%(lineno)d): %(message)s"
)
datefmt_cfg = "%Y-%m-%d %H:%M:%S"
fmt = logging.Formatter(fmt=format_cfg, datefmt=datefmt_cfg, style="%")
else:
fmt = logging.Formatter()
return numeric_level, fmt
def configure(config=None):
if config is None:
config = {}
log_level = config.get("level", None)
console_log_level = config.get("console_level", log_level)
numeric_level, fmt = _get_log_level(
log_level, default_log_level=DEFAULT_FILE_LOG_LEVEL
)
root_logger = logging.getLogger()
# remove any other handlers that may be assigned previously
# and could cause unexpected log collisions
root_logger.handlers = []
# add a console handler that only shows errors and warnings
ch = logging.StreamHandler()
console_numeric_level, console_fmt = _get_log_level(
console_log_level, default_log_level=DEFAULT_CONSOLE_LOG_LEVEL
)
ch.setLevel(console_numeric_level)
# add formatter to ch
ch.setFormatter(console_fmt)
# add ch to logger
root_logger.addHandler(ch)
# add a file handler if configured
log_filename = config.get("file", None)
if log_filename:
log_directory = os.path.dirname(log_filename)
with pathlib.Path(log_directory) as log_dir:
log_dir.mkdir(parents=True, exist_ok=True)
print(f"Log messages directed to {log_filename}")
handler = logging.handlers.RotatingFileHandler(
log_filename,
# each log file will be up to 10MB in size
maxBytes=100 * 1024 * 1024,
# 20 backup files will be kept. Older will be erased.
backupCount=20,
)
handler.setFormatter(fmt)
root_logger.addHandler(handler)
root_logger.setLevel(numeric_level)
effective_level = log.getEffectiveLevel()
assert numeric_level == effective_level
log.info("Logging configured with level %s", logging.getLevelName(effective_level))
Functions
def configure(config=None)
-
Expand source code
def configure(config=None): if config is None: config = {} log_level = config.get("level", None) console_log_level = config.get("console_level", log_level) numeric_level, fmt = _get_log_level( log_level, default_log_level=DEFAULT_FILE_LOG_LEVEL ) root_logger = logging.getLogger() # remove any other handlers that may be assigned previously # and could cause unexpected log collisions root_logger.handlers = [] # add a console handler that only shows errors and warnings ch = logging.StreamHandler() console_numeric_level, console_fmt = _get_log_level( console_log_level, default_log_level=DEFAULT_CONSOLE_LOG_LEVEL ) ch.setLevel(console_numeric_level) # add formatter to ch ch.setFormatter(console_fmt) # add ch to logger root_logger.addHandler(ch) # add a file handler if configured log_filename = config.get("file", None) if log_filename: log_directory = os.path.dirname(log_filename) with pathlib.Path(log_directory) as log_dir: log_dir.mkdir(parents=True, exist_ok=True) print(f"Log messages directed to {log_filename}") handler = logging.handlers.RotatingFileHandler( log_filename, # each log file will be up to 10MB in size maxBytes=100 * 1024 * 1024, # 20 backup files will be kept. Older will be erased. backupCount=20, ) handler.setFormatter(fmt) root_logger.addHandler(handler) root_logger.setLevel(numeric_level) effective_level = log.getEffectiveLevel() assert numeric_level == effective_level log.info("Logging configured with level %s", logging.getLevelName(effective_level))