feat: add Logs tab with cursor-paginated search, level filters, and live tail
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m3s
CI / docker (push) Successful in 1m11s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 49s

- Extend GET /api/v1/logs with cursor pagination, multi-level filtering,
  optional application scoping, and level count aggregation
- Add exchangeId, instanceId, application, mdc fields to log responses
- Refactor ClickHouseLogStore with keyset pagination (N+1 pattern)
- Add LogSearchRequest/LogSearchResponse core domain records
- Create LogSearchPageResponse wrapper DTO
- Add Logs as 4th content tab (Exchanges | Dashboard | Runtime | Logs)
- Implement LogSearch component with debounced search, level filter bar,
  expandable log entries, cursor pagination, and live tail mode
- Add cross-navigation: exchange header → logs, log tab → logs tab
- Update ClickHouseLogStoreIT with cursor, multi-level, cross-app tests

Closes: #104

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-02 08:47:16 +02:00
parent a52751da1b
commit b73f5e6dd4
22 changed files with 1405 additions and 119 deletions

View File

@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { useNavigate } from 'react-router';
import { GitBranch, Server, RotateCcw } from 'lucide-react';
import { GitBranch, Server, RotateCcw, FileText } from 'lucide-react';
import { StatusDot, MonoText, Badge } from '@cameleer/design-system';
import { useCorrelationChain } from '../../api/queries/correlation';
import { useAgents } from '../../api/queries/agents';
@@ -100,6 +100,13 @@ export function ExchangeHeader({ detail, onCorrelatedSelect, onClearSelection }:
</>
)}
<span className={styles.duration}>{formatDuration(detail.durationMs)}</span>
<button
className={styles.linkBtn}
onClick={() => navigate(`/logs/${detail.applicationId}?exchangeId=${detail.exchangeId}`)}
title="View surrounding logs"
>
<FileText size={12} className={styles.icon} />
</button>
</div>
{/* Route control / replay — only if agent supports it AND user has operator+ role */}