Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0637d49

Browse files
committed
logging: added QueueListener and documentation.
1 parent d12a420 commit 0637d49

3 files changed

Lines changed: 207 additions & 4 deletions

File tree

Doc/library/logging.rst

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2651,12 +2651,18 @@ The :class:`QueueHandler` class, located in the :mod:`logging.handlers` module,
26512651
supports sending logging messages to a queue, such as those implemented in the
26522652
:mod:`queue` or :mod:`multiprocessing` modules.
26532653

2654+
Along with the :class:`QueueListener` class, :class:`QueueHandler` can be used
2655+
to let handlers do their work on a separate thread from the one which does the
2656+
logging. This is important in Web applications and also other service
2657+
applications where threads servicing clients need to respond as quickly as
2658+
possible, while any potentially slow operations (such as sending an email via
2659+
:class:`SMTPHandler`) are done on a separate thread.
26542660

26552661
.. class:: QueueHandler(queue)
26562662

26572663
Returns a new instance of the :class:`QueueHandler` class. The instance is
26582664
initialized with the queue to send messages to. The queue can be any queue-
2659-
like object; it's passed as-is to the :meth:`enqueue` method, which needs
2665+
like object; it's used as-is by the :meth:`enqueue` method, which needs
26602666
to know how to send messages to it.
26612667

26622668

@@ -2688,8 +2694,80 @@ supports sending logging messages to a queue, such as those implemented in the
26882694

26892695
The :class:`QueueHandler` class was not present in previous versions.
26902696

2697+
.. queue-listener:
2698+
2699+
QueueListener
2700+
^^^^^^^^^^^^^
2701+
2702+
The :class:`QueueListener` class, located in the :mod:`logging.handlers`
2703+
module, supports receiving logging messages from a queue, such as those
2704+
implemented in the :mod:`queue` or :mod:`multiprocessing` modules. The
2705+
messages are received from a queue in an internal thread and passed, on
2706+
the same thread, to one or more handlers for processing.
2707+
2708+
Along with the :class:`QueueHandler` class, :class:`QueueListener` can be used
2709+
to let handlers do their work on a separate thread from the one which does the
2710+
logging. This is important in Web applications and also other service
2711+
applications where threads servicing clients need to respond as quickly as
2712+
possible, while any potentially slow operations (such as sending an email via
2713+
:class:`SMTPHandler`) are done on a separate thread.
2714+
2715+
.. class:: QueueListener(queue, *handlers)
2716+
2717+
Returns a new instance of the :class:`QueueListener` class. The instance is
2718+
initialized with the queue to send messages to and a list of handlers which
2719+
will handle entries placed on the queue. The queue can be any queue-
2720+
like object; it's passed as-is to the :meth:`dequeue` method, which needs
2721+
to know how to get messages from it.
2722+
2723+
.. method:: dequeue(block)
2724+
2725+
Dequeues a record and return it, optionally blocking.
2726+
2727+
The base implementation uses ``get()``. You may want to override this
2728+
method if you want to use timeouts or work with custom queue
2729+
implementations.
2730+
2731+
.. method:: prepare(record)
2732+
2733+
Prepare a record for handling.
2734+
2735+
This implementation just returns the passed-in record. You may want to
2736+
override this method if you need to do any custom marshalling or
2737+
manipulation of the record before passing it to the handlers.
2738+
2739+
.. method:: handle(record)
2740+
2741+
Handle a record.
2742+
2743+
This just loops through the handlers offering them the record
2744+
to handle. The actual object passed to the handlers is that which
2745+
is returned from :meth:`prepare`.
2746+
2747+
.. method:: start()
2748+
2749+
Starts the listener.
2750+
2751+
This starts up a background thread to monitor the queue for
2752+
LogRecords to process.
2753+
2754+
.. method:: stop()
2755+
2756+
Stops the listener.
2757+
2758+
This asks the thread to terminate, and then waits for it to do so.
2759+
Note that if you don't call this before your application exits, there
2760+
may be some records still left on the queue, which won't be processed.
2761+
2762+
.. versionadded:: 3.2
2763+
2764+
The :class:`QueueListener` class was not present in previous versions.
2765+
26912766
.. _zeromq-handlers:
26922767

2768+
Subclassing QueueHandler
2769+
^^^^^^^^^^^^^^^^^^^^^^^^
2770+
26932771
You can use a :class:`QueueHandler` subclass to send messages to other kinds
26942772
of queues, for example a ZeroMQ "publish" socket. In the example below,the
26952773
socket is created separately and passed to the handler (as its 'queue')::
@@ -2716,6 +2794,7 @@ data needed by the handler to create the socket::
27162794
def __init__(self, uri, socktype=zmq.PUB, ctx=None):
27172795
self.ctx = ctx or zmq.Context()
27182796
socket = zmq.Socket(self.ctx, socktype)
2797+
socket.bind(uri)
27192798
QueueHandler.__init__(self, socket)
27202799

27212800
def enqueue(self, record):
@@ -2725,6 +2804,23 @@ data needed by the handler to create the socket::
27252804
def close(self):
27262805
self.queue.close()
27272806

