Logging module for use in other projects I'm trying to make a simple module I can paste into my various projects to provide basic logging capabilities. I'm hoping to become less dependent on print because:


Using logging will allow me to quickly enable and disable what's being logged
logging has built-in file and formatting utilities
The built-in levels of logging are helpful


Here's the log.py module:

import argparse
import logging
import sys


def parse_log_args(parser: argparse.ArgumentParser):
    default_level = 'INFO'
    parser.add_argument("-l", "--log", dest="loglevel",
                        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
                        default=default_level,
                        help=f"Set the logging level (default: {default_level})",
                        type=str)

    parser.add_argument(
        '-lf', '--logfile',
        help="File for logging",
        dest="logfile",
        type=str
    )

    return parser


def conf_logging(logger: logging.Logger, loglevel: str, path=None):

    if loglevel.upper() not in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'):
        raise ValueError("Invalid logging level")

    if path is None:
        console_formatter = logging.Formatter('[%(levelname)s] %(message)s')
        handler = logging.StreamHandler(sys.stdout)
        handler.setFormatter(console_formatter)
    else:
        file_formatter = logging.Formatter(
            ('%(asctime)s [%(levelname)s] %(name)s.%(funcName)s'
             '@ L%(lineno)d\n  %(message)s'))
        handler = logging.FileHandler(path, encoding='utf-8')
        handler.setFormatter(file_formatter)

    l_name = logging.getLevelName(loglevel)
    handler.setLevel(l_name)
    logger.setLevel(l_name)

    logger.addHandler(handler)

    return logger


Here's an example of its usage:

import logging
import argparse
from log import parse_log_args, conf_logging

logger = logging.getLogger(__name__)

parser = argparse.ArgumentParser()

parser = parse_log_args(parser)
args = parser.parse_args()
logger = conf_logging(logger, args.loglevel, args.logfile)

logger.debug("What")
logger.info("okay")


Which can be called as follows:

&gt; python basic.py --log INFO
[INFO] okay
&gt; python basic.py --log DEBUG
[DEBUG] What
[INFO] okay


I'm not super familiar with the logging module, so I was a little surprised I had to have to set the logging lever for both the logger and the handler. Is there a better, more explicit way of writing this?
It's more natural to say "--loglevel", ... than "--log", dest="loglevel", .... A log switch suggests that it takes an argument of a logfile output filename.

Not sure why you need dest for the logfile argument.

You're specifying type of str which is already the default, but maybe you did it to help with type annotations?

Raising "Invalid logging level" is maybe superfluous, better to let the underlying logging library deal with it. Looks like you're reaching across layers.

Naming your module log is perhaps a bit adventurous, as sometimes folks concisely write log.info() instead of logger.info().

In the example, I recommend deleting:

logger = logging.getLogger(__name__)


in favor of conf_logging(logging.getLogger(__name__), args.loglevel, args.logfile).
Perhaps you'd like to make it more convenient for callers, by defaulting loglevel, and letting a caller just pass in __name__ if that's all that is interesting.

Overall, it looks like a useful utility.