Skip to content

Commit 582b593

Browse files
csm10495vsajip
authored andcommitted
[3.13] pythongh-132106: Ensure that running logging.handlers.QueueListener cannot be started again (pythonGH-132444)
Prevents a thread leak Co-authored-by: Bénédikt Tran <[email protected]> (cherry picked from commit 5863cd7) Co-authored-by: Charles Machalow <[email protected]>
1 parent 4ff5d88 commit 582b593

File tree

4 files changed

+43
-2
lines changed

4 files changed

+43
-2
lines changed

Doc/library/logging.handlers.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,10 @@ possible, while any potentially slow operations (such as sending an email via
11721172
This starts up a background thread to monitor the queue for
11731173
LogRecords to process.
11741174

1175+
.. versionchanged:: next
1176+
Raises :exc:`RuntimeError` if called and the listener is already
1177+
running.
1178+
11751179
.. method:: stop()
11761180

11771181
Stops the listener.

Lib/logging/handlers.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,6 +1545,9 @@ def start(self):
15451545
This starts up a background thread to monitor the queue for
15461546
LogRecords to process.
15471547
"""
1548+
if self._thread is not None:
1549+
raise RuntimeError("Listener already started")
1550+
15481551
self._thread = t = threading.Thread(target=self._monitor)
15491552
t.daemon = True
15501553
t.start()

Lib/test/test_logging.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4336,8 +4336,40 @@ def test_queue_listener(self):
43364336
self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
43374337
handler.close()
43384338

4339-
@unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
4340-
'logging.handlers.QueueListener required for this test')
4339+
def test_queue_listener_context_manager(self):
4340+
handler = TestHandler(support.Matcher())
4341+
with logging.handlers.QueueListener(self.queue, handler) as listener:
4342+
self.assertIsInstance(listener, logging.handlers.QueueListener)
4343+
self.assertIsNotNone(listener._thread)
4344+
self.assertIsNone(listener._thread)
4345+
4346+
# doesn't hurt to call stop() more than once.
4347+
listener.stop()
4348+
self.assertIsNone(listener._thread)
4349+
4350+
def test_queue_listener_context_manager(self):
4351+
handler = TestHandler(support.Matcher())
4352+
with logging.handlers.QueueListener(self.queue, handler) as listener:
4353+
self.assertIsInstance(listener, logging.handlers.QueueListener)
4354+
self.assertIsNotNone(listener._thread)
4355+
self.assertIsNone(listener._thread)
4356+
4357+
# doesn't hurt to call stop() more than once.
4358+
listener.stop()
4359+
self.assertIsNone(listener._thread)
4360+
4361+
def test_queue_listener_multi_start(self):
4362+
handler = TestHandler(support.Matcher())
4363+
with logging.handlers.QueueListener(self.queue, handler) as listener:
4364+
self.assertRaises(RuntimeError, listener.start)
4365+
4366+
with listener:
4367+
self.assertRaises(RuntimeError, listener.start)
4368+
4369+
listener.start()
4370+
listener.stop()
4371+
4372+
>>>>>>> 5863cd70b87... gh-132106: Ensure that running `logging.handlers.QueueListener` cannot be started again (GH-132444)
43414373
def test_queue_listener_with_StreamHandler(self):
43424374
# Test that traceback and stack-info only appends once (bpo-34334, bpo-46755).
43434375
listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:meth:`QueueListener.start <logging.handlers.QueueListener.start>` now
2+
raises a :exc:`RuntimeError` if the listener is already started.

0 commit comments

Comments
 (0)