Skip to content

Commit 4d764a6

Browse files
authored
fix: drop expected websocket keepalive timeout Sentry noise (baserow#5225)
Asyncio logs these handled disconnects when websocket clients vanish without a close handshake, which floods Sentry without indicating an application error.
1 parent 04174e7 commit 4d764a6

4 files changed

Lines changed: 91 additions & 0 deletions

File tree

backend/src/baserow/config/settings/base.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,10 @@ def __setitem__(self, key, value):
14351435
from sentry_sdk.integrations.django import DjangoIntegration
14361436
from sentry_sdk.scrubber import DEFAULT_DENYLIST, EventScrubber
14371437

1438+
from baserow.core.sentry import (
1439+
drop_expected_asyncio_websocket_ping_timeout_events,
1440+
)
1441+
14381442
# Exclude integrations whose module-level imports are incompatible:
14391443
# - pydantic_ai: sentry-sdk patches ToolManager._call_tool which was
14401444
# removed in pydantic-ai >= 1.x (now execute_tool_call)
@@ -1451,6 +1455,7 @@ def __setitem__(self, key, value):
14511455
dsn=SENTRY_DSN,
14521456
integrations=[DjangoIntegration(signals_spans=False, middleware_spans=False)],
14531457
send_default_pii=False,
1458+
before_send=drop_expected_asyncio_websocket_ping_timeout_events,
14541459
event_scrubber=EventScrubber(recursive=True, denylist=SENTRY_DENYLIST),
14551460
environment=os.getenv("SENTRY_ENVIRONMENT", ""),
14561461
)

backend/src/baserow/core/sentry.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,37 @@
1+
import logging
2+
from typing import Any
3+
14
from django.contrib.auth import get_user_model
25

36

7+
def drop_expected_asyncio_websocket_ping_timeout_events(
8+
event: dict[str, Any], hint: dict[str, Any]
9+
) -> dict[str, Any] | None:
10+
"""
11+
Ignore websocket keepalive timeouts logged by asyncio.
12+
13+
These are emitted by the websockets stack when a client disappears without a
14+
clean close handshake. They are noisy, expected in production, and don't
15+
point to an application error in Baserow itself.
16+
"""
17+
18+
log_record = hint.get("log_record")
19+
if not isinstance(log_record, logging.LogRecord):
20+
return event
21+
22+
if log_record.name != "asyncio":
23+
return event
24+
25+
message = log_record.getMessage()
26+
if (
27+
"ConnectionClosedError exception in shielded future" in message
28+
and "keepalive ping timeout" in message
29+
):
30+
return None
31+
32+
return event
33+
34+
435
def setup_user_in_sentry(user):
536
"""
637
This function sets the user in the Sentry context. This is useful for debugging
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import logging
2+
3+
from baserow.core.sentry import drop_expected_asyncio_websocket_ping_timeout_events
4+
5+
6+
def test_drop_expected_asyncio_websocket_ping_timeout_events():
7+
event = {"logger": "asyncio"}
8+
record = logging.makeLogRecord(
9+
{
10+
"name": "asyncio",
11+
"levelno": logging.ERROR,
12+
"levelname": "ERROR",
13+
"msg": (
14+
"ConnectionClosedError exception in shielded future future: "
15+
"<Future finished exception=ConnectionClosedError(None, "
16+
"Close(code=<CloseCode.INTERNAL_ERROR: 1011>, reason='keepalive "
17+
"ping timeout'), None)>"
18+
),
19+
}
20+
)
21+
22+
assert (
23+
drop_expected_asyncio_websocket_ping_timeout_events(
24+
event, {"log_record": record}
25+
)
26+
is None
27+
)
28+
29+
30+
def test_drop_expected_asyncio_websocket_ping_timeout_events_keeps_other_errors():
31+
event = {"logger": "asyncio"}
32+
record = logging.makeLogRecord(
33+
{
34+
"name": "asyncio",
35+
"levelno": logging.ERROR,
36+
"levelname": "ERROR",
37+
"msg": "Task exception was never retrieved",
38+
}
39+
)
40+
41+
assert (
42+
drop_expected_asyncio_websocket_ping_timeout_events(
43+
event, {"log_record": record}
44+
)
45+
== event
46+
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "bug",
3+
"message": "Prevent expected websocket keepalive timeout logs from being reported to Sentry.",
4+
"issue_origin": "github",
5+
"issue_number": 5224,
6+
"domain": "core",
7+
"bullet_points": [],
8+
"created_at": "2026-04-17"
9+
}

0 commit comments

Comments
 (0)