+ """This is a logging context that can be used to temporarily change the
+ way we are logging within its scope. Logging changes may include:
+
+ - Changing the logging level (e.g. from INFO to DEBUG)
+ - Adding a prefix or suffix to every log message produced
+ - Adding temporary Handlers to direct the logging output elsewhere
+
+ Setup for doctest / examples. This will normally be taken care of
+ by code in :meth:`initialize_logging` so you don't have to worry
+ about it.
+
+ >>> logging_format = "%(prefix)s%(message)s%(suffix)s"
+ >>> logger = logging.getLogger(__name__ + ".LoggingContext")
+ >>> logger.setLevel(logging.INFO)
+ >>> handler = logging.StreamHandler(sys.stdout)
+ >>> handler.setFormatter(
+ ... MillisecondAwareFormatter(
+ ... fmt=logging_format,
+ ... datefmt='',
+ ... )
+ ... )
+ >>> logger.addHandler(handler)
+ >>> logger.addFilter(PrefixAddingFilter(None))
+ >>> logger.addFilter(SuffixAddingFilter(None))
+
+ First, this logger should be currently be configured to send
+ INFO+ messages to sys.stdout. Let's see it in action:
+
+ >>> logger.info("Hello world!")
+ Hello world!
+ >>> logger.debug("You should not see this")
+
+ The first example is to simply change the level of the logger.
+ Here we temporarily change it to DEBUG within the body of the
+ :class:`LoggingContext`:
+
+ >>> with LoggingContext(logger, level=logging.DEBUG):
+ ... logger.debug("You should see this now")
+ ... logger.info("Of course you should still see this too")
+ You should see this now
+ Of course you should still see this too
+
+ >>> logger.debug("Outside of the context we are at INFO again")
+ >>> logger.debug("(which is why you don't see these)")
+ >>> logger.info("But you should see this at INFO level")
+ But you should see this at INFO level
+
+ The prefix and suffix argument prepend or append a message to
+ all log output. To do this, you need %(prefix)s and
+ %(suffix)s placeholders in your logger format string
+ indicating where to insert the data. This is useful, for
+ example, to add an active request identifier to the set of log
+ messages produced while processing it.
+
+ >>> logger.info("About to work on a new request")
+ About to work on a new request
+
+ >>> with LoggingContext(logger, prefix='10.0.0.13> '):
+ ... logger.info("Working on it now")
+ 10.0.0.13> Working on it now
+
+ >>> logger.info("Done with that request")
+ Done with that request
+
+ LoggingContext can also be used to add temporary handler(s).
+ This code temporarily uses two stdout handlers to double the
+ output for testing purporses but you could also temporarily,
+ e.g., add a RotatingFileHandler or SysLogHandler etc...
+
+ >>> with LoggingContext(logger, handlers=[logging.StreamHandler(sys.stdout)]):
+ ... logger.info("TEST")
+ TEST
+ TEST
+
+ Once leaving the context, logger's behavior is restored. In
+ this case, the extra handler is removed so output will not
+ longer be doubled.
+
+ >>> logger.info("OUTSIDE")
+ OUTSIDE
+
+ LoggingContext can also be used as a decorator if that is more
+ convenient:
+
+ >>> @LoggingContext(logger, level=logging.DEBUG)
+ ... def log_stuff(logger):
+ ... logger.debug("But inside, the decorator has changed us to DEBUG")
+
+ >>> logger.debug("Outside, we're at INFO level and you don't see this")
+ >>> log_stuff(logger)
+ But inside, the decorator has changed us to DEBUG
+ >>> logger.debug("And, of course, out here we're still at INFO afterwards")