[Django]-Programmatically listen for log message sent to a Python/Django logger

5๐Ÿ‘

So after reading through a number of pages, I found that there were a few logging functions that could be useful to me. I especially liked the descriptions for addHandler, addFilter. Not quite as informative as some of the rest, nor more informative than the naming convention of the functions themselves. However, I do not maintain any public documentation, so can not complain.

  1. https://docs.djangoproject.com/en/dev/topics/logging/
  2. https://docs.python.org/2/library/logging.html#logger-objects
  3. https://docs.python.org/2/howto/logging-cookbook.html#filters-contextual
  4. http://www.onlamp.com/pub/a/python/2005/06/02/logging.html

After a bit of trial and error I put the following together. My first attempt was with addHandler but this seemed a lot more involved, and lacking in documentation, than addFilter. So basically the code sets up a fake logging filter, which listens to every log message, and appends them to an array further up the scope. It always returns true, so log messages should never be lost. Iโ€™m not sure if this is optimal, or if there is a better more semantic way, but it does indeed work.

import logging

messages = []
logger = logging.getLogger('shrubbery')

# use a filter to listen out for the log
class ListenFilter(logging.Filter):
    def filter(self, record):
        messages.append(record.getMessage())
        return True

# plug our filter listener in
f = ListenFilter()
logger.addFilter(f)

# do stuff
other_code_that_cant_be_altered_that_will_trigger_log_messages()

# do more stuff, this time reading messages
assert_messages_be_good_and_expected(messages)

# tidy up
logger.removeFilter(f)

2๐Ÿ‘

Thanks to the answer of Pebbl! Just wanted to add that it seems cleaner to use the contextmanager to install/remove the filter:

logger_ = logging.getLogger('some.external.logger')

from contextlib import contextmanager

@contextmanager
def install_remove_filter(logger_to_filter, filter_to_add_remove):
    logger_to_filter.addFilter(filter_to_add_remove)
    yield
    logger_to_filter.removeFilter(filter_to_add_remove)

# the filter above
my_filter = ListenFilter()

# installing/removing the filter in the context of the call
with install_remove_filter(logger_, my_filter):
    call_some_function()

# access the internals of my_filter
# .... 

Leave a comment