Skip to content

feat(observability): Console, structured logs, and screen rework - #100

Merged
nfebe merged 24 commits into
mainfrom
feat/observability-logs
Aug 5, 2026
Merged

feat(observability): Console, structured logs, and screen rework#100
nfebe merged 24 commits into
mainfrom
feat/observability-logs

Conversation

@nfebe

@nfebe nfebe commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Reworks the observability and logs experience.

  • Logs render as structured, expandable rows coloured by level, with multi-line entries (an exception and its stack trace) folded into one row and the raw terminal a toggle away.
  • Logs can be read from a deployment's own files, not just container output, plus a fleet-wide Logs page.
  • Metrics, logs, alerts, and dashboards fold into one Observability console at /observability with a left rail, replacing the scattered sidebar entries.
  • The observability screens are denser: the oversized count cards become a compact strip, fleet and host charts split into tabs (fleet first) with a per-deployment filter, and alerts move to a stable two-column layout so a burst of firing alerts no longer shoves the rules around.

Fixes: a long unbreakable log line no longer widens the whole page; the console body scrolls on its own; the collapsed sidebar opens hover flyouts, so grouped nav stays reachable (it was unreachable before).

Depends on the companion agent change for the structured records, file log sources, and network-rate metrics.

nfebe added 20 commits August 5, 2026 14:09
The log viewer's auto-scroll checkbox and the live-streaming button were
both labelled "Follow", so it was unclear which one tailed the container.
The scroll checkbox now reads "Auto-scroll", leaving "Follow" to mean the
live stream.
Deployment logs now render as one row per line, each showing the source
service, time, and severity, with the message on a single line and a
click to expand the full text, any structured JSON fields, and the raw
line. Rows are colour-coded by level and can be narrowed to errors,
warnings, info, or debug. The old terminal remains one toggle away as
the raw view, and both the live stream and the snapshot feed the same
rows, so following a container and reading a tail look identical.
The attached-databases panel moves out of the bottom of Overview into a
dedicated Databases tab, with a clearer layout, an empty state for
deployments with none, and a link through to the database-server
manager.
Deployment metrics, container health, and recovery history now have a
first-class Monitoring tab next to Logs, so the two observability views
sit together instead of the metrics living in a plugin tab tacked on
after Configuration. The tab is always present and degrades to a clear
"observability app not running" state when the plugin is absent; the old
metrics deep-link still resolves here.
The logs view now has a source picker: container output by default, the
log files a deployment's kind conventionally writes, and any file the
user points it at from the "Point at a file" action. Switching source
re-reads it, following included, so an app's own log files show up in the
same structured view without leaving the page.
A single unbreakable line, such as a long file-log entry, forced the
main content area past the viewport and added a page-level horizontal
scrollbar, because the flex column was allowed to keep its min-content
width. It now shrinks to the available width, so long lines clip within
the log view instead of stretching the layout.
The sidebar now has a Logs entry alongside Observability: pick any
deployment and read its logs in the same structured view, from container
output or one of its own log files, with live follow. The chosen
deployment is kept in the URL so the view is shareable.
A log entry that spans several lines, such as an exception with its
stack trace, now reads as a single expandable row instead of dozens of
level-less lines. Continuation lines (indented frames, numbered frames,
trailing brackets) fold into the entry above them; lines that are not
continuations always start their own entry, so independent streams like
access logs are never merged. Level detection matches the agent, so a
class name mentioned in a frame no longer reads as an error.
Selects projected into the log toolbar showed a light background against
the dark toolbar. They now match the toolbar regardless of the app
theme, so the source and deployment pickers are readable on the Logs
page.
The deployment metrics view goes back to the observability plugin's own
"Metrics & Health" tab, now rendered right after Quick Actions instead
of trailing the tab bar. The Databases tab gets a proper card layout: a
type-coloured icon, clearer heading, and a readable key/value grid, with
real padding around the tab.
Drop the explanatory comments added across the log views, keeping only a
short note on the flex min-width guard. No behaviour change.
…ne console

Observability was scattered across several sidebar entries. It is now a
single console at /observability with a left rail (Overview, Logs,
Alerts, Dashboards), so the pillars sit together instead of as flat
siblings. The fleet Logs page moves in as the Logs section, and the
sidebar carries one Observability entry. Per-deployment detail keeps its
own Logs and Metrics & Health tabs.
The metrics screen replaces the four oversized count cards with a
compact stat strip, adds a per-deployment filter that scopes the charts,
shrinks the charts so several fit without scrolling, and gains real
padding. The alerts screen moves to a stable two-column layout: the
rules stay put as the main column while firing and recent alerts sit in
bounded, scrollable side panels, so a burst of alerts no longer shoves
the rules around.
The console now fills the viewport below the header: the left rail and
the page header stay put while each section scrolls its own content.
This is opt-in per route, so other pages keep their normal page scroll.
The rules table was stretched across most of the width while showing
little, so rules and the firing/recent panels now share the space
evenly.
…panel

