简体   繁体   中英

How to avoid root handler being called from the custom logger in Python?

I have a basic config for the logging module with debug level - now I want to create another logger with error level only. How can I do that?

The problem is that the root handler is called in addition to the error-handler - this is something I want to avoid.

import logging
fmt = '%(asctime)s:%(funcName)s:%(lineno)d:%(levelname)s:%(name)s:%(message)s'
logging.basicConfig(level=logging.DEBUG, format=fmt)

logger = logging.getLogger('Temp')
logger.setLevel(logging.ERROR)
handler = logging.StreamHandler()
handler.setLevel(logging.ERROR)
logger.addHandler(handler)

logger.error('boo')

The above code prints boo twice while I expect once only, and I have no idea what to do with this annoying issue...

In [4]: logger.error('boo')
boo
2021-04-26 18:54:24,329:<module>:1:ERROR:Temp:boo

In [5]: logger.handlers
Out[5]: [<StreamHandler stderr (ERROR)>]

Some basics about the logging module

  • logger: a person who receives the log string, sorts it by a predefined level, then uses his own handler if any to process the log and, by default passes the log to his superior.
  • root logger: the superior of superiors, does all the things that a normal logger does but doesn't pass the received log to anyone else.
  • handler : a private contractor of a logger, who actually does anything with the log, eg. formats the log, writes it to a file or stdout , or sends it through tcp/udp.
  • formatter : a theme, a design that the handler applies to the log.
  • basicConfig : a shortcut way to config the root logger. This is useful when you want him to do all the job and all his lower rank loggers would just pass the log to him.
    With no argument, basicConfig sets root logger's level to WARNING and add a StreamHandler that output the log to stderr .

What you did

  1. You created a format and used a shortcut basicConfig to config the root logger. You want the root logger to do all the actual logging things
  2. You created a new low-rank logger Temp
  3. You want it to accept logs with only ERROR level and above.
  4. You created another StreamHandler . Which output to stdout by default.
  5. You want it to handle only ERROR level and above
  6. Oh, you assigned it to Temp logger, which made 5. redundant since the level is set in 3.
    Oh wait, thought you just want the root logger to do the job since 1. !
  7. You logged an ERROR with your logger.

What happened

Your Temp logger accepted a string boo at ERROR level. Then told its handler to process the string. Since this handler didn't have any formatter assigned to it, it outputted the string as-is to stdout : boo
After that, Temp logger passed the string boo to his superior, the root logger.

The root logger accepted the log since the log level is ERROR > WARNING . The root logger then told its handler to process the string boo . This handler apply the format string to boo . Add timestamp, add location, add the name of logger that passed the log, etc.
Finally it outputted the result to stderr : 2021-04-26 18:54:24,329:<module>:1:ERROR:Temp:boo

Make it right

Since your code does exactly what you tell it to do, you have to tell it as much detail as possible.

  • Only use basicConfig when you are lazy. By removing basicConfig line, your problem solved.
  • Use logger = logging.getLogger('__name__') so that the logger has the name of the module. Looking at the log and know exactly which import path that it came from.
  • Decide if a logger should keep the log for it own or pass it up the chain with propagate property. In your case, logger.propagate = False also solves the problem.
  • Use a dictConfig file so you don't get messed with the config code.
  • In practice, you should not add handlers to your logger, and just let the logger pass the log all the way to the root and let the root do the actual logging. Why?
    • Someone else uses your code as a module, can control the logging. For example, not output to stdout but to tcp/udp, output with a different format, etc.
    • You can turn off the logging from a specific logger entirely, by propagating=False .
    • You know exactly all the handlers and formatters in the code if you only added them to the root logger. You have a centralized control over the logging.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM