#!/usr/bin/env python3
"""Utility class for logging. This class is a wrapper around the original
logging module :py:mod:`logging` and can be used to apply the
`panda_autograsp <https://github.com/rickstaa/panda_autograsp>`_
formatters, filters and handlers to the logging object.
"""
# Make script both python2 and python3 compatible
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
try:
input = raw_input
except NameError:
pass
# Main python packages
import logging
import sys
import os
import colorlog
#################################################
# Script parameters #############################
#################################################
ROOT_LOG_LEVEL = logging.INFO
ROOT_LOG_STREAM = sys.stdout
#################################################
# Configure root logger function ################
#################################################
[docs]def clear_root():
"""Function used to reset the root logger."""
root_logger = logging.getLogger()
# clear any existing handles to streams because we don't want duplicate logs
# NOTE: we assume that any stream handles we find are to ROOT_LOG_STREAM,
# which is usually the case(because it is stdout). This is fine because we
# will be re-creating that handle. Otherwise we might be deleting a handle
# that won't be re-created, which could result in dropped logs.
for hdlr in root_logger.handlers:
if isinstance(hdlr, logging.StreamHandler):
root_logger.removeHandler(hdlr)
# create nullhandler to suppress no handler warning
root_logger.addHandler(logging.NullHandler())
# Set root configured to true
Logger.ROOT_CONFIGURED = False
#################################################
# Configure root logger function ################
#################################################
#################################################
# Root Logger file handler add ##################
#################################################
[docs]def add_root_log_file(log_file, mode="a", encoding=None, delay=False):
"""
Add a log file to the root logger.
Parameters
----------
log_file : :py:obj:`str`
The path to the log file.
mode : :py:obj:`str`
Log file writing mode, by default 'a'.
encoding : :py:obj:`str`
File encoding used, by default None.
delay : :py:obj:`str`
If delay is true, then file opening is deferred until the first call
to emit(), by default False.
"""
root_logger = logging.getLogger()
# Create model folder if it does not exists
log_folder = os.path.abspath(os.path.join(log_file, os.pardir))
if not os.path.exists(log_folder):
try:
os.makedirs(log_folder)
except OSError:
root_logger.info(
"Log file could not be created logger not logging to file {}".format(
log_file
)
)
return
# Add a file handle to the root logger
hdlr = logging.FileHandler(log_file, mode, encoding, delay)
formatter = logging.Formatter(
"%(asctime)s %(name)-10s %(levelname)-8s %(message)s", datefmt="%m-%d %H:%M:%S"
)
hdlr.setFormatter(formatter)
root_logger.info("Root logger now logging to {}".format(log_file))
root_logger.addHandler(hdlr)
#################################################
# Logger class ##################################
#################################################
[docs]class Logger(object):
"""Panda autograsp Logger class.
"""
ROOT_CONFIGURED = False
[docs] @staticmethod
def clear_root():
"""Reset root logger."""
clear_root()
[docs] @staticmethod
def get_logger(
name=None,
log_level=logging.INFO,
log_file=None,
silence=False,
mode="a",
encoding=None,
delay=False,
):
"""
Build a logger. All logs will be propagated up to the root logger if not
silenced. If log_file is provided, logs will be written out to that file.
If no logger name is given, log_file will be handed the root logger,
otherwise it will only be used by this particular logger.
Parameters
----------
name : :py:obj:`str`
The name of the logger to be built, by default "" thus formatting the
root logger.
log_level : :py:obj:`int`
The log level. See the python logging module documentation for possible
enum values.
log_file : :py:obj:`str`
The path to the log file to log to.
silence : :py:obj:`bool`
Whether or not to silence this logger. If it is silenced, the only way
to get output from this logger is through a non-global log file.
mode : :py:obj:`str`
Log file writing mode, by default 'a'.
encoding : :py:obj:`str`
File encoding used, by default None.
delay : :py:obj:`str`
If delay is true, then file opening is deferred until the first call
to emit(), by default False.
Returns
-------
:py:obj:`Logger`
A custom logger.
"""
# Create a new logger object with the panda_autograsp formatting
if not name: # Format the root logger
# some checks for silencing/no-op logging
if silence:
raise ValueError(
"You can't silence a logger and log to a global log file!"
)
# Setup root_logger
if not Logger.ROOT_CONFIGURED:
root_logger = configure_root(log_level)
Logger.ROOT_CONFIGURED = True
# configure the log file stream
if log_file is not None:
add_root_log_file(log_file, mode, encoding, delay)
# Return root logger
return root_logger
else: # Create new logger object
no_op = False
# some checks for silencing/no-op logging
if silence and log_file is None:
logging.warning("You are creating a no-op logger!")
no_op = True
# build a logger
logger = logging.getLogger(name)
logger.setLevel(log_level)
# silence the logger by preventing it from propagating upwards to the root
logger.propagate = not silence
# configure the log file stream
if log_file is not None:
# Add logger file handler #
hdlr = logging.FileHandler(log_file, mode, encoding, delay)
formatter = logging.Formatter(
"%(asctime)s %(name)-10s %(levelname)-8s %(message)s",
datefmt="%m-%d %H:%M:%S",
)
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
# add a no-op handler to suppress warnings about there being no handlers
if no_op:
logger.addHandler(logging.NullHandler())
return logger
[docs] @staticmethod
def add_log_file(log_file=None, logger=None, mode="a", encoding=None, delay=False):
"""
Add a log file to this logger. If no logger is given, log_file will be
handed the root logger, otherwise it will only be used by this particular
logger.
Parameters
----------
log_file : :py:obj:`str`
The path to the log file to log to.
logger : :py:obj:`logging.Logger`
The logger.
mode : :py:obj:`str`
Log file writing mode, by default 'a'.
encoding : :py:obj:`str`
File encoding used, by default None.
delay : :py:obj:`str`
If delay is true, then file opening is deferred until the first call
to emit(), by default False.
"""
# Add logfile to logger
if logger: # Add to root logger
add_root_log_file(log_file, mode, encoding, delay)
else: # Add to specified logger
hdlr = logging.FileHandler(log_file, mode, encoding, delay)
formatter = logging.Formatter(
"%(asctime)s %(name)-10s %(levelname)-8s %(message)s",
datefmt="%m-%d %H:%M:%S",
)
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)