(feature) Add focused filtering to the service logs view

Extend the Django logs view with filters for service unit, severity, time
window, host, run id, and message text. Pass severity and time window directly
to journalctl, then apply host/run/message filtering to the returned pobsync
journal lines.

This makes failed or slow backups easier to investigate from the control panel
without needing shell access.
This commit is contained in:
2026-05-21 00:24:07 +02:00
parent 98695f9888
commit a9e40df44b
3 changed files with 86 additions and 8 deletions

View File

@@ -703,8 +703,26 @@ def _log_context(request) -> dict[str, object]:
"6": "Info",
"7": "Debug",
}
time_windows = {
"1h": "Last hour",
"6h": "Last 6 hours",
"24h": "Last 24 hours",
"7d": "Last 7 days",
"": "All available",
}
since_values = {
"1h": "1 hour ago",
"6h": "6 hours ago",
"24h": "24 hours ago",
"7d": "7 days ago",
}
selected_unit = request.GET.get("unit", "")
priority = request.GET.get("priority", "0..4")
time_window = request.GET.get("window", "24h")
if time_window not in time_windows:
time_window = "24h"
host_filter = request.GET.get("host", "").strip()
run_filter = request.GET.get("run", "").strip()
query = request.GET.get("q", "").strip()
lines = []
error = ""
@@ -713,6 +731,8 @@ def _log_context(request) -> dict[str, object]:
error = "journalctl is not available in this runtime."
else:
command = ["journalctl", "--no-pager", "-n", "300", "-o", "short-iso"]
if time_window:
command.extend(["--since", since_values[time_window]])
if selected_unit in units:
command.extend(["-u", selected_unit])
else:
@@ -725,16 +745,38 @@ def _log_context(request) -> dict[str, object]:
error = result.stderr.strip() or "Could not read journal logs."
else:
lines = result.stdout.splitlines()
if query:
lowered_query = query.lower()
lines = [line for line in lines if lowered_query in line.lower()]
lines = _filter_log_lines(lines, query=query, host=host_filter, run_id=run_filter)
return {
"units": units,
"priorities": priorities,
"time_windows": time_windows,
"selected_unit": selected_unit,
"selected_priority": priority,
"selected_window": time_window,
"host_filter": host_filter,
"run_filter": run_filter,
"query": query,
"lines": lines,
"error": error,
}
def _filter_log_lines(lines: list[str], *, query: str, host: str, run_id: str) -> list[str]:
filters = []
if query:
filters.append(lambda line: query.lower() in line.lower())
if host:
filters.append(lambda line: host.lower() in line.lower())
if run_id:
run_tokens = (
f"run {run_id}",
f"run={run_id}",
f"run_id={run_id}",
f"run-{run_id}",
f"#{run_id}",
)
filters.append(lambda line: any(token in line.lower() for token in run_tokens))
if not filters:
return lines
return [line for line in lines if all(matches(line) for matches in filters)]