| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1846,8 +1846,11 @@ the use of a :class:`Filter` does not provide the desired result. | |||
| 1846 | 1846 | ||
| 1847 | 1847 | .. _zeromq-handlers: | |
| 1848 | 1848 | ||
| 1849 | - Subclassing QueueHandler - a ZeroMQ example | ||
| 1850 | - ------------------------------------------- | ||
| 1849 | + Subclassing QueueHandler and QueueListener- a ZeroMQ example | ||
| 1850 | + ------------------------------------------------------------ | ||
| 1851 | + | ||
| 1852 | + Subclass ``QueueHandler`` | ||
| 1853 | + ^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 1851 | 1854 | ||
| 1852 | 1855 | You can use a :class:`QueueHandler` subclass to send messages to other kinds | |
| 1853 | 1856 | of queues, for example a ZeroMQ 'publish' socket. In the example below,the | |
@@ -1885,8 +1888,8 @@ data needed by the handler to create the socket:: | |||
| 1885 | 1888 | self.queue.close() | |
| 1886 | 1889 | ||
| 1887 | 1890 | ||
| 1888 | - Subclassing QueueListener - a ZeroMQ example | ||
| 1889 | - -------------------------------------------- | ||
| 1891 | + Subclass ``QueueListener`` | ||
| 1892 | + ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 1890 | 1893 | ||
| 1891 | 1894 | You can also subclass :class:`QueueListener` to get messages from other kinds | |
| 1892 | 1895 | of queues, for example a ZeroMQ 'subscribe' socket. Here's an example:: | |
@@ -1903,25 +1906,134 @@ of queues, for example a ZeroMQ 'subscribe' socket. Here's an example:: | |||
| 1903 | 1906 | msg = self.queue.recv_json() | |
| 1904 | 1907 | return logging.makeLogRecord(msg) | |
| 1905 | 1908 | ||
| 1909 | + .. _pynng-handlers: | ||
| 1906 | 1910 | ||
| 1907 | - .. seealso:: | ||
| 1911 | + Subclassing QueueHandler and QueueListener- a ``pynng`` example | ||
| 1912 | + --------------------------------------------------------------- | ||
| 1908 | 1913 | ||
| 1909 | - Module :mod:`logging` | ||
| 1910 | - API reference for the logging module. | ||
| 1914 | + In a similar way to the above section, we can implement a listener and handler | ||
| 1915 | + using `pynng <https://pypi.org/project/pynng/>`_, which is a Python binding to | ||
| 1916 | + `NNG <https://nng.nanomsg.org/>`_, billed as a spiritual successor to ZeroMQ. | ||
| 1917 | + The following snippets illustrate -- you can test them in an environment which has | ||
| 1918 | + ``pynng`` installed. Juat for variety, we present the listener first. | ||
| 1911 | 1919 | ||
| 1912 | - Module :mod:`logging.config` | ||
| 1913 | - Configuration API for the logging module. | ||
| 1914 | 1920 | ||
| 1915 | - Module :mod:`logging.handlers` | ||
| 1916 | - Useful handlers included with the logging module. | ||
| 1921 | + Subclass ``QueueListener`` | ||
| 1922 | + ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 1923 | + | ||
| 1924 | + .. code-block:: python | ||
| 1925 | + | ||
| 1926 | + import json | ||
| 1927 | + import logging | ||
| 1928 | + import logging.handlers | ||
| 1929 | + | ||
| 1930 | + import pynng | ||
| 1917 | 1931 | ||
| 1918 | - :ref:`A basic logging tutorial <logging-basic-tutorial>` | ||
| 1932 | + DEFAULT_ADDR = "tcp://localhost:13232" | ||
| 1919 | 1933 | ||
| 1920 | - :ref:`A more advanced logging tutorial <logging-advanced-tutorial>` | ||
| 1934 | + interrupted = False | ||
| 1921 | 1935 | ||
| 1936 | + class NNGSocketListener(logging.handlers.QueueListener): | ||
| 1937 | + | ||
| 1938 | + def __init__(self, uri, /, *handlers, **kwargs): | ||
| 1939 | + # Have a timeout for interruptability, and open a | ||
| 1940 | + # subscriber socket | ||
| 1941 | + socket = pynng.Sub0(listen=uri, recv_timeout=500) | ||
| 1942 | + # The b'' subscription matches all topics | ||
| 1943 | + topics = kwargs.pop('topics', None) or b'' | ||
| 1944 | + socket.subscribe(topics) | ||
| 1945 | + # We treat the socket as a queue | ||
| 1946 | + super().__init__(socket, *handlers, **kwargs) | ||
| 1947 | + | ||
| 1948 | + def dequeue(self, block): | ||
| 1949 | + data = None | ||
| 1950 | + # Keep looping while not interrupted and no data received over the | ||
| 1951 | + # socket | ||
| 1952 | + while not interrupted: | ||
| 1953 | + try: | ||
| 1954 | + data = self.queue.recv(block=block) | ||
| 1955 | + break | ||
| 1956 | + except pynng.Timeout: | ||
| 1957 | + pass | ||
| 1958 | + except pynng.Closed: # sometimes hit when you hit Ctrl-C | ||
| 1959 | + break | ||
| 1960 | + if data is None: | ||
| 1961 | + return None | ||
| 1962 | + # Get the logging event sent from a publisher | ||
| 1963 | + event = json.loads(data.decode('utf-8')) | ||
| 1964 | + return logging.makeLogRecord(event) | ||
| 1965 | + | ||
| 1966 | + def enqueue_sentinel(self): | ||
| 1967 | + # Not used in this implementation, as the socket isn't really a | ||
| 1968 | + # queue | ||
| 1969 | + pass | ||
| 1970 | + | ||
| 1971 | + logging.getLogger('pynng').propagate = False | ||
| 1972 | + listener = NNGSocketListener(DEFAULT_ADDR, logging.StreamHandler(), topics=b'') | ||
| 1973 | + listener.start() | ||
| 1974 | + print('Press Ctrl-C to stop.') | ||
| 1975 | + try: | ||
| 1976 | + while True: | ||
| 1977 | + pass | ||
| 1978 | + except KeyboardInterrupt: | ||
| 1979 | + interrupted = True | ||
| 1980 | + finally: | ||
| 1981 | + listener.stop() | ||
| 1982 | + | ||
| 1983 | + | ||
| 1984 | + Subclass ``QueueHandler`` | ||
| 1985 | + ^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 1922 | 1986 | ||
| 1923 | 1987 | .. currentmodule:: logging | |
| 1924 | 1988 | ||
| 1989 | + .. code-block:: python | ||
| 1990 | + | ||
| 1991 | + import json | ||
| 1992 | + import logging | ||
| 1993 | + import logging.handlers | ||
| 1994 | + import time | ||
| 1995 | + import random | ||
| 1996 | + | ||
| 1997 | + import pynng | ||
| 1998 | + | ||
| 1999 | + DEFAULT_ADDR = "tcp://localhost:13232" | ||
| 2000 | + | ||
| 2001 | + class NNGSocketHandler(logging.handlers.QueueHandler): | ||
| 2002 | + | ||
| 2003 | + def __init__(self, uri): | ||
| 2004 | + socket = pynng.Pub0(dial=uri, send_timeout=500) | ||
| 2005 | + super().__init__(socket) | ||
| 2006 | + | ||
| 2007 | + def enqueue(self, record): | ||
| 2008 | + # Send the record as UTF-8 encoded JSON | ||
| 2009 | + d = dict(record.__dict__) | ||
| 2010 | + data = json.dumps(d) | ||
| 2011 | + self.queue.send(data.encode('utf-8')) | ||
| 2012 | + | ||
| 2013 | + def close(self): | ||
| 2014 | + self.queue.close() | ||
| 2015 | + | ||
| 2016 | + logging.getLogger('pynng').propagate = False | ||
| 2017 | + handler = NNGSocketHandler(DEFAULT_ADDR) | ||
| 2018 | + logging.basicConfig(level=logging.DEBUG, | ||
| 2019 | + handlers=[logging.StreamHandler(), handler], | ||
| 2020 | + format='%(levelname)-8s %(name)10s %(message)s') | ||
| 2021 | + levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, | ||
| 2022 | + logging.CRITICAL) | ||
| 2023 | + logger_names = ('myapp', 'myapp.lib1', 'myapp.lib2') | ||
| 2024 | + msgno = 1 | ||
| 2025 | + while True: | ||
| 2026 | + # Just randomly select some loggers and levels and log away | ||
| 2027 | + level = random.choice(levels) | ||
| 2028 | + logger = logging.getLogger(random.choice(logger_names)) | ||
| 2029 | + logger.log(level, 'Message no. %5d' % msgno) | ||
| 2030 | + msgno += 1 | ||
| 2031 | + delay = random.random() * 2 + 0.5 | ||
| 2032 | + time.sleep(delay) | ||
| 2033 | + | ||
| 2034 | + You can run the above two snippets in separate command shells. | ||
| 2035 | + | ||
| 2036 | + | ||
| 1925 | 2037 | An example dictionary-based configuration | |
| 1926 | 2038 | ----------------------------------------- | |
| 1927 | 2039 | ||
@@ -3418,7 +3530,7 @@ The worker thread is implemented using Qt's ``QThread`` class rather than the | |||
| 3418 | 3530 | :mod:`threading` module, as there are circumstances where one has to use | |
| 3419 | 3531 | ``QThread``, which offers better integration with other ``Qt`` components. | |
| 3420 | 3532 | ||
| 3421 | - The code should work with recent releases of either ``PySide6``, ``PyQt6``, | ||
| 3533 | + The code should work with recent releases of any of ``PySide6``, ``PyQt6``, | ||
| 3422 | 3534 | ``PySide2`` or ``PyQt5``. You should be able to adapt the approach to earlier | |
| 3423 | 3535 | versions of Qt. Please refer to the comments in the code snippet for more | |
| 3424 | 3536 | detailed information. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2013,7 +2013,7 @@ def basicConfig(**kwargs): | |||
| 2013 | 2013 | that this argument is incompatible with 'filename' - if both | |
| 2014 | 2014 | are present, 'stream' is ignored. | |
| 2015 | 2015 | handlers If specified, this should be an iterable of already created | |
| 2016 | - handlers, which will be added to the root handler. Any handler | ||
| 2016 | + handlers, which will be added to the root logger. Any handler | ||
| 2017 | 2017 | in the list which does not have a formatter assigned will be | |
| 2018 | 2018 | assigned the formatter created in this function. | |
| 2019 | 2019 | force If this keyword is specified as true, any existing handlers | |
| Back | FazBrowse Home | New Git URL |
0 commit comments