feat(logs): System logs tab, log rules, and server-side deletion - #101
Conversation
The logs page now has a system tab for logs the host writes rather than a deployment, starting with the proxy's access and error logs, and the access log can be narrowed to one deployment. A deployment's own logs can again be narrowed to a single container, which had been lost. The trash button now empties the log on the server behind a confirmation that names exactly what goes, rather than clearing the view, which is what refresh already did. A log entry can be handed to the assistant on its own, so the question is about the line the reader is pointing at instead of everything on screen. Follow moves in with the other actions, since it acts on the stream rather than filtering it.
Alerts now covers logs as well as metrics. A rule says which lines matter, how many times within how long, and how long to stay quiet after firing, so a repeated error raises an incident on its own instead of waiting for someone to be watching. Incidents read as a feed, each carrying the line that fired it, how often it recurred, and where the assistant is configured, a short explanation and a suggested next step.
Opening the panel with a lapsed session no longer spends a rejection on every call the page makes. The agent reads a run of rejections from one address as an attack and blocks it, so an operator whose token expired could lock themselves out simply by loading the dashboard. The first call of a burst now goes alone and the rest wait on what it learns, which turns a page load into one rejection instead of a dozen. A signed-out visitor sends nothing at all. The gate arms again once a burst has drained, so a session that lapses while the panel is open costs one rejection rather than one per call from then on. Reading the current user shared none of this because it built its own client; it now goes through the same one as everything else.
Code Review SummaryThe PR introduces significant improvements to log management, including log-based alerting (rules and incidents), system infrastructure logs, and a more resilient API client session management system. The 'Session Gate' is a standout feature addressing a critical authentication edge case. 🚀 Key Improvements
💡 Minor Suggestions
|
Deploying flatrun-ui with
|
| Latest commit: |
6711257
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://99649fb8.flatrun-ui.pages.dev |
| Branch Preview URL: | https://feat-system-logs-and-log-tri.flatrun-ui.pages.dev |
|
|
||
| // Debugging one entry hands over that entry alone. The toolbar's assistant reads everything on | ||
| // screen, which buries the line the reader is actually pointing at. | ||
| const debugEntry = (entry: LogEntry) => { |
There was a problem hiding this comment.
Move the store initialization outside the debugEntry function to the top-level script setup. While Pinia stores are efficient, locating the store on every single 'Debug' click is unnecessary overhead. Additionally, the regex was refined to strip '-logs' for a cleaner AI subject name.
| const debugEntry = (entry: LogEntry) => { | |
| const assistStore = useAssistStore(); | |
| const debugEntry = (entry: LogEntry) => { | |
| const base = props.assistContext ?? { scope: "system" as const, subject: props.fileName.replace(/-logs\.txt$/, "") }; | |
| assistStore.open({ | |
| ...base, | |
| seedMessage: `Debug this log entry from ${base.subject}: what caused it, how serious is it, and what should I do next?`, | |
| seedContext: `\`\`\`\n${entry.lines.join("\n")}\n\`\`\``, | |
| }); | |
| }; |
|
|
||
| apiClient.interceptors.request.use((config) => { | ||
| const publicPaths = ["/auth/login", "/auth/status", "/setup", "/health"]; | ||
| const isPublic = (url: string) => publicPaths.some((p) => url.startsWith(p)); |
There was a problem hiding this comment.
The isPublic check relies on startsWith. If a URL is passed without a leading slash or contains query parameters, it might behave unexpectedly. Normalizing the path or stripping queries ensures the matching is reliable.
| const isPublic = (url: string) => publicPaths.some((p) => url.startsWith(p)); | |
| const isPublic = (url: string) => { | |
| const path = url.split('?')[0]; | |
| const normalized = path.startsWith('/') ? path : `/${path}`; | |
| return publicPaths.some((p) => normalized.startsWith(p)); | |
| }; |
The trash button used to clear the buffer, which is what refresh already did, so it now deletes on
the server behind a confirmation naming exactly what goes.
The auth commit is a self-inflicted lockout. The agent reads a run of rejections from one address
as an attack and blocks it, and the panel fires a page of calls at once, so loading the dashboard
with a lapsed token spent a rejection on each and could block the operator. An interceptor cannot
unsend a request, so the first call of a burst now goes alone and the rest wait on what it learns.
Reading the current user built its own client and bypassed all of this, which is why it was one of
the calls in the run.
Needs flatrun/agent#206 for the endpoints it calls.