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

Commit b2df071

Browse files
authored
Improve logging for cancelled requests (#12587)
Don't log stack traces for cancelled requests and use a custom HTTP status code of 499. Signed-off-by: Sean Quah <[email protected]>
1 parent 75dff3d commit b2df071

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

changelog.d/12587.misc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add `@cancellable` decorator, for use on endpoint methods that can be cancelled when clients disconnect.

docs/usage/administration/request_log.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ See the following for how to decode the dense data available from the default lo
2828
| NNNN | Total time waiting for response to DB queries across all parallel DB work from this request |
2929
| OOOO | Count of DB transactions performed |
3030
| PPPP | Response body size |
31-
| QQQQ | Response status code (prefixed with ! if the socket was closed before the response was generated) |
31+
| QQQQ | Response status code<br/>Suffixed with `!` if the socket was closed before the response was generated.<br/>A `499!` status code indicates that Synapse also cancelled request processing after the socket was closed.<br/> |
3232
| RRRR | Request |
3333
| SSSS | User-agent |
3434
| TTTT | Events fetched from DB to service this request (note that this does not include events fetched from the cache) |

synapse/http/server.py

+30
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from zope.interface import implementer
4444

4545
from twisted.internet import defer, interfaces
46+
from twisted.internet.defer import CancelledError
4647
from twisted.python import failure
4748
from twisted.web import resource
4849
from twisted.web.server import NOT_DONE_YET, Request
@@ -82,6 +83,14 @@
8283
</html>
8384
"""
8485

86+
# A fictional HTTP status code for requests where the client has disconnected and we
87+
# successfully cancelled the request. Used only for logging purposes. Clients will never
88+
# observe this code unless cancellations leak across requests or we raise a
89+
# `CancelledError` ourselves.
90+
# Analogous to nginx's 499 status code:
91+
# https://github.com/nginx/nginx/blob/release-1.21.6/src/http/ngx_http_request.h#L128-L134
92+
HTTP_STATUS_REQUEST_CANCELLED = 499
93+
8594

8695
def return_json_error(f: failure.Failure, request: SynapseRequest) -> None:
8796
"""Sends a JSON error response to clients."""
@@ -93,6 +102,17 @@ def return_json_error(f: failure.Failure, request: SynapseRequest) -> None:
93102
error_dict = exc.error_dict()
94103

95104
logger.info("%s SynapseError: %s - %s", request, error_code, exc.msg)
105+
elif f.check(CancelledError):
106+
error_code = HTTP_STATUS_REQUEST_CANCELLED
107+
error_dict = {"error": "Request cancelled", "errcode": Codes.UNKNOWN}
108+
109+
if not request._disconnected:
110+
logger.error(
111+
"Got cancellation before client disconnection from %r: %r",
112+
request.request_metrics.name,
113+
request,
114+
exc_info=(f.type, f.value, f.getTracebackObject()), # type: ignore[arg-type]
115+
)
96116
else:
97117
error_code = 500
98118
error_dict = {"error": "Internal server error", "errcode": Codes.UNKNOWN}
@@ -155,6 +175,16 @@ def return_html_error(
155175
request,
156176
exc_info=(f.type, f.value, f.getTracebackObject()), # type: ignore[arg-type]
157177
)
178+
elif f.check(CancelledError):
179+
code = HTTP_STATUS_REQUEST_CANCELLED
180+
msg = "Request cancelled"
181+
182+
if not request._disconnected:
183+
logger.error(
184+
"Got cancellation before client disconnection when handling request %r",
185+
request,
186+
exc_info=(f.type, f.value, f.getTracebackObject()), # type: ignore[arg-type]
187+
)
158188
else:
159189
code = HTTPStatus.INTERNAL_SERVER_ERROR
160190
msg = "Internal server error"

0 commit comments

Comments
 (0)