mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
make logs opt-in and remove them from /
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -12,7 +12,7 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
|
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
|
||||||
<title>Roomba Rover</title>
|
<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">
|
<link rel="stylesheet" crossorigin href="/assets/index-BVKa8Zph.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const loggerRoot = require('../../globals/logger');
|
|||||||
const logger = loggerRoot.child('logStream');
|
const logger = loggerRoot.child('logStream');
|
||||||
|
|
||||||
const MAX_HISTORY = 200;
|
const MAX_HISTORY = 200;
|
||||||
|
const LOG_ROOM = 'log:subscribers';
|
||||||
const history = [];
|
const history = [];
|
||||||
|
|
||||||
function pushEntry(entry) {
|
function pushEntry(entry) {
|
||||||
@@ -17,11 +18,22 @@ function pushEntry(entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function broadcast(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) {
|
function hydrateSocket(socket) {
|
||||||
if (!socket) return;
|
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);
|
socket.emit('log:init', history);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +50,23 @@ loggerRoot.registerSink(({ level, label, message, timestamp }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
io.on('connection', (socket) => {
|
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);
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
} from './controls/index.js';
|
} from './controls/index.js';
|
||||||
import RoomCameraPanel from './components/RoomCameraPanel/index.jsx';
|
import RoomCameraPanel from './components/RoomCameraPanel/index.jsx';
|
||||||
import KinectPanel from './components/KinectPanel/index.jsx';
|
import KinectPanel from './components/KinectPanel/index.jsx';
|
||||||
import LogPanel from './components/LogPanel/index.jsx';
|
|
||||||
import DriverVideo from './components/DriverVideo/index.jsx';
|
import DriverVideo from './components/DriverVideo/index.jsx';
|
||||||
import RightPaneTabs from './components/RightPaneTabs/index.jsx';
|
import RightPaneTabs from './components/RightPaneTabs/index.jsx';
|
||||||
import ModeGateOverlay from './components/ModeGateOverlay/index.jsx';
|
import ModeGateOverlay from './components/ModeGateOverlay/index.jsx';
|
||||||
@@ -82,7 +81,6 @@ function DesktopLayout({ layout, onOpenHelpOverlay }) {
|
|||||||
<DriverVideo />
|
<DriverVideo />
|
||||||
<PiHostStatsCard />
|
<PiHostStatsCard />
|
||||||
<TelemetryPanel />
|
<TelemetryPanel />
|
||||||
{/* <LogPanel /> */}
|
|
||||||
</div>
|
</div>
|
||||||
<div className={`flex min-w-0 flex-1 flex-col ${themeGapClass} overflow-y-auto`}>
|
<div className={`flex min-w-0 flex-1 flex-col ${themeGapClass} overflow-y-auto`}>
|
||||||
<GlobalObjectiveBanner layout={layout} />
|
<GlobalObjectiveBanner layout={layout} />
|
||||||
@@ -174,7 +172,6 @@ function MobileFeatureTabs({
|
|||||||
<TabPanel id="settings">
|
<TabPanel id="settings">
|
||||||
<div className={themeStackClass}>
|
<div className={themeStackClass}>
|
||||||
<SettingsPanel />
|
<SettingsPanel />
|
||||||
<LogPanel />
|
|
||||||
</div>
|
</div>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
|
|||||||
@@ -2,12 +2,45 @@
|
|||||||
// Purpose: Defines the Log Panel module and the local helpers/components used in this file.
|
// 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.
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
||||||
import { useSessionSelector } from '../../context/SessionContext.jsx';
|
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';
|
import CardFrame from '../CardFrame/index.jsx';
|
||||||
|
|
||||||
export default function LogPanel() {
|
export default function LogPanel() {
|
||||||
|
const socket = useSocket();
|
||||||
const logs = useSessionSelector((state) => state.logs);
|
const logs = useSessionSelector((state) => state.logs);
|
||||||
const rendered = useMemo(() => logs.slice().reverse(), [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 (
|
return (
|
||||||
<CardFrame title="Server logs" bodyClassName="space-y-0.5 text-base">
|
<CardFrame title="Server logs" bodyClassName="space-y-0.5 text-base">
|
||||||
<div className="surface h-64 overflow-y-auto font-mono text-xs">
|
<div className="surface h-64 overflow-y-auto font-mono text-xs">
|
||||||
|
|||||||
Reference in New Issue
Block a user