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

+
+
+ +
+
+ +
+ + +
+
+
+ Loading 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 +
+ + + + + + + + + + + + {% for r in records %} + + + + + + + {% endfor %} + {% if not records %} + + + + {% endif %} + +
TimeLevelLoggerMessage
+ {{ r.recorded_at.strftime('%H:%M:%S') if r.recorded_at else '—' }} + + {{ r.level }} + + {{ r.logger }} + + {{ r.message }} +
No log entries found.
+ +
+ {% if page > 1 %} + + {% endif %} + Page {{ page }} / {{ total_pages }} + {% if page < total_pages %} + + {% endif %} +
+
\ No newline at end of file