2807+
Subclassing QueueListener
2808+
^^^^^^^^^^^^^^^^^^^^^^^^^
2809+
2810+
You can also subclass :class:`QueueListener` to get messages from other kinds
2811+
of queues, for example a ZeroMQ "subscribe" socket. Here's an example::
2812+
2813+
class ZeroMQSocketListener(QueueListener):
2814+
def __init__(self, uri, *handlers, **kwargs):
2815+
self.ctx = kwargs.get('ctx') or zmq.Context()
2816+
socket = zmq.Socket(self.ctx, zmq.SUB)
2817+
socket.setsockopt(zmq.SUBSCRIBE, '') # subscribe to everything
2818+
socket.connect(uri)
2819+
2820+
def dequeue(self):
2821+
msg = self.queue.recv()
2822+
return logging.makeLogRecord(json.loads(msg))
2823+
27282824
.. _formatter-objects:
27292825

27302826
Formatter Objects

Lib/logging/handlers.py

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,8 +1178,8 @@ def enqueue(self, record):
11781178

11791179
def prepare(self, record):
11801180
"""
1181-
Prepares a record for queuing. The object returned by this
1182-
method is enqueued.
1181+
Prepares a record for queuing. The object returned by this method is
1182+
enqueued.
11831183
11841184
The base implementation formats the record to merge the message
11851185
and arguments, and removes unpickleable items from the record
@@ -1205,11 +1205,115 @@ def emit(self, record):
12051205
"""
12061206
Emit a record.
12071207
1208-
Writes the LogRecord to the queue, preparing it first.
1208+
Writes the LogRecord to the queue, preparing it for pickling first.
12091209
"""
12101210
try:
12111211
self.enqueue(self.prepare(record))
12121212
except (KeyboardInterrupt, SystemExit):
12131213
raise
12141214
except:
12151215
self.handleError(record)
1216+
1217+
class QueueListener(object):
1218+
"""
1219+
This class implements an internal threaded listener which watches for
1220+
LogRecords being added to a queue, removes them and passes them to a
1221+
list of handlers for processing.
1222+
"""
1223+
_sentinel = None
1224+
1225+
def __init__(self, queue, *handlers):
1226+
"""
1227+
Initialise an instance with the specified queue and
1228+
handlers.
1229+
"""
1230+
self.queue = queue
1231+
self.handlers = handlers
1232+
self._stop = threading.Event()
1233+
self._thread = None
1234+
1235+
def dequeue(self, block):
1236+
"""
1237+
Dequeue a record and return it, optionally blocking.
1238+
1239+
The base implementation uses get. You may want to override this method
1240+
if you want to use timeouts or work with custom queue implementations.
1241+
"""
1242+
return self.queue.get(block)
1243+
1244+
def start(self):
1245+
"""
1246+
Start the listener.
1247+
1248+
This starts up a background thread to monitor the queue for
1249+
LogRecords to process.
1250+
"""
1251+
self._thread = t = threading.Thread(target=self._monitor)
1252+
t.setDaemon(True)
1253+
t.start()
1254+
1255+
def prepare(self , record):
1256+
"""
1257+
Prepare a record for handling.
1258+
1259+
This method just returns the passed-in record. You may want to
1260+
override this method if you need to do any custom marshalling or
1261+
manipulation of the record before passing it to the handlers.
1262+
"""
1263+
return record
1264+
1265+
def handle(self, record):
1266+
"""
1267+
Handle a record.
1268+
1269+
This just loops through the handlers offering them the record
1270+
to handle.
1271+
"""
1272+
record = self.prepare(record)
1273+
for handler in self.handlers:
1274+
handler.handle(record)
1275+
1276+
def _monitor(self):
1277+
"""
1278+
Monitor the queue for records, and ask the handler
1279+
to deal with them.
1280+
1281+
This method runs on a separate, internal thread.
1282+
The thread will terminate if it sees a sentinel object in the queue.
1283+
"""
1284+
q = self.queue
1285+
has_task_done = hasattr(q, 'task_done')
1286+
while not self._stop.isSet():
1287+
try:
1288+
record = self.dequeue(True)
1289+
if record is self._sentinel:
1290+
break
1291+
self.handle(record)
1292+
if has_task_done:
1293+
q.task_done()
1294+
except queue.Empty:
1295+
pass
1296+
# There might still be records in the queue.
1297+
while True:
1298+
try:
1299+
record = self.dequeue(False)
1300+
if record is self._sentinel:
1301+
break
1302+
self.handle(record)
1303+
if has_task_done:
1304+
q.task_done()
1305+
except queue.Empty:
1306+
break
1307+
1308+
def stop(self):
1309+
"""
1310+
Stop the listener.
1311+
1312+
This asks the thread to terminate, and then waits for it to do so.
1313+
Note that if you don't call this before your application exits, there
1314+
may be some records still left on the queue, which won't be processed.
1315+
"""
1316+
self._stop.set()
1317+
self.queue.put_nowait(self._sentinel)
1318+
self._thread.join()
1319+
self._thread = None

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ Core and Builtins
6262
Library
6363
-------
6464

65+
- Logging: Added QueueListener class to facilitate logging usage for
66+
performance-critical threads.
67+
6568
- Issue #9916: Add some missing errno symbols.
6669

6770
- Issue #9877: Expose sysconfig.get_makefile_filename()

0 commit comments

Comments
 (0)