feat(ui): hide dashboard self-polling internals via default filter

On real backend, the logs page was drowning in dashboard self-polling
noise — every `/api/logs` poll emitted a 149-stage `web-server:http`
trace, burying real provider activity.

- New workspace state `hideDashboardInternals` defaults ON. Entries
  whose `source` matches `/^web-server:/i` are filtered client-side
  before they reach the list.
- Toggle exposed in advanced filters as a labelled checkbox/switch with
  a one-line description, so users can opt in to see internals when
  debugging the dashboard itself.
- `clearAdvancedFilters` resets the toggle back to ON to keep the
  default signal-clean experience.
- Header stat strip (entries / traces / errors) wired through the shell.

Backend-side `web-server:*` instrumentation stays intact (still useful
for ops debugging) — just hidden from the user-facing log feed by
default.

Refs #1138, #1141, #1142
This commit is contained in:
Tam Nhu Tran
2026-04-30 14:31:32 -04:00
parent e04598eef7
commit 4a77251021
3 changed files with 56 additions and 0 deletions
+30
View File
@@ -44,6 +44,9 @@ export interface LogsFiltersProps {
onRequestIdChange?: (v: string) => void;
timeWindow?: LogsTimeWindow;
onTimeWindowChange?: (v: LogsTimeWindow) => void;
/** When true, hides entries from `web-server:*` sources. Default ON. */
hideDashboardInternals?: boolean;
onHideDashboardInternalsChange?: (next: boolean) => void;
onClearAll?: () => void;
}
@@ -88,6 +91,8 @@ export function LogsFilters({
onRequestIdChange,
timeWindow = 'all',
onTimeWindowChange,
hideDashboardInternals = true,
onHideDashboardInternalsChange,
onClearAll,
}: LogsFiltersProps) {
const [advancedOpen, setAdvancedOpen] = useState(false);
@@ -312,6 +317,31 @@ export function LogsFilters({
</Select>
</div>
) : null}
{onHideDashboardInternalsChange ? (
<div className="flex items-start justify-between gap-3 rounded border border-border/60 bg-muted/20 p-2">
<div className="space-y-0.5">
<Label
htmlFor="logs-hide-internals"
className="block text-[12px] font-medium text-foreground"
>
Hide dashboard internals
</Label>
<p className="text-[11px] text-muted-foreground">
Suppress <code className="rounded bg-background px-1">web-server:*</code>{' '}
self-polling.
</p>
</div>
<input
id="logs-hide-internals"
type="checkbox"
role="switch"
checked={hideDashboardInternals}
onChange={(e) => onHideDashboardInternalsChange(e.target.checked)}
className={cn('mt-0.5 h-4 w-4 cursor-pointer accent-foreground', FOCUS_RING)}
aria-label="Hide dashboard internals"
/>
</div>
) : null}
</CollapsibleContent>
</Collapsible>
+15
View File
@@ -137,6 +137,18 @@ export function LogsShell({ workspace, updateConfig }: LogsShellProps) {
[workspace.entriesQuery.data]
);
// Derive header stat strip values once per data refetch.
const headerStats = useMemo(() => {
const data = workspace.entriesQuery.data ?? [];
const requestIds = new Set<string>();
let errors = 0;
for (const entry of data) {
if (entry.requestId) requestIds.add(entry.requestId);
if (entry.level === 'error') errors += 1;
}
return { entries: data.length, traces: requestIds.size, errors };
}, [workspace.entriesQuery.data]);
const focusSearch = useCallback(() => {
const input = document.getElementById('logs-search') as HTMLInputElement | null;
if (input) {
@@ -180,6 +192,8 @@ export function LogsShell({ workspace, updateConfig }: LogsShellProps) {
onRequestIdChange={workspace.setRequestIdFilter}
timeWindow={workspace.timeWindow}
onTimeWindowChange={workspace.setTimeWindow}
hideDashboardInternals={workspace.hideDashboardInternals}
onHideDashboardInternalsChange={workspace.setHideDashboardInternals}
onClearAll={workspace.clearAdvancedFilters}
/>
);
@@ -228,6 +242,7 @@ export function LogsShell({ workspace, updateConfig }: LogsShellProps) {
isFetching={workspace.entriesQuery.isFetching || workspace.sourcesQuery.isFetching}
hasError={Boolean(workspace.entriesQuery.error)}
capturedCount={workspace.entriesQuery.data?.length ?? 0}
stats={headerStats}
onRefresh={handleRefresh}
onOpenSettings={openSettings}
onOpenShortcuts={() => setShortcutsOpen(true)}
+11
View File
@@ -116,6 +116,10 @@ export function useLogsWorkspace() {
const [limit, setLimit] = useState(DEFAULT_LIMIT);
const [selectedEntryId, setSelectedEntryId] = useState<string | null>(null);
const [isPaused, setIsPaused] = useState(false);
// Default ON: dashboard self-polling generates 100s of identical entries
// per refresh which drown out real provider activity. Users can opt in to
// see internals via the advanced filter toggle.
const [hideDashboardInternals, setHideDashboardInternals] = useState(true);
const frozenIdsRef = useRef<Set<string>>(new Set());
const deferredSearch = useDeferredValue(search.trim());
@@ -151,6 +155,7 @@ export function useLogsWorkspace() {
debouncedRequestId,
timeWindow,
limit,
hideDashboardInternals ? 'hide-internals' : 'show-internals',
mockEnabled ? 'mock' : 'live',
],
queryFn: async () => {
@@ -191,6 +196,9 @@ export function useLogsWorkspace() {
const ts = Date.parse(entry.timestamp);
if (Number.isFinite(ts) && now - ts > cutoffMs) return false;
}
// Hide dashboard self-polling unless user opted in. Preserves the
// signal-to-noise ratio for fresh-load investigations.
if (hideDashboardInternals && /^web-server:/i.test(entry.source)) return false;
return true;
});
const head = filtered[0];
@@ -265,6 +273,7 @@ export function useLogsWorkspace() {
setStageFilter('');
setRequestIdFilter('');
setTimeWindow('all');
setHideDashboardInternals(true);
}, []);
return {
@@ -285,6 +294,8 @@ export function useLogsWorkspace() {
setRequestIdFilter,
timeWindow,
setTimeWindow,
hideDashboardInternals,
setHideDashboardInternals,
limit,
setLimit,
selectedEntryId: activeSelectedEntryId,