make logs opt-in and remove them from /

This commit is contained in:
legop3
2026-06-12 11:53:41 -04:00
parent c0050498b2
commit dc193d09db
5 changed files with 74 additions and 15 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -12,7 +12,7 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
<title>Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-D0FgC_A-.js"></script>
<script type="module" crossorigin src="/assets/index-CiUVS_ut.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BVKa8Zph.css">
</head>
<body>
+31 -2
View File
@@ -7,6 +7,7 @@ const loggerRoot = require('../../globals/logger');
const logger = loggerRoot.child('logStream');
const MAX_HISTORY = 200;
const LOG_ROOM = 'log:subscribers';
const history = [];
function pushEntry(entry) {
@@ -17,11 +18,22 @@ function pushEntry(entry) {
}
function broadcast(entry) {
io.emit('log:entry', entry);
/*
The raw server log stream can produce dozens of socket messages per second.
Sending those messages only to sockets that joined the diagnostic log room
keeps ordinary driver pages from spending network, parsing, and session-store
work on logs they never render.
*/
io.to(LOG_ROOM).emit('log:entry', entry);
}
function hydrateSocket(socket) {
if (!socket) return;
/*
Hydration is tied to an explicit subscription instead of connection startup.
This preserves the existing "latest 200 logs" operator view while avoiding
a large initial payload for pages that do not mount the log panel.
*/
socket.emit('log:init', history);
}
@@ -38,6 +50,23 @@ loggerRoot.registerSink(({ level, label, message, timestamp }) => {
});
io.on('connection', (socket) => {
logger.info('Hydrating log history for', socket.id);
socket.on('log:subscribe', () => {
/*
Joining before hydration means a log emitted during the same event loop
turn cannot be missed between "send history" and "start live stream".
A duplicate edge entry is less harmful than a hidden gap in diagnostics,
and entries have stable ids for React keys if that ever occurs.
*/
socket.join(LOG_ROOM);
hydrateSocket(socket);
});
socket.on('log:unsubscribe', () => {
/*
Leaving the room is enough to stop future log entries. The retained server
history remains global so the next subscriber can still hydrate from the
same rolling diagnostic buffer.
*/
socket.leave(LOG_ROOM);
});
});
-3
View File
@@ -15,7 +15,6 @@ import {
} from './controls/index.js';
import RoomCameraPanel from './components/RoomCameraPanel/index.jsx';
import KinectPanel from './components/KinectPanel/index.jsx';
import LogPanel from './components/LogPanel/index.jsx';
import DriverVideo from './components/DriverVideo/index.jsx';
import RightPaneTabs from './components/RightPaneTabs/index.jsx';
import ModeGateOverlay from './components/ModeGateOverlay/index.jsx';
@@ -82,7 +81,6 @@ function DesktopLayout({ layout, onOpenHelpOverlay }) {
<DriverVideo />
<PiHostStatsCard />
<TelemetryPanel />
{/* <LogPanel /> */}
</div>
<div className={`flex min-w-0 flex-1 flex-col ${themeGapClass} overflow-y-auto`}>
<GlobalObjectiveBanner layout={layout} />
@@ -174,7 +172,6 @@ function MobileFeatureTabs({
<TabPanel id="settings">
<div className={themeStackClass}>
<SettingsPanel />
<LogPanel />
</div>
</TabPanel>
</TabPanels>
+34 -1
View File
@@ -2,12 +2,45 @@
// Purpose: Defines the Log Panel module and the local helpers/components used in this file.
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
import { useSessionSelector } from '../../context/SessionContext.jsx';
import { useMemo } from 'react';
import { useEffect, useMemo } from 'react';
import { useSocket } from '../../context/SocketContext.jsx';
import CardFrame from '../CardFrame/index.jsx';
export default function LogPanel() {
const socket = useSocket();
const logs = useSessionSelector((state) => state.logs);
const rendered = useMemo(() => logs.slice().reverse(), [logs]);
useEffect(() => {
const subscribe = () => {
socket.emit('log:subscribe');
};
/*
Server logs are noisy enough that receiving them globally can make normal
driving views pay for a diagnostic tool they are not using. The panel owns
the subscription because it is the visible consumer: when a route or tab
unmounts this component, the server can stop sending log traffic to this
browser entirely instead of merely hiding the rendered rows.
The connect listener matters because Socket.IO rooms are attached to the
current server-side socket instance. A reconnect gives the browser a fresh
room membership, so the mounted panel must ask for the log room again.
*/
subscribe();
socket.on('connect', subscribe);
return () => {
/*
Unsubscribing on unmount keeps inactive tab panels and non-log routes
from continuing to receive high-volume log entries after the operator
has navigated away from the diagnostic view.
*/
socket.off('connect', subscribe);
socket.emit('log:unsubscribe');
};
}, [socket]);
return (
<CardFrame title="Server logs" bodyClassName="space-y-0.5 text-base">
<div className="surface h-64 overflow-y-auto font-mono text-xs">