feat(host,web): host log ring + GET /api/v1/logs + console Logs page
Remote debugging without shell access: a tracing layer tees every event at DEBUG-and-up — independent of the RUST_LOG filter gating stderr/host.log, so console-side debugging never needs a restart — into a bounded in-memory ring (log_capture.rs, 4096 newest entries, OnceLock singleton like config()), installed at both init sites (stderr path in main, the Windows service file path). The mgmt API serves it cursor-paged at GET /api/v1/logs?after=&limit= — bearer-only and deliberately NOT on the mTLS cert allowlist (log lines can name client identities and host paths). The web console grows a Logs page (follow/pause · min-level filter · text search · eviction-gap badge); polling self-paces: a non-empty page advances the after-cursor (new query key → immediate refetch, drains backlogs), an empty page idles at the 2s interval. OpenAPI regenerated; ring pagination/eviction, layer wiring, and the authed route are unit-tested; Storybook story included. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import type { LogEntry } from "@/api/gen/model/logEntry";
|
||||
import { LogsCard } from "@/sections/Logs/LogsCard";
|
||||
import { LogsView } from "@/sections/Logs/view";
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
// A deterministic slice of host logs covering every level, incl. the gamepad-driver health lines
|
||||
// the page exists to surface — no live host needed.
|
||||
const BASE = 1_750_000_000_000;
|
||||
const entry = (
|
||||
seq: number,
|
||||
level: string,
|
||||
target: string,
|
||||
msg: string,
|
||||
): LogEntry => ({ seq, ts_ms: BASE + seq * 750, level, target, msg });
|
||||
|
||||
const fixtureEntries: LogEntry[] = [
|
||||
entry(1, "INFO", "punktfunk_host", "punktfunk-host 0.4.2 (punktfunk_core ABI v2)"),
|
||||
entry(2, "INFO", "punktfunk_host::mgmt", "management API listening over HTTPS addr=0.0.0.0:47990"),
|
||||
entry(3, "DEBUG", "punktfunk_host::discovery", "mDNS advertise _punktfunk._udp pair=required"),
|
||||
entry(4, "INFO", "punktfunk_host::punktfunk1", "session start mode=1920x1080@60 codec=hevc"),
|
||||
entry(5, "INFO", "punktfunk_host::inject", "virtual Xbox 360 created (Windows XUSB companion)"),
|
||||
entry(6, "WARN", "punktfunk_host::inject", "gamepad driver not attached to Global\\pfxusb-shm-0 after 3s — is the pf_xusb driver installed? (punktfunk-host.exe driver install --gamepad)"),
|
||||
entry(7, "ERROR", "punktfunk_host::inject", "virtual Xbox 360 creation failed — controller input disabled (is the pf_xusb driver installed?)"),
|
||||
entry(8, "INFO", "punktfunk_host::encode", "NVENC opened 1920x1080 nv12 gop=inf rfi=on"),
|
||||
];
|
||||
|
||||
const meta = {
|
||||
title: "Pages/Logs",
|
||||
component: LogsView,
|
||||
parameters: { layout: "padded" },
|
||||
} satisfies Meta<typeof LogsView>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
// The real page layout (LogsView) with the pure viewer card + fixture entries in its slot.
|
||||
export const Following: Story = {
|
||||
args: {
|
||||
viewer: (
|
||||
<LogsCard
|
||||
entries={fixtureEntries}
|
||||
follow
|
||||
onFollow={noop}
|
||||
onClear={noop}
|
||||
dropped={false}
|
||||
/>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const PausedWithGap: Story = {
|
||||
args: {
|
||||
viewer: (
|
||||
<LogsCard
|
||||
entries={fixtureEntries}
|
||||
follow={false}
|
||||
onFollow={noop}
|
||||
onClear={noop}
|
||||
dropped
|
||||
/>
|
||||
),
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user