Reputation: 29957
I want to use httpimport
as a logging library common to several scripts. This module generates logs of its own which I do not know how to silence.
In other cases such as this one, I would have used
logging.getLogger('httpimport').setLevel(logging.ERROR)
but it did not work.
The following code is a stub of the "common logging code" mentioned above:
# toconsole.py
import logging
import os
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s %(message)s')
handler_console = logging.StreamHandler()
level = logging.DEBUG if 'DEV' in os.environ else logging.INFO
handler_console.setLevel(level)
handler_console.setFormatter(formatter)
log.addHandler(handler_console)
# disable httpimport logging except for errors+
logging.getLogger('httpimport').setLevel(logging.ERROR)
A simple usage such as
import httpimport
httpimport.INSECURE = True
with httpimport.remote_repo(['githublogging'], 'http://localhost:8000/') :
from toconsole import log
log.info('yay!')
gives the following output
[!] Using non HTTPS URLs ('http://localhost:8000//') can be a security hazard!
2019-08-25 13:56:48,671 yay!
yay!
The second (bare) yay!
must be coming from httpimport
, namely from its logging setup.
How can I disable the logging for such a module, or better - raise its level so that only errors+ are logged?
Note: this question was initially asked at the Issues section of the GitHub repository for httpimport
but the author did not know either how to fix that.
Upvotes: 1
Views: 4239
Reputation: 391
Author of httpimport
here.
I totally forgot I was using the basicConfig
logger
thing.
It is fixed in master
right now (0.7.2) - will be included in next PyPI release:
https://github.com/operatorequals/httpimport/commit/ff2896c8f666c3f16b0f27716c732d68be018ef7
Upvotes: 6
Reputation: 1783
The reason why this is happening is because when you do import httpimport
they do the initial configuration for the logging machinery. This happens right here. What this means is that the root logger already has a StreamHandler
attached to it. Because of this, and the fact that all loggers inherit from the root logger, when you do log.info('yay')
it not only uses your Handler
and Formatter
, but it also propagates all they way to the root logger, which also emits the message.
Remember that whoever calls basicConfig
first when an application starts that sets up the default configuration for the root logger, which in turn, is inherited by all loggers, unless otherwise specified.
If you have a complex logging configuration you need to ensure that you call it before you do any third-party imports which might call basicConfig
. basicConfig
is idempotent meaning the first call seals the deal, and subsequent calls have no effect.
log.propagate = False
and you will see that the 2nd yay will not show.Formatter
directly to the already existent root Handler
by doing something like this (without adding another Handler
yourself)root = logging.getLogger('')
formatter = logging.Formatter('%(asctime)s %(message)s')
root_handler = root.handlers[0]
root_handler.setFormatter(formatter)
You could do a basicConfig
call when you initialize your application (if you had such a config available, with initial Formatters
and Handlers
, etc. that will elegantly attach everything to the root logger neatly) and then you would only do something like logger = logging.getLogger(__name__)
and logger.info('some message')
that would work the way you'd expect because it would propagate all the way to the root logger which already has your configuration.
You could remove the initial Handler
that's present on the root logger by doing something like
root = logging.getLogger('')
root.handlers = []
... and many more solutions, but you get the idea.
Also do note that logging.getLogger('httpimport').setLevel(logging.ERROR)
this works perfectly fine. No messages below logging.ERROR
would be logged by that logger, it's just that the problem wasn't from there.
If you however want to completely disable a logger you can just do logger.disabled = True
(also do note, again, that the problem wasn't from the httpimport
logger, as aforementioned)
Change your toconsole.py with this and you won't see the second yay.
import logging
import os
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
root_logger = logging.getLogger('')
root_handler = root_logger.handlers[0]
formatter = logging.Formatter('%(asctime)s %(message)s')
root_handler.setFormatter(formatter)
# or you could just keep your old code and just add log.propagate = False
# or any of the above solutions and it would work
logging.getLogger('httpimport').setLevel(logging.ERROR)
Upvotes: 4