Alexy Grabov
Alexy Grabov

Reputation: 151

Using variable-controlled logging level in Python logger

I want to write a function that will get the requested logging level from the user, i.e. something like:

import logging

logger = logging.getLogger()

def func(log_level: <Type?>):
    logger.log_level("everything is bad")

Can this be done?

Upvotes: 2

Views: 1944

Answers (3)

Vinay Sajip
Vinay Sajip

Reputation: 99415

Just use the log() method of a logger, which takes a level as well as a format string and arguments.

import logging

logger = logging.getLogger()

def func(log_level: int, message: str):
    logger.log(log_level, message)
    
logging.basicConfig(level=logging.DEBUG, format='%(levelname)-8s %(message)s')

func(logging.DEBUG, 'message at DEBUG level')
func(logging.INFO, 'message at INFO level')
func(logging.CRITICAL, 'message at CRITICAL level')

Which prints

DEBUG    message at DEBUG level
INFO     message at INFO level
CRITICAL message at CRITICAL level

when run.

Upvotes: 2

Olgierd Wiśniewski
Olgierd Wiśniewski

Reputation: 553

You can use the context manager, I quote for the documentation "There are times when it would be useful to temporarily change the logging configuration and revert it back after doing something. For this, a context manager is the most obvious way of saving and restoring the logging context. Here is a simple example of such a context manager, which allows you to optionally change the logging level and add a logging handler purely in the scope of the context manager:"

import logging
import sys

class LoggingContext:
    def __init__(self, logger, level=None, handler=None, close=True):
        self.logger = logger
        self.level = level
        self.handler = handler
        self.close = close

    def __enter__(self):
        if self.level is not None:
            self.old_level = self.logger.level
            self.logger.setLevel(self.level)
        if self.handler:
            self.logger.addHandler(self.handler)

    def __exit__(self, et, ev, tb):
        if self.level is not None:
            self.logger.setLevel(self.old_level)
        if self.handler:
            self.logger.removeHandler(self.handler)
        if self.handler and self.close:
            self.handler.close()
        # implicit return of None => don't swallow exceptions

Exactly in the documentation, it is here: https://docs.python.org/3/howto/logging-cookbook.html#using-a-context-manager-for-selective-logging . You can also rewrite any of the sample code snippets from: https://docs.python.org/3/howto/logging-cookbook.html#using-logging-in-multiple-modules .

Upvotes: 0

ot98ikyq
ot98ikyq

Reputation: 69

You could use this. It first looks a little verbose, but you can easily add addtional handlers (maybe some that log to a file...)

import logging

logger = logging.getLogger()
handler = logging.StreamHandler() #you can also use FileHandler here, but then you need to specify a path
handler.setLevel(logging.NOTSET)

LOG_FORMAT = "%(levelname)s: %(message)s"
formatter = logging.Formatter(LOG_FORMAT)

handler.setFormatter(formatter)
logger.addHandler(handler)

logger.log(logging.CRITICAL, "your logging message 1") #==> CRITICAL: your logging message 1
logger.log(logging.WARN,     "your logging message 2") #==> WARN: your logging message 2

Note 1: LOG_FORMAT is defined according to the logging-documentation

Note 2: the logging levels (logging.DEBUG, logging.INFO...) are explained here. You can also use integer values accordingly:

logging.XX Value
NOTSET 0
DEBUG 10
INFO 20
WARN 30
ERROR 40
CRITICAL 50

Upvotes: 1

Related Questions