@@ -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.
0 commit comments