116 lines
4.1 KiB
TypeScript
116 lines
4.1 KiB
TypeScript
import React, { useState } from 'react'
|
|
import {
|
|
Box,
|
|
Typography,
|
|
Paper,
|
|
TextField,
|
|
InputAdornment,
|
|
CircularProgress,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableContainer,
|
|
TableHead,
|
|
TableRow,
|
|
Chip,
|
|
} from '@mui/material'
|
|
import {
|
|
Search as SearchIcon,
|
|
FilterList as FilterIcon,
|
|
} from '@mui/icons-material'
|
|
import { useLogs } from '../hooks/useLogs'
|
|
|
|
export default function Logs() {
|
|
const [query, setQuery] = useState('')
|
|
const { logs, isLoading, isRefetching } = useLogs(query)
|
|
|
|
return (
|
|
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
|
<Box sx={{ mb: 3, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end' }}>
|
|
<Box>
|
|
<Typography variant="h4" gutterBottom>
|
|
Logs Viewer
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary">
|
|
Query and analyze system logs from Loki
|
|
</Typography>
|
|
</Box>
|
|
{isRefetching && <CircularProgress size={20} />}
|
|
</Box>
|
|
|
|
<Paper sx={{ p: 2, mb: 3 }}>
|
|
<TextField
|
|
fullWidth
|
|
size="small"
|
|
placeholder='Search logs (e.g. {pillar="worker"} |= "error")...'
|
|
value={query}
|
|
onChange={(e) => setQuery(e.target.value)}
|
|
InputProps={{
|
|
startAdornment: (
|
|
<InputAdornment position="start">
|
|
<SearchIcon fontSize="small" />
|
|
</InputAdornment>
|
|
),
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
<FilterIcon fontSize="small" color="action" cursor="pointer" />
|
|
</InputAdornment>
|
|
)
|
|
}}
|
|
/>
|
|
</Paper>
|
|
|
|
<TableContainer component={Paper} sx={{ flexGrow: 1, bgcolor: '#0d1117' }}>
|
|
<Table stickyHeader size="small">
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell sx={{ bgcolor: '#161b22', color: '#8b949e', borderBottom: '1px solid #30363d', width: 200 }}>Timestamp</TableCell>
|
|
<TableCell sx={{ bgcolor: '#161b22', color: '#8b949e', borderBottom: '1px solid #30363d', width: 120 }}>Level</TableCell>
|
|
<TableCell sx={{ bgcolor: '#161b22', color: '#8b949e', borderBottom: '1px solid #30363d' }}>Message</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{logs.map((log: any, index: number) => (
|
|
<TableRow key={index} sx={{ '&:hover': { bgcolor: '#161b22' } }}>
|
|
<TableCell sx={{ color: '#8b949e', borderBottom: '1px solid #21262d', fontFamily: 'monospace' }}>
|
|
{log.timestamp}
|
|
</TableCell>
|
|
<TableCell sx={{ borderBottom: '1px solid #21262d' }}>
|
|
<Chip
|
|
label={log.level || 'INFO'}
|
|
size="small"
|
|
sx={{
|
|
height: 20,
|
|
fontSize: '0.7rem',
|
|
bgcolor: log.level === 'ERROR' ? '#f8514933' : log.level === 'WARN' ? '#d2992233' : '#23863633',
|
|
color: log.level === 'ERROR' ? '#f85149' : log.level === 'WARN' ? '#d29922' : '#3fb950',
|
|
border: '1px solid currentColor'
|
|
}}
|
|
/>
|
|
</TableCell>
|
|
<TableCell sx={{ color: '#e6edf3', borderBottom: '1px solid #21262d', fontFamily: 'monospace' }}>
|
|
{log.message}
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
{logs.length === 0 && !isLoading && (
|
|
<TableRow>
|
|
<TableCell colSpan={3} sx={{ textAlign: 'center', py: 5, color: '#8b949e', borderBottom: 'none' }}>
|
|
No logs found for the current query
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
{isLoading && (
|
|
<TableRow>
|
|
<TableCell colSpan={3} sx={{ textAlign: 'center', py: 5, borderBottom: 'none' }}>
|
|
<CircularProgress size={30} />
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
</Box>
|
|
)
|
|
}
|