Network alert metrics and charts now read as a rate (per second), so the
threshold you set and the value you see match what the agent measures.
The firing and recent lists move into one card with a divider instead of
two separate boxes, so the recent list no longer floats oddly beside the
rules.
Collapsing the sidebar hid every group's items with no way to reach
them. Each group now opens a hover flyout showing its links, so
navigation still works with only icons on screen. The sidebar starts
collapsed and remembers the choice, and the direct items carry tooltips.
The overview stacked the host charts above the fleet charts, so both
competed for the same scroll. They are now two tabs with fleet first;
the deployment filter shows only for fleet, and the time range applies
to both.
The connection dot hung to the left of the collapsed footer. It now
centers under the collapse control and carries a tooltip, so its state
reads clearly with the label hidden.
@sourceant

sourceant Bot commented Aug 5, 2026

Copy link
Copy Markdown

Code Review Summary

The PR significantly improves the observability experience, introducing a structured log viewer, a unified observability console, and improved layout density. The code is well-structured and follows Vue 3 best practices, with good use of composables and reactive state management.

🚀 Key Improvements

  • New structured log viewer with level filtering, stack-trace folding, and raw/structured toggle.
  • Fleet-wide logs view and support for reading from specific log files.
  • Unified /observability route for better navigation.
  • Dense, tabbed layout for resource metrics and alerts.
  • Sidebar rework with collapsed-state persistence and hover flyouts.

💡 Minor Suggestions

  • Relax the log timestamp regex to support more common formats (like space-separated or missing timezones).
  • Optimize the autoscroll watcher to handle the case where the log buffer size remains constant due to pruning.
  • Avoid showing a successful copy state if navigator.clipboard is unavailable.

@sourceant sourceant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review complete. See the overview comment for a summary.


const uiVersion = __APP_VERSION__;
const sidebarCollapsed = ref(false);
const sidebarCollapsed = ref(localStorage.getItem("sidebar_collapsed") !== "false");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current check !== "false" results in the sidebar defaulting to a collapsed state for first-time visitors (where localStorage.getItem returns null). For better navigation discoverability, it is generally preferred to default to an expanded state.

Suggested change
const sidebarCollapsed = ref(localStorage.getItem("sidebar_collapsed") !== "false");
const sidebarCollapsed = ref(localStorage.getItem("sidebar_collapsed") === "true");

Comment thread src/types/logs.ts Outdated
if (isContinuation) {
entries[entries.length - 1].lines.push(rec.raw);
} else {
entries.push({ record: rec, lines: [rec.raw], key: i });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the array index i as a key is problematic when logs are being pruned from the start of the array in useLogStream. This shifts indices and causes UI state (like row expansion) to apply to the wrong rows. Using a stable ID assigned when the record is created is safer.

Suggested change
entries.push({ record: rec, lines: [rec.raw], key: i });
entries.push({ record: rec, lines: [rec.raw], key: rec.id ?? i });

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploying flatrun-ui with  Cloudflare Pages  Cloudflare Pages

Latest commit: cf30a0d
Status: ✅  Deploy successful!
Preview URL: https://85df0e02.flatrun-ui.pages.dev
Branch Preview URL: https://feat-observability-logs.flatrun-ui.pages.dev

View logs

@sourceant sourceant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review complete. See the overview comment for a summary.

const effectiveRecords = computed<LogRecord[]>(() => {
if (props.records && props.records.length) return props.records;
if (!props.logs) return [];
return props.logs

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current split logic only handles \n and ignores \r. In environments where logs might contain carriage returns (e.g., Windows-based containers or specific network protocols), this leaves trailing \r characters. Additionally, the fallback mapping does not assign stable IDs, which will cause UI flickering or loss of expansion state in the structured view when the log buffer slides, as groupLogRecords will fall back to using unstable array indices as keys.

Suggested change
return props.logs
return props.logs
.replace(/\r?\n$/, "")
.split(/\r?\n/)
.map((line, i) => ({ ...parseLogLine(line), id: i }));

Comment thread src/views/DeploymentDetailView.vue Outdated
try {
const response = await deploymentsApi.logs(route.params.name as string);
const response = await deploymentsApi.logs(route.params.name as string, {
tail: logsTail.value || 100,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as in the logStream.start call: selecting 'All logs' (0) results in only fetching 100 lines due to the || operator.

Suggested change
tail: logsTail.value || 100,
tail: logsTail.value ?? 100,

@nfebe
nfebe force-pushed the feat/observability-logs branch from 86c8ffc to cf30a0d Compare August 5, 2026 17:03

@sourceant sourceant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review complete. No specific code suggestions were generated. See the overview comment for a summary.

@nfebe
nfebe merged commit 1c36500 into main Aug 5, 2026
5 checks passed
@nfebe
nfebe deleted the feat/observability-logs branch August 5, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant