Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c334202

Browse files
committed
Merged documentation update from 3.5.
2 parents 00f4648 + d93a601 commit c334202

1 file changed

Lines changed: 102 additions & 0 deletions

File tree

Doc/howto/logging-cookbook.rst

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2409,3 +2409,105 @@ When this script is run, it should print something like::
24092409

24102410
showing how the time is formatted both as local time and UTC, one for each
24112411
handler.
2412+
2413+
2414+
.. _context-manager:
2415+
2416+
Using a context manager for selective logging
2417+
---------------------------------------------
2418+
2419+
There are times when it would be useful to temporarily change the logging
2420+
configuration and revert it back after doing something. For this, a context
2421+
manager is the most obvious way of saving and restoring the logging context.
2422+
Here is a simple example of such a context manager, which allows you to
2423+
optionally change the logging level and add a logging handler purely in the
2424+
scope of the context manager::
2425+
2426+
import logging
2427+
import sys
2428+
2429+
class LoggingContext(object):
2430+
def __init__(self, logger, level=None, handler=None, close=True):
2431+
self.logger = logger
2432+
self.level = level
2433+
self.handler = handler
2434+
self.close = close
2435+
2436+
def __enter__(self):
2437+
if self.level is not None:
2438+
self.old_level = self.logger.level
2439+
self.logger.setLevel(self.level)
2440+
if self.handler:
2441+
self.logger.addHandler(self.handler)
2442+
2443+
def __exit__(self, et, ev, tb):
2444+
if self.level is not None:
2445+
self.logger.setLevel(self.old_level)
2446+
if self.handler:
2447+
self.logger.removeHandler(self.handler)
2448+
if self.handler and self.close:
2449+
self.handler.close()
2450+
# implicit return of None => don't swallow exceptions
2451+
2452+
If you specify a level value, the logger's level is set to that value in the
2453+
scope of the with block covered by the context manager. If you specify a
2454+
handler, it is added to the logger on entry to the block and removed on exit
2455+
from the block. You can also ask the manager to close the handler for you on
2456+
block exit - you could do this if you don't need the handler any more.
2457+
2458+
To illustrate how it works, we can add the following block of code to the
2459+
above::
2460+
2461+
if __name__ == '__main__':
2462+
logger = logging.getLogger('foo')
2463+
logger.addHandler(logging.StreamHandler())
2464+
logger.setLevel(logging.INFO)
2465+
logger.info('1. This should appear just once on stderr.')
2466+
logger.debug('2. This should not appear.')
2467+
with LoggingContext(logger, level=logging.DEBUG):
2468+
logger.debug('3. This should appear once on stderr.')
2469+
logger.debug('4. This should not appear.')
2470+
h = logging.StreamHandler(sys.stdout)
2471+
with LoggingContext(logger, level=logging.DEBUG, handler=h, close=True):
2472+
logger.debug('5. This should appear twice - once on stderr and once on stdout.')
2473+
logger.info('6. This should appear just once on stderr.')
2474+
logger.debug('7. This should not appear.')
2475+
2476+
We initially set the logger's level to ``INFO``, so message #1 appears and
2477+
message #2 doesn't. We then change the level to ``DEBUG`` temporarily in the
2478+
following ``with`` block, and so message #3 appears. After the block exits, the
2479+
logger's level is restored to ``INFO`` and so message #4 doesn't appear. In the
2480+
next ``with`` block, we set the level to ``DEBUG`` again but also add a handler
2481+
writing to ``sys.stdout``. Thus, message #5 appears twice on the console (once
2482+
via ``stderr`` and once via ``stdout``). After the ``with`` statement's
2483+
completion, the status is as it was before so message #6 appears (like message
2484+
#1) whereas message #7 doesn't (just like message #2).
2485+
2486+
If we run the resulting script, the result is as follows::
2487+
2488+
$ python logctx.py
2489+
1. This should appear just once on stderr.
2490+
3. This should appear once on stderr.
2491+
5. This should appear twice - once on stderr and once on stdout.
2492+
5. This should appear twice - once on stderr and once on stdout.
2493+
6. This should appear just once on stderr.
2494+
2495+
If we run it again, but pipe ``stderr`` to ``/dev/null``, we see the following,
2496+
which is the only message written to ``stdout``::
2497+
2498+
$ python logctx.py 2>/dev/null
2499+
5. This should appear twice - once on stderr and once on stdout.
2500+
2501+
Once again, but piping ``stdout`` to ``/dev/null``, we get::
2502+
2503+
$ python logctx.py >/dev/null
2504+
1. This should appear just once on stderr.
2505+
3. This should appear once on stderr.
2506+
5. This should appear twice - once on stderr and once on stdout.
2507+
6. This should appear just once on stderr.
2508+
2509+
In this case, the message #5 printed to ``stdout`` doesn't appear, as expected.
2510+
2511+
Of course, the approach described here can be generalised, for example to attach
2512+
logging filters temporarily. Note that the above code works in Python 2 as well
2513+
as Python 3.

0 commit comments

Comments
 (0)