Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 011260b

Browse filesBrowse 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
Copy full SHA for 011260b

File tree

Expand file treeCollapse file tree

1 file changed

+76
-0
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+76
-0
lines changed

‎Doc/howto/logging-cookbook.rst

Copy file name to clipboardExpand all lines: Doc/howto/logging-cookbook.rst
+76Lines changed: 76 additions & 0 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.