diff options
author | Vinay Sajip <vinay_sajip@yahoo.co.uk> | 2022-10-02 13:26:14 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-02 13:26:14 (GMT) |
commit | cac2e8a51fe6412b7bc864274a60927ececfd833 (patch) | |
tree | ffc5c0985d710d288dcdcded715f510d67fc1d61 /Doc/howto/logging-cookbook.rst | |
parent | e8165d47b852e933c176209ddc0b5836a9b0d5f4 (diff) | |
download | cpython-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.rst | 76 |
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: |