Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 8b42a4e

Browse files
authored
Gracefully handle a pending logging connection during shutdown. (#8685)
1 parent f21e24f commit 8b42a4e

File tree

5 files changed

+36
-10
lines changed

5 files changed

+36
-10
lines changed

changelog.d/8607.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support generating structured logs via the standard logging configuration.

changelog.d/8607.misc

Lines changed: 0 additions & 1 deletion
This file was deleted.

changelog.d/8685.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support generating structured logs via the standard logging configuration.

synapse/logging/_remote.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,15 @@
2626
from zope.interface import implementer
2727

2828
from twisted.application.internet import ClientService
29-
from twisted.internet.defer import Deferred
29+
from twisted.internet.defer import CancelledError, Deferred
3030
from twisted.internet.endpoints import (
3131
HostnameEndpoint,
3232
TCP4ClientEndpoint,
3333
TCP6ClientEndpoint,
3434
)
3535
from twisted.internet.interfaces import IPushProducer, ITransport
3636
from twisted.internet.protocol import Factory, Protocol
37+
from twisted.python.failure import Failure
3738

3839
logger = logging.getLogger(__name__)
3940

@@ -131,9 +132,11 @@ def __init__(
131132
factory = Factory.forProtocol(Protocol)
132133
self._service = ClientService(endpoint, factory, clock=_reactor)
133134
self._service.startService()
135+
self._stopping = False
134136
self._connect()
135137

136138
def close(self):
139+
self._stopping = True
137140
self._service.stopService()
138141

139142
def _connect(self) -> None:
@@ -146,17 +149,21 @@ def _connect(self) -> None:
146149

147150
self._connection_waiter = self._service.whenConnected(failAfterFailures=1)
148151

149-
@self._connection_waiter.addErrback
150-
def fail(r):
151-
r.printTraceback(file=sys.__stderr__)
152+
def fail(failure: Failure) -> None:
153+
# If the Deferred was cancelled (e.g. during shutdown) do not try to
154+
# reconnect (this will cause an infinite loop of errors).
155+
if failure.check(CancelledError) and self._stopping:
156+
return
157+
158+
# For a different error, print the traceback and re-connect.
159+
failure.printTraceback(file=sys.__stderr__)
152160
self._connection_waiter = None
153161
self._connect()
154162

155-
@self._connection_waiter.addCallback
156-
def writer(r):
163+
def writer(result: Protocol) -> None:
157164
# We have a connection. If we already have a producer, and its
158165
# transport is the same, just trigger a resumeProducing.
159-
if self._producer and r.transport is self._producer.transport:
166+
if self._producer and result.transport is self._producer.transport:
160167
self._producer.resumeProducing()
161168
self._connection_waiter = None
162169
return
@@ -167,12 +174,14 @@ def writer(r):
167174

168175
# Make a new producer and start it.
169176
self._producer = LogProducer(
170-
buffer=self._buffer, transport=r.transport, format=self.format,
177+
buffer=self._buffer, transport=result.transport, format=self.format,
171178
)
172-
r.transport.registerProducer(self._producer, True)
179+
result.transport.registerProducer(self._producer, True)
173180
self._producer.resumeProducing()
174181
self._connection_waiter = None
175182

183+
self._connection_waiter.addCallbacks(writer, fail)
184+
176185
def _handle_pressure(self) -> None:
177186
"""
178187
Handle backpressure by shedding records.

tests/logging/test_remote_handler.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,19 @@ def test_log_backpressure_cut_middle(self):
151151
+ ["warn %s" % (i,) for i in range(15, 20)],
152152
logs,
153153
)
154+
155+
def test_cancel_connection(self):
156+
"""
157+
Gracefully handle the connection being cancelled.
158+
"""
159+
handler = RemoteHandler(
160+
"127.0.0.1", 9000, maximum_buffer=10, _reactor=self.reactor
161+
)
162+
logger = self.get_logger(handler)
163+
164+
# Send a message.
165+
logger.info("Hello there, %s!", "wally")
166+
167+
# Do not accept the connection and shutdown. This causes the pending
168+
# connection to be cancelled (and should not raise any exceptions).
169+
handler.close()

0 commit comments

Comments
 (0)