I'm surprised by the output from Python's logging module. I wrote up the Python.org's How-To for Logging. When I run the sample code, there is a lot of (confusing) duplication.
The StreamHandler duplicates the log messages, one for each time I reload the file in iPython with the magic %run
In [4]: %run main.py
2018-05-11 2127:33 - WARNING - 3. This is a warning, yous!
2018-05-11 2127:33 - ERROR - 4. Here is an error
2018-05-11 2127:33 - CRITICAL - 5. This is f-ing critical!
[...]
In [7]: %run main.py
2018-05-11 2127:38 - WARNING - 3. This is a warning, yous!
2018-05-11 2127:38 - WARNING - 3. This is a warning, yous!
2018-05-11 2127:38 - WARNING - 3. This is a warning, yous!
2018-05-11 2127:38 - ERROR - 4. Here is an error
2018-05-11 2127:38 - ERROR - 4. Here is an error
2018-05-11 2127:38 - ERROR - 4. Here is an error
2018-05-11 2127:38 - CRITICAL - 5. This is f-ing critical!
2018-05-11 2127:38 - CRITICAL - 5. This is f-ing critical!
2018-05-11 2127:38 - CRITICAL - 5. This is f-ing critical!
I added a FileHandler:
fh = logging.FileHandler("app.log")
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(fh)
This also duplicates:
xtian@spaceghost> cat app.log
2018-05-11 2159:24 - WARNING - 3. This is a warning!
2018-05-11 2159:24 - ERROR - 4. This is an error
2018-05-11 2159:24 - CRITICAL - 5. This is fucking critical!
[...]
2018-05-11 2201:00 - WARNING - 3. This is a warning, yous!
2018-05-11 2201:00 - WARNING - 3. This is a warning, yous!
2018-05-11 2201:00 - WARNING - 3. This is a warning, yous!
2018-05-11 2201:00 - ERROR - 4. Here is an error.
2018-05-11 2201:00 - ERROR - 4. Here is an error.
2018-05-11 2201:00 - ERROR - 4. Here is an error.
2018-05-11 2201:00 - CRITICAL - 5. This is f-ing critical!
2018-05-11 2201:00 - CRITICAL - 5. This is f-ing critical!
2018-05-11 2201:00 - CRITICAL - 5. This is f-ing critical!
I also followed the suggestions from other posts, and added these lines just before the message calls:
# propagation fix
logger.propagate = False
The results are the same.
Setting up to post, I see a similar question:
What could cause the logging module to log a record multiple times?
But all this debugging is for the OP's original custom code. My issue is with the sample code, which I expect should warn or explain better what's happening.
The docs say,
Note. If you attach a handler to a logger and one or more of its ancestors, it may emit the same record multiple times. In general, you should not need to attach a handler to more than one logger - if you just attach it to the appropriate logger which is highest in the logger hierarchy, then it will see all events logged by all descendant loggers, provided that their propagate setting is left set to True. A common scenario is to attach handlers only to the root logger, and to let propagation take care of the rest.
You can see the full test file main.py
here to see if I have 'more than one logger'.
import logging
# Root Logger
logger = logging.getLogger(__name__)
# Console handler
ch = logging.StreamHandler()
ch.setLevel(logging.WARNING)
# Formatter
formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(message)s' , datefmt='%Y-%m-%d %H%M:%S', style='%')
# Add formatter to Console handler ch
ch.setFormatter(formatter)
# Add ch to logger
logger.addHandler(ch)
# Text File handler
fh = logging.FileHandler("app.log")
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(fh)
# propagation fix
logger.propagate = False
# Example Application code
logger.debug("1. This is a debug message.")
logger.info("2. This is an info message.")
logger.warning('3. This is a warning!')
logger.error('4. This is an error')
logger.critical("5. This is fucking critical!")
The
logger
returned bylogging.getLogger(__name__)
is the same logger each time%run main.py
is executed. Butinstantiates a new StreamHandler each time, which is then added to the
logger
:Thus, on subsequent runs of
%run main.py
, thelogger
has multiple handlers attached to it and a record gets emitted by each handler.The first time
%run main.py
is run, two handlers are attached tologger
:The second time, there are now four handlers:
To prevent duplication, you could call
logger.removeHandler
between%run
calls:or, modify
main.py
so that new handlers are not attached each time%run
is called. For example, you could setuplogger
usinglogging.config.dictConfig
:Using this code,
%run main.py
emits the same output each time.