summaryrefslogtreecommitdiffstats
path: root/Doc/howto/logging-cookbook.rst
diff options
context:
space:
mode:
authorVinay Sajip <vinay_sajip@yahoo.co.uk>2022-10-02 13:26:14 (GMT)
committerGitHub <noreply@github.com>2022-10-02 13:26:14 (GMT)
commitcac2e8a51fe6412b7bc864274a60927ececfd833 (patch)
treeffc5c0985d710d288dcdcded715f510d67fc1d61 /Doc/howto/logging-cookbook.rst
parente8165d47b852e933c176209ddc0b5836a9b0d5f4 (diff)
downloadcpython-cac2e8a51fe6412b7bc864274a60927ececfd833.zip
cpython-cac2e8a51fe6412b7bc864274a60927ececfd833.tar.gz
cpython-cac2e8a51fe6412b7bc864274a60927ececfd833.tar.bz2
[docs] Update logging cookbook with recipe for using a logger like an output… (GH-97730)
Diffstat (limited to 'Doc/howto/logging-cookbook.rst')
-rw-r--r--Doc/howto/logging-cookbook.rst76
1 files changed, 76 insertions, 0 deletions
diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst
index 5b07974..ff7ba07 100644
--- a/Doc/howto/logging-cookbook.rst
+++ b/Doc/howto/logging-cookbook.rst
@@ -3428,6 +3428,82 @@ the above handler, you'd pass structured data using something like this::
i = 1
logger.debug('Message %d', i, extra=extra)
+How to treat a logger like an output stream
+-------------------------------------------
+
+Sometimes, you need to interface to a third-party API which expects a file-like
+object to write to, but you want to direct the API's output to a logger. You
+can do this using a class which wraps a logger with a file-like API.
+Here's a short script illustrating such a class:
+
+.. code-block:: python
+
+ import logging
+
+ class LoggerWriter:
+ def __init__(self, logger, level):
+ self.logger = logger
+ self.level = level
+
+ def write(self, message):
+ if message != '\n': # avoid printing bare newlines, if you like
+ self.logger.log(self.level, message)
+
+ def flush(self):
+ # doesn't actually do anything, but might be expected of a file-like
+ # object - so optional depending on your situation
+ pass
+
+ def close(self):
+ # doesn't actually do anything, but might be expected of a file-like
+ # object - so optional depending on your situation. You might want
+ # to set a flag so that later calls to write raise an exception
+ pass
+
+ def main():
+ logging.basicConfig(level=logging.DEBUG)
+ logger = logging.getLogger('demo')
+ info_fp = LoggerWriter(logger, logging.INFO)
+ debug_fp = LoggerWriter(logger, logging.DEBUG)
+ print('An INFO message', file=info_fp)
+ print('A DEBUG message', file=debug_fp)
+
+ if __name__ == "__main__":
+ main()
+
+When this script is run, it prints
+
+.. code-block:: text
+
+ INFO:demo:An INFO message
+ DEBUG:demo:A DEBUG message
+
+You could also use ``LoggerWriter`` to redirect ``sys.stdout`` and
+``sys.stderr`` by doing something like this:
+
+.. code-block:: python
+
+ import sys
+
+ sys.stdout = LoggerWriter(logger, logging.INFO)
+ sys.stderr = LoggerWriter(logger, logging.WARNING)
+
+You should do this *after* configuring logging for your needs. In the above
+example, the :func:`~logging.basicConfig` call does this (using the
+``sys.stderr`` value *before* it is overwritten by a ``LoggerWriter``
+instance). Then, you'd get this kind of result:
+
+.. code-block:: pycon
+
+ >>> print('Foo')
+ INFO:demo:Foo
+ >>> print('Bar', file=sys.stderr)
+ WARNING:demo:Bar
+ >>>
+
+Of course, these above examples show output according to the format used by
+:func:`~logging.basicConfig`, but you can use a different formatter when you
+configure logging.
.. patterns-to-avoid: