Skip to content

Commit 011260b

Browse files
[3.11] [docs] Update logging cookbook with recipe for using a logger like an output… (GH-97730) (GH-97735)
1 parent 8c528ef commit 011260b

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

Doc/howto/logging-cookbook.rst

+76
Original file line numberDiff line numberDiff line change
@@ -3428,6 +3428,82 @@ the above handler, you'd pass structured data using something like this::
34283428
i = 1
34293429
logger.debug('Message %d', i, extra=extra)
34303430

3431+
How to treat a logger like an output stream
3432+
-------------------------------------------
3433+
3434+
Sometimes, you need to interface to a third-party API which expects a file-like
3435+
object to write to, but you want to direct the API's output to a logger. You
3436+
can do this using a class which wraps a logger with a file-like API.
3437+
Here's a short script illustrating such a class:
3438+
3439+
.. code-block:: python
3440+
3441+
import logging
3442+
3443+
class LoggerWriter:
3444+
def __init__(self, logger, level):
3445+
self.logger = logger
3446+
self.level = level
3447+
3448+
def write(self, message):
3449+
if message != '\n': # avoid printing bare newlines, if you like
3450+
self.logger.log(self.level, message)
3451+
3452+
def flush(self):
3453+
# doesn't actually do anything, but might be expected of a file-like
3454+
# object - so optional depending on your situation
3455+
pass
3456+
3457+
def close(self):
3458+
# doesn't actually do anything, but might be expected of a file-like
3459+
# object - so optional depending on your situation. You might want
3460+
# to set a flag so that later calls to write raise an exception
3461+
pass
3462+
3463+
def main():
3464+
logging.basicConfig(level=logging.DEBUG)
3465+
logger = logging.getLogger('demo')
3466+
info_fp = LoggerWriter(logger, logging.INFO)
3467+
debug_fp = LoggerWriter(logger, logging.DEBUG)
3468+
print('An INFO message', file=info_fp)
3469+
print('A DEBUG message', file=debug_fp)
3470+
3471+
if __name__ == "__main__":
3472+
main()
3473+
3474+
When this script is run, it prints
3475+
3476+
.. code-block:: text
3477+
3478+
INFO:demo:An INFO message
3479+
DEBUG:demo:A DEBUG message
3480+
3481+
You could also use ``LoggerWriter`` to redirect ``sys.stdout`` and
3482+
``sys.stderr`` by doing something like this:
3483+
3484+
.. code-block:: python
3485+
3486+
import sys
3487+
3488+
sys.stdout = LoggerWriter(logger, logging.INFO)
3489+
sys.stderr = LoggerWriter(logger, logging.WARNING)
3490+
3491+
You should do this *after* configuring logging for your needs. In the above
3492+
example, the :func:`~logging.basicConfig` call does this (using the
3493+
``sys.stderr`` value *before* it is overwritten by a ``LoggerWriter``
3494+
instance). Then, you'd get this kind of result:
3495+
3496+
.. code-block:: pycon
3497+
3498+
>>> print('Foo')
3499+
INFO:demo:Foo
3500+
>>> print('Bar', file=sys.stderr)
3501+
WARNING:demo:Bar
3502+
>>>
3503+
3504+
Of course, these above examples show output according to the format used by
3505+
:func:`~logging.basicConfig`, but you can use a different formatter when you
3506+
configure logging.
34313507

34323508
.. patterns-to-avoid:
34333509

0 commit comments

Comments
 (0)