I\'m using Python logging, and for some reason, all of my messages are appearing twice.
I have a module to configure logging:
# BUG: It\'s outputting
A call to logging.debug()
calls logging.basicConfig()
if there are no root handlers installed. That was happening for me in a test framework where I couldn't control the order that test cases fired. My initialization code was installing the second one. The default uses logging.BASIC_FORMAT that I didn't want.
It seems that if you output something to the logger (accidentally) then configure it, it is too late. For example, in my code I had
logging.warning("look out)"
...
ch = logging.StreamHandler(sys.stdout)
root = logging.getLogger()
root.addHandler(ch)
root.info("hello")
I would get something like (ignoring the format options)
look out
hello
hello
and everything was written to stdout twice. I believe this is because the first call to logging.warning
creates a new handler automatically, and then I explicitly added another handler. The problem went away when I removed the accidental first logging.warning
call.
I'm a python newbie, but this seemed to work for me (Python 2.7)
while logger.handlers:
logger.handlers.pop()
I was getting a strange situation where console logs were doubled but my file logs were not. After a ton of digging I figured it out.
Please be aware that third party packages can register loggers. This is something to watch out for (and in some cases can't be prevented). In many cases third party code checks to see if there are any existing root logger handlers; and if there isn't--they register a new console handler.
My solution to this was to register my console logger at the root level:
rootLogger = logging.getLogger() # note no text passed in--that's how we grab the root logger
if not rootLogger.handlers:
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(logging.Formatter('%(process)s|%(levelname)s] %(message)s'))
rootLogger.addHandler(ch)
You are calling configure_logging
twice (maybe in the __init__
method of Boy
) : getLogger
will return the same object, but addHandler
does not check if a similar handler has already been added to the logger.
Try tracing calls to that method and eliminating one of these. Or set up a flag logging_initialized
initialized to False
in the __init__
method of Boy
and change configure_logging
to do nothing if logging_initialized
is True
, and to set it to True
after you've initialized the logger.
If your program creates several Boy
instances, you'll have to change the way you do things with a global configure_logging
function adding the handlers, and the Boy.configure_logging
method only initializing the self.logger
attribute.
Another way of solving this is by checking the handlers attribute of your logger:
logger = logging.getLogger('my_logger')
if not logger.handlers:
# create the handlers and call logger.addHandler(logging_handler)
In my case I'd to set logger.propagate = False
to prevent double printing.
In below code if you remove logger.propagate = False
then you will see double printing.
import logging
from typing import Optional
_logger: Optional[logging.Logger] = None
def get_logger() -> logging.Logger:
global _logger
if _logger is None:
raise RuntimeError('get_logger call made before logger was setup!')
return _logger
def set_logger(name:str, level=logging.DEBUG) -> None:
global _logger
if _logger is not None:
raise RuntimeError('_logger is already setup!')
_logger = logging.getLogger(name)
_logger.handlers.clear()
_logger.setLevel(level)
ch = logging.StreamHandler()
ch.setLevel(level)
# warnings.filterwarnings("ignore", "(Possibly )?corrupt EXIF data", UserWarning)
ch.setFormatter(_get_formatter())
_logger.addHandler(ch)
_logger.propagate = False # otherwise root logger prints things again
def _get_formatter() -> logging.Formatter:
return logging.Formatter(
'[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s')