feat: add log source filter (app/agent) to runtime log viewers
Some checks failed
CI / cleanup-branch (push) Has been skipped
CI / build (push) Failing after 1m7s
CI / docker (push) Has been skipped
CI / deploy (push) Has been skipped
CI / deploy-feature (push) Has been skipped

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-12 10:27:59 +02:00
parent b03dfee4f3
commit 6b00bf81e3
3 changed files with 30 additions and 4 deletions

View File

@@ -15,6 +15,7 @@ export interface LogEntryResponse {
instanceId: string | null;
application: string | null;
mdc: Record<string, string> | null;
source: string | null;
}
export interface LogSearchPageResponse {
@@ -29,6 +30,7 @@ export interface LogSearchParams {
level?: string;
application?: string;
agentId?: string;
source?: string;
exchangeId?: string;
logger?: string;
from?: string;
@@ -45,6 +47,7 @@ async function fetchLogs(params: LogSearchParams): Promise<LogSearchPageResponse
if (params.level) urlParams.set('level', params.level);
if (params.application) urlParams.set('application', params.application);
if (params.agentId) urlParams.set('agentId', params.agentId);
if (params.source) urlParams.set('source', params.source);
if (params.exchangeId) urlParams.set('exchangeId', params.exchangeId);
if (params.logger) urlParams.set('logger', params.logger);
if (params.from) urlParams.set('from', params.from);
@@ -89,7 +92,7 @@ export function useLogs(
export function useApplicationLogs(
application?: string,
agentId?: string,
options?: { limit?: number; toOverride?: string; exchangeId?: string },
options?: { limit?: number; toOverride?: string; exchangeId?: string; source?: string },
) {
const refetchInterval = useRefreshInterval(15_000);
const { timeRange } = useGlobalFilters();
@@ -99,6 +102,7 @@ export function useApplicationLogs(
const params: LogSearchParams = {
application: application || undefined,
agentId: agentId || undefined,
source: options?.source || undefined,
exchangeId: options?.exchangeId || undefined,
from: useTimeRange ? timeRange.start.toISOString() : undefined,
to: useTimeRange ? to : undefined,
@@ -109,7 +113,7 @@ export function useApplicationLogs(
queryKey: ['logs', 'compat', application, agentId,
useTimeRange ? timeRange.start.toISOString() : null,
useTimeRange ? to : null,
options?.limit, options?.exchangeId],
options?.limit, options?.exchangeId, options?.source],
queryFn: () => fetchLogs(params),
enabled: !!application,
placeholderData: (prev) => prev,

View File

@@ -87,6 +87,11 @@ const LOG_LEVEL_ITEMS: ButtonGroupItem[] = [
{ value: 'trace', label: 'Trace', color: 'var(--text-muted)' },
];
const LOG_SOURCE_ITEMS: ButtonGroupItem[] = [
{ value: 'app', label: 'App' },
{ value: 'agent', label: 'Agent' },
];
// ── AgentHealth page ─────────────────────────────────────────────────────────
export default function AgentHealth() {
@@ -138,9 +143,10 @@ export default function AgentHealth() {
const [logSearch, setLogSearch] = useState('');
const [logLevels, setLogLevels] = useState<Set<string>>(new Set());
const [logSource, setLogSource] = useState<string>(''); // '' = all, 'app', 'agent'
const [logSortAsc, setLogSortAsc] = useState(false);
const [logRefreshTo, setLogRefreshTo] = useState<string | undefined>();
const { data: rawLogs } = useApplicationLogs(appId, undefined, { toOverride: logRefreshTo });
const { data: rawLogs } = useApplicationLogs(appId, undefined, { toOverride: logRefreshTo, source: logSource || undefined });
const logEntries = useMemo<LogEntry[]>(() => {
const mapped = (rawLogs || []).map((l) => ({
timestamp: l.timestamp ?? '',
@@ -554,6 +560,11 @@ export default function AgentHealth() {
</button>
)}
</div>
<ButtonGroup
items={LOG_SOURCE_ITEMS}
value={logSource ? new Set([logSource]) : new Set()}
onChange={(v) => setLogSource(v.size === 0 ? '' : [...v][0])}
/>
<ButtonGroup items={LOG_LEVEL_ITEMS} value={logLevels} onChange={setLogLevels} />
{logLevels.size > 0 && (
<Button variant="ghost" size="sm" onClick={() => setLogLevels(new Set())}>

View File

@@ -26,11 +26,17 @@ const LOG_LEVEL_ITEMS: ButtonGroupItem[] = [
{ value: 'trace', label: 'Trace', color: 'var(--text-muted)' },
];
const LOG_SOURCE_ITEMS: ButtonGroupItem[] = [
{ value: 'app', label: 'App' },
{ value: 'agent', label: 'Agent' },
];
export default function AgentInstance() {
const { appId, instanceId } = useParams();
const { timeRange } = useGlobalFilters();
const [logSearch, setLogSearch] = useState('');
const [logLevels, setLogLevels] = useState<Set<string>>(new Set());
const [logSource, setLogSource] = useState<string>(''); // '' = all, 'app', 'agent'
const [logSortAsc, setLogSortAsc] = useState(false);
const [eventSortAsc, setEventSortAsc] = useState(false);
const [logRefreshTo, setLogRefreshTo] = useState<string | undefined>();
@@ -136,7 +142,7 @@ export default function AgentInstance() {
);
// Application logs
const { data: rawLogs } = useApplicationLogs(appId, instanceId, { toOverride: logRefreshTo });
const { data: rawLogs } = useApplicationLogs(appId, instanceId, { toOverride: logRefreshTo, source: logSource || undefined });
const logEntries = useMemo<LogEntry[]>(() => {
const mapped = (rawLogs || []).map((l) => ({
timestamp: l.timestamp ?? '',
@@ -435,6 +441,11 @@ export default function AgentInstance() {
</button>
)}
</div>
<ButtonGroup
items={LOG_SOURCE_ITEMS}
value={logSource ? new Set([logSource]) : new Set()}
onChange={(v) => setLogSource(v.size === 0 ? '' : [...v][0])}
/>
<ButtonGroup items={LOG_LEVEL_ITEMS} value={logLevels} onChange={setLogLevels} />
{logLevels.size > 0 && (
<button className={logStyles.logClearFilters} onClick={() => setLogLevels(new Set())}>