Solution
Add the filter to the handler rather than the logger:
handler.addFilter(logging.Filter('foo'))
Explanation
In the flow chart diagram you posted, notice there are two diamonds:
- Does a filter attached to logger reject the record?
- Does a filter attached to hander reject the record?
Thus, you get two swings at rejecting a LogRecord. If you attach the filter to the root logger, but initiate the LogRecord through, say, the foo or bar loggers, then the LogRecord does not get filtered because the LogRecord passes through the foo or bar loggers freely and the root logger filter never enters into play. (Look at the flow chart again.)
In contrast, the StreamHandler defined by basicConfig is capable of filtering any LogRecord passes to it.
So: add the filter to the handler rather than the logger:
# foo.py
import logging
import bar
logger = logging.getLogger('foo')
def foo():
logger.info('hello from foo')
if __name__ == '__main__':
# Trivial logging setup.
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(name)-20s %(levelname)-8s %(message)s",
datefmt="%m-%d %H:%M")
for handler in logging.root.handlers:
handler.addFilter(logging.Filter('foo'))
foo()
bar.bar()
yields
06-24 09:17 foo INFO hello from foo
If you want to allow logging from loggers whose name begins with foo or bar, but not from any other loggers, you could create a whitelist filter like this:
import logging
foo_logger = logging.getLogger('foo')
bar_logger = logging.getLogger('bar')
baz_logger = logging.getLogger('baz')
class Whitelist(logging.Filter):
def __init__(self, *whitelist):
self.whitelist = [logging.Filter(name) for name in whitelist]
def filter(self, record):
return any(f.filter(record) for f in self.whitelist)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(name)-20s %(levelname)-8s %(message)s",
datefmt="%m-%d %H:%M")
for handler in logging.root.handlers:
handler.addFilter(Whitelist('foo', 'bar'))
foo_logger.info('hello from foo')
# 06-24 09:41 foo INFO hello from foo
bar_logger.info('hello from bar')
# 06-24 09:41 bar INFO hello from bar
baz_logger.info('hello from baz')
# No output since Whitelist filters if record.name not begin with 'foo' or 'bar'
And similarly, you could blacklist logger names with this:
class Blacklist(Whitelist):
def filter(self, record):
return not Whitelist.filter(self, record)