From 1e4086a0fdd2e9df9bb288e13ecd2405ca66632a Mon Sep 17 00:00:00 2001
From: zwitschi
Date: Sun, 7 Jun 2026 18:10:50 +0200
Subject: [PATCH] feat: add logging routes and update health page to display
system logs
---
src/arbitrade/api/routes.py | 46 ++++++++++++
src/arbitrade/web/templates/health.html | 47 +++++++++++-
.../web/templates/partials/log_table.html | 72 +++++++++++++++++++
3 files changed, 162 insertions(+), 3 deletions(-)
create mode 100644 src/arbitrade/web/templates/partials/log_table.html
diff --git a/src/arbitrade/api/routes.py b/src/arbitrade/api/routes.py
index c5b048a..98b0b75 100644
--- a/src/arbitrade/api/routes.py
+++ b/src/arbitrade/api/routes.py
@@ -1493,3 +1493,49 @@ async def dashboard_api_pairings_sync(request: Request) -> HTMLResponse:
name="partials/pairings_table.html",
context={"request": request, "pairings": pairings},
)
+
+
+# ── Log routes ──────────────────────────────────────────────────────────────
+
+
+@router.get("/dashboard/fragment/logs", response_class=HTMLResponse)
+async def dashboard_logs_fragment(
+ request: Request,
+ level: str | None = None,
+ page: int = 1,
+ per_page: int = 50,
+) -> HTMLResponse:
+ """HTMX fragment: log table for health page."""
+ repo = LogRepository(request.app.state.store)
+ offset = (page - 1) * per_page
+ records = await repo.query(level=level, limit=per_page, offset=offset)
+ total = await repo.count_filtered(level=level)
+ total_pages = max(1, (total + per_page - 1) // per_page)
+ return templates.TemplateResponse(
+ request=request,
+ name="partials/log_table.html",
+ context={
+ "request": request,
+ "records": records,
+ "page": page,
+ "total_pages": total_pages,
+ "total": total,
+ "current_level": level or "all",
+ },
+ )
+
+
+@router.post("/dashboard/api/logging/aggregate", response_class=JSONResponse)
+async def dashboard_logging_aggregate(request: Request) -> JSONResponse:
+ from arbitrade.logging.maintenance import run_log_aggregation
+
+ await run_log_aggregation(request.app.state.store)
+ return JSONResponse({"status": "ok"})
+
+
+@router.post("/dashboard/api/logging/archive", response_class=JSONResponse)
+async def dashboard_logging_archive(request: Request) -> JSONResponse:
+ from arbitrade.logging.maintenance import run_log_archive
+
+ count = await run_log_archive(request.app.state.store)
+ return JSONResponse({"status": "ok", "archived": count})
diff --git a/src/arbitrade/web/templates/health.html b/src/arbitrade/web/templates/health.html
index 603f6b9..d64df4d 100644
--- a/src/arbitrade/web/templates/health.html
+++ b/src/arbitrade/web/templates/health.html
@@ -1,8 +1,10 @@
{% extends "_base.html" %} {% block title %}Arbitrade Health Check{% endblock %}
{% block header %} {% with page_title="Arbitrade Health Check",
-page_subtitle="Live system state." %} {% include "_header.html" %} {% endwith %}
-{% endblock %} {% block main_class %}shell{% endblock %} {% block content %}
-
+page_subtitle="Live system state and logs." %} {% include "_header.html" %} {%
+endwith %} {% endblock %} {% block main_class %}shell{% endblock %} {% block
+content %}
+
+
Arbitrade Bootstrap Complete
Status: {{ status }}
UTC: {{ time }}
@@ -18,4 +20,43 @@ page_subtitle="Live system state." %} {% include "_header.html" %} {% endwith %}
{"status":"ok","service":"arbitrade"}
+
+
+ System Logs
+
+
+
+
+
+
+
+
+
{% endblock %} {% block scripts %}{% endblock %}
diff --git a/src/arbitrade/web/templates/partials/log_table.html b/src/arbitrade/web/templates/partials/log_table.html
new file mode 100644
index 0000000..fc95e5b
--- /dev/null
+++ b/src/arbitrade/web/templates/partials/log_table.html
@@ -0,0 +1,72 @@
+
+
+
+ {{ total }} entries
+
+
+
+
+
+ | Time |
+ Level |
+ Logger |
+ Message |
+
+
+
+ {% for r in records %}
+
+ |
+ {{ r.recorded_at.strftime('%H:%M:%S') if r.recorded_at else '—' }}
+ |
+
+ {{ r.level }}
+ |
+
+ {{ r.logger }}
+ |
+
+ {{ r.message }}
+ |
+
+ {% endfor %}
+ {% if not records %}
+
+ | No log entries found. |
+
+ {% endif %}
+
+
+
+
+ {% if page > 1 %}
+
+ {% endif %}
+ Page {{ page }} / {{ total_pages }}
+ {% if page < total_pages %}
+
+ {% endif %}
+
+
\ No newline at end of file