fix(diagnostics): honest live console after review
- Live stops polling on a failed read and marks the list as possibly stale (with Retry), instead of re-raising the error dialog every tick. - While live, a refetch reads the newest page only, whatever the cache holds (a late "Load older", a revisited filter set). - Copy is disabled and the list dimmed while a filter change loads. - Dates follow the UI language; the pivot chip uses the shared .chip. - Tests: query-string wire test, Copy, every facet's skip key.
This commit is contained in:
@@ -39,6 +39,8 @@ PAGE_MAX = 200
|
||||
FACET_EVENTS_MAX = 100
|
||||
|
||||
Level = Literal["TRACE", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||
# The filters a facet can leave out of its own count (`LogFilters.conditions`).
|
||||
Facet = Literal["levels", "sources", "user", "event"]
|
||||
# `none` selects the rows that have no account / no event code (a scheduler line, an anonymous
|
||||
# request) — a real value to filter on, not the absence of a filter.
|
||||
NONE = "none"
|
||||
@@ -106,7 +108,7 @@ class LogFilters:
|
||||
since: datetime | None
|
||||
until: datetime | None
|
||||
|
||||
def conditions(self, skip: str | None = None) -> list:
|
||||
def conditions(self, skip: Facet | None = None) -> list:
|
||||
"""The WHERE conditions, ANDed by the caller. `skip` leaves one filter out: a facet counts
|
||||
its own values under every OTHER filter, so picking a level does not zero the other
|
||||
levels' counts (the way back), while a request pivot does narrow them (honest counts)."""
|
||||
@@ -210,7 +212,7 @@ def log_facets(
|
||||
most frequent events — each counted under every filter but its own (`LogFilters.conditions`).
|
||||
`total` is the "all levels" count: every filter but the level one."""
|
||||
|
||||
def counts(col, skip: str, cap: int | None = None) -> list[dict]:
|
||||
def counts(col, skip: Facet, cap: int | None = None) -> list[dict]:
|
||||
n = func.count().label("n")
|
||||
stmt = select(col, n).where(*filters.conditions(skip)).group_by(col).order_by(n.desc(), col)
|
||||
if cap is not None:
|
||||
|
||||
@@ -172,3 +172,20 @@ def test_a_facet_counts_under_every_other_filter_but_not_its_own(db, admin):
|
||||
assert body["total"] == 2
|
||||
# Every other facet counts under the level filter too.
|
||||
assert body["sources"] == [{"value": "worker", "count": 1}]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("params", "facet", "expected"),
|
||||
[
|
||||
({"sources": ["worker"]}, "sources", {"api", "worker"}),
|
||||
({"user": "1"}, "users", {"1", "none"}),
|
||||
({"event": "sync.rss.failed"}, "events", {"http.request", "sync.rss.failed"}),
|
||||
],
|
||||
)
|
||||
def test_every_facet_keeps_its_siblings_under_its_own_filter(db, admin, params, facet, expected):
|
||||
# The way back from a pick: the picked facet still offers the values it was picked from.
|
||||
_add(db, _row(user_id=1), _row(source="worker", event="sync.rss.failed"))
|
||||
|
||||
body = admin.get("/api/admin/logs/facets", params=params).json()
|
||||
|
||||
assert {f["value"] for f in body[facet]} == expected
|
||||
|
||||
Reference in New Issue
Block a user