50

I'm trying to create a basic logger that will be colored without external packages,

# these have to be the first functions so I can use it in the logger settings
def create_log_name(log_path="{}/log", filename="zeus-log-{}.log"):
    if not os.path.exists(log_path.format(os.getcwd())):
        os.mkdir(log_path.format(os.getcwd()))
    find_file_amount = len(os.listdir(log_path.format(os.getcwd())))
    full_log_path = "{}/{}".format(log_path.format(os.getcwd()), filename.format(find_file_amount + 1))
    return full_log_path


def set_color_value(levelname):
    log_set = {
        "INFO": "\033[92m{}\033[0m",
        "WARNING": "\033[93m{}\033[0m",
        "DEBUG": "\033[94m{}\033[0m",
        "ERROR": "\033[91m{}\033[0m",
        "CRITICAL": "\033[91m{}\033[0m"
    }
    return log_set[levelname].format(levelname)

logger = logging.getLogger("zeus-log")
logger.setLevel(logging.DEBUG)
file_handler = logging.FileHandler(
    filename=create_log_name(), mode="a+"
)
file_handler.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
file_format = logging.Formatter(
    '%(asctime)s;%(name)s;%(levelname)s;%(message)s'
)
console_format = logging.Formatter(
    "[%(asctime)s {}] %(message)s".format(set_color_value()), "%H:%M:%S"
)
file_handler.setFormatter(file_format)
console_handler.setFormatter(console_format)
logger.addHandler(console_handler)
logger.addHandler(file_handler)

So as of right now, all I need to do is get the current log level that will be set in the logging.Formatter and send it to my little function:

console_format = logging.Formatter(
    "[%(asctime)s {}] %(message)s".format(set_color_value()), "%H:%M:%S"
)

Is it possible to get the current log level from the logging package?


For example, lets say I pass logger.INFO("test") I need a way to get that INFO part in as a string, from there, set_color_value("INFO") should return:

enter image description here

vvvvv
  • 25,404
  • 19
  • 49
  • 81
wahwahwah
  • 773
  • 1
  • 6
  • 11

5 Answers5

55

If you're using the root logger, for example because you called logging.basicConfig() then you can use

import logging
logging.root.level

For example

if logging.DEBUG >= logging.root.level:
    # Do something
bcattle
  • 12,115
  • 6
  • 62
  • 82
  • 11
    It is better to use the `isEnabledFor()` function instead of an explicit comparision. See here: https://docs.python.org/3/howto/logging.html#optimization – Alexander Pozdneev Jul 26 '21 at 13:56
32

Yes, you can check the logger level by

level = logger.level
  • 1
    Doesn't this just show the level that the current logger is set to? I need a way to get the logging information from each string passed from the logger, for example, lets say that I pass `logger.FATAL("test")` I need that `FATAL` part passed as a string – wahwahwah Aug 28 '17 at 16:22
  • 6
    This answer answers what the question asked. Your question needs improvement since I arrived here looking for this answer. – Matthew Purdon Oct 25 '17 at 19:26
  • 4
    **Agreed.** The content of your question is at odds with the title of your question, @wahwahwah. The latter is likely of more interest to StackOverflowers (including myself). It *is* oddly non-orthogonal that the `logging` API defines `setLevel()` and `getEffectiveLevel()` methods but *no* `getLevel()` method, when they could have simply defined a trivial `getLevel(self): return self.level` method. `` – Cecil Curry Mar 09 '18 at 09:15
  • 3
    Whether I set this to `INFO` or `DEBUG` e.g. like this `logging.basicConfig(stream=sys.stdout, level='DEBUG')` the logger.level always shows me 0, not helpful. @CecilCurry What does "_oddly_ non-orthogonal" mean? In the context of [this definition of orthogonality](http://www.catb.org/~esr/writings/taoup/html/ch04s02.html#orthogonality) I am not sure – Davos May 15 '18 at 03:58
  • 1
    @CecilCurry Seconding your opinion. The documentation page of `logging` doesn't mention `level` could be directly accessed through the logger object. It only mentions `getEffectiveLevel` as the way to get the current log level. Nothing else could be found to achieve the same purpose. – Qiang Xu Sep 03 '20 at 14:15
  • This really should be `logger.getEffectiveLevel()` since if the `logger.level = 0` then it will retrieve the logging.root.level which is 30 by default (if it hasn't been modified). And therefore `logger.getEffectiveLevel() != logger.level` – Mark Apr 06 '21 at 17:25
21

As explained in this walk through the source code logger.level is often wrong.

You want logger.getEffectiveLevel()

To quote the source:

Here’s the takeaway: don’t rely on .level. If you haven’t explicitly set a level on your logger object, and you’re depending on .level for some reason, then your logging setup will likely behave differently than you expected it to.

EliadL
  • 6,230
  • 2
  • 26
  • 43
Dan MacNeil
  • 383
  • 2
  • 5
9

I decided to do this a different way and add color through the string itself with a level number:

def set_color(org_string, level=None):
    color_levels = {
        10: "\033[36m{}\033[0m",       # DEBUG
        20: "\033[32m{}\033[0m",       # INFO
        30: "\033[33m{}\033[0m",       # WARNING
        40: "\033[31m{}\033[0m",       # ERROR
        50: "\033[7;31;31m{}\033[0m"   # FATAL/CRITICAL/EXCEPTION
    }
    if level is None:
        return color_levels[20].format(org_string)
    else:
        return color_levels[int(level)].format(org_string)

So for example:

logger.info(set_color("test"))
logger.debug(set_color("test", level=10))
logger.warning(set_color("test", level=30))
logger.error(set_color("test", level=40))
logger.fatal(set_color("test", level=50))

Will output:

enter image description here

wahwahwah
  • 773
  • 1
  • 6
  • 11
  • You can also replace the default `logging.Formatter` with ColorFormatter subclass that has format method ``` COLOR_MAP = { DEBUG : 'white', <...> ERROR : 'red', } def format(self, record): msg = logging.Formatter.format(self, record) # Now set color based on our level, if mapped. Else leave as is if record.levelno in ColorFormatter.COLOR_MAP: msg = termcolor.colored(msg, ColorFormatter.COLOR_MAP[record.levelno]) return msg ``` – Evgen Aug 12 '21 at 02:09
8

In your logger instance you can check it like this, as @Milán Vásárhelyi said:

myLogger.level

That will return the level as int. if you prefer to show the name, as string, you can do:

logging.getLevelName(myLogger.level)