Module pipettin-piper.piper.log

Functions

def setup_logging(directory: str = None,
level: int = 4,
prefix='piper',
verbose=True,
make_symlink=True,
loglimit_mb=None)
Expand source code
def setup_logging(directory:str=None, level:int=4, prefix="piper", verbose=True, make_symlink=True, loglimit_mb=None):
    """Set-up logging for the piper module."""

    current_handlers = logging.getLogger().handlers
    if prefix in [h.name for h in current_handlers]:
        logging.warning(f"Logging already setup for '{prefix}'. Doing nothing.")
        return

    _, logfile_path = tempfile.mkstemp(prefix=prefix + "-", suffix=".log", dir=directory)

    if loglimit_mb is None:
        # Create a FileHandler
        file_handler = logging.FileHandler(logfile_path)
    else:
        # Create a rotating file handler
        file_handler = handlers.RotatingFileHandler(
            logfile_path, maxBytes=loglimit_mb*BYTES_PER_MB, backupCount=4
        )

    log_level = LEVELS[max(0, min(int(level), 4))]
    file_handler.setLevel(log_level)

    # Set formatter for the handler
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s [%(filename)s:%(lineno)s]: %(message)s',
        datefmt='%H:%M:%S'
    )
    file_handler.setFormatter(formatter)

    file_handler.name = prefix

    logging.basicConfig(handlers=[file_handler], level=log_level,
                        # By setting force to True, any previous handlers are removed.
                        # See: https://docs.python.org/3.11/library/logging.html#logging.basicConfig
                        force=True)

    if verbose:
        print(f"Logging to file at level {log_level}: {logfile_path}")

    # Symlink the logfile.
    if make_symlink:
        symlink_logfile(logfile_path, directory, prefix, verbose)

    # print("Handlers 1:")
    # handlers = logging.getLogger().handlers
    # print(handlers)
    # print(handlers[0])
    # print(handlers[0].name)

Set-up logging for the piper module.

Expand source code
def symlink_logfile(logfile_path, directory, prefix, verbose):

    # Create a symlink to the log file.
    if directory is not None:
        symlink_path = os.path.join(directory, f"{prefix}.log")
    else:
        # Create a symlink to the log file in the system's temporary directory (e.g. "/tmp/piper.log")
        temp_dir = tempfile.gettempdir()
        symlink_path = os.path.join(temp_dir, f"{prefix}.log")

    try:
        if os.path.islink(symlink_path) or os.path.exists(symlink_path):
            if verbose:
                print(f"Deleting previous symlink to the logfile: {symlink_path}")
            os.remove(symlink_path)
        os.symlink(logfile_path, symlink_path)
        if verbose:
            print(f"Symlink to logfile created: {symlink_path} -> {logfile_path}")
    except OSError as e:
        print(f"Error creating symlink to logfile:  {symlink_path} -> {logfile_path} ({e})")

Classes

class CustomFormatter (fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)
Expand source code
class CustomFormatter(logging.Formatter):

    grey = "\x1b[38;20m"
    yellow = "\x1b[33;20m"
    red = "\x1b[31;20m"
    bold_red = "\x1b[31;1m"
    reset = "\x1b[0m"
    # format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
    format_string = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    datefmt = '%H:%M:%S'

    FORMATS = {
        logging.DEBUG: grey + format_string + reset,
        logging.INFO: format_string,
        logging.WARNING: yellow + format_string + reset,
        logging.ERROR: red + format_string + reset,
        logging.CRITICAL: bold_red + format_string + reset
    }

    def format(self, record):
        log_fmt = self.FORMATS.get(record.levelno)
        formatter = logging.Formatter(log_fmt, datefmt=self.datefmt)
        return formatter.format(record)

Formatter instances are used to convert a LogRecord to text.

Formatters need to know how a LogRecord is constructed. They are responsible for converting a LogRecord to (usually) a string which can be interpreted by either a human or an external system. The base Formatter allows a formatting string to be specified. If none is supplied, the style-dependent default value, "%(message)s", "{message}", or "${message}", is used.

The Formatter can be initialized with a format string which makes use of knowledge of the LogRecord attributes - e.g. the default value mentioned above makes use of the fact that the user's message and arguments are pre- formatted into a LogRecord's message attribute. Currently, the useful attributes in a LogRecord are described by:

%(name)s Name of the logger (logging channel) %(levelno)s Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL) %(levelname)s Text logging level for the message ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL") %(pathname)s Full pathname of the source file where the logging call was issued (if available) %(filename)s Filename portion of pathname %(module)s Module (name portion of filename) %(lineno)d Source line number where the logging call was issued (if available) %(funcName)s Function name %(created)f Time when the LogRecord was created (time.time() return value) %(asctime)s Textual time when the LogRecord was created %(msecs)d Millisecond portion of the creation time %(relativeCreated)d Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded (typically at application startup time) %(thread)d Thread ID (if available) %(threadName)s Thread name (if available) %(process)d Process ID (if available) %(message)s The result of record.getMessage(), computed just as the record is emitted

Initialize the formatter with specified format strings.

Initialize the formatter either with the specified format string, or a default as described above. Allow for specialized date formatting with the optional datefmt argument. If datefmt is omitted, you get an ISO8601-like (or RFC 3339-like) format.

Use a style parameter of '%', '{' or '$' to specify that you want to use one of %-formatting, :meth:str.format ({}) formatting or :class:string.Template formatting in your format string.

Changed in version: 3.2

Added the style parameter.

Ancestors

  • logging.Formatter

Class variables

var FORMATS
var bold_red
var datefmt
var format_string
var grey
var red
var reset
var yellow

Methods

def format(self, record)
Expand source code
def format(self, record):
    log_fmt = self.FORMATS.get(record.levelno)
    formatter = logging.Formatter(log_fmt, datefmt=self.datefmt)
    return formatter.format(record)

Format the specified record as text.

The record's attribute dictionary is used as the operand to a string formatting operation which yields the returned string. Before formatting the dictionary, a couple of preparatory steps are carried out. The message attribute of the record is computed using LogRecord.getMessage(). If the formatting string uses the time (as determined by a call to usesTime(), formatTime() is called to format the event time. If there is exception information, it is formatted using formatException() and appended to the message.