Reputation: 358
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)>]
Upvotes: 10
Views: 5872
Reputation: 2419
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.basicConfig
sets root
logger's level to WARNING
and add a StreamHandler
that output the log to stderr
.format
and used a shortcut basicConfig
to config the root
logger. You want the root logger to do all the actual logging thingsTemp
ERROR
level and above.StreamHandler
. Which output to stdout
by default.ERROR
level and aboveTemp
logger, which made 5.
redundant since the level is set in 3.
root
logger to do the job since 1.
!ERROR
with your logger.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 applies the format string to boo
. Added timestamp, added location, added 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
Since your code does exactly what you tell it to do, you have to tell it as much detail as possible.
basicConfig
when you are lazy. By removing basicConfig
line, your problem solved.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.propagate
property. In your case, logger.propagate = False
also solves the problem.root
and let the root do the actual logging. Why?
propagating=False
.root
logger. You have centralized control over the logging.Upvotes: 18
Reputation: 28
You could also just set the Logger "propogate" property to True.
Upvotes: 0