Event logs bug fix

minor improvements download logs, auto scroll, clear logs
This commit is contained in:
Stijnus 2024-12-13 01:40:58 +01:00
parent e39f16e436
commit 87057f8137
3 changed files with 78 additions and 40 deletions

View File

@ -3,14 +3,34 @@ import { useSettings } from '~/lib/hooks/useSettings';
import { toast } from 'react-toastify';
import { Switch } from '~/components/ui/Switch';
import { logStore, type LogEntry } from '~/lib/stores/logs';
import { useStore } from '@nanostores/react';
export default function EventLogsTab() {
const {} = useSettings();
const showLogs = useStore(logStore.showLogs);
const [logLevel, setLogLevel] = useState<LogEntry['level']>('info');
const [autoScroll, setAutoScroll] = useState(true);
const [searchQuery, setSearchQuery] = useState('');
const [, forceUpdate] = useState({});
const filteredLogs = useMemo(() => {
const logs = logStore.getLogs();
return logs.filter((log) => {
const matchesLevel = !logLevel || log.level === logLevel;
const matchesSearch =
!searchQuery ||
log.message.toLowerCase().includes(searchQuery.toLowerCase()) ||
JSON.stringify(log.details).toLowerCase().includes(searchQuery.toLowerCase());
return matchesLevel && matchesSearch;
});
}, [logLevel, searchQuery]);
// Effect to initialize showLogs
useEffect(() => {
logStore.showLogs.set(true);
}, []);
useEffect(() => {
// Add some initial logs for testing
logStore.logSystem('System started', { version: '1.0.0' });
@ -18,6 +38,14 @@ export default function EventLogsTab() {
logStore.logError('Failed to connect to provider', new Error('Connection timeout'), { provider: 'OpenAI' });
}, []);
useEffect(() => {
const container = document.querySelector('.logs-container');
if (container && autoScroll) {
container.scrollTop = container.scrollHeight;
}
}, [filteredLogs, autoScroll]);
const handleClearLogs = useCallback(() => {
if (confirm('Are you sure you want to clear all logs?')) {
logStore.clearLogs();
@ -54,10 +82,6 @@ export default function EventLogsTab() {
}
}, []);
const filteredLogs = useMemo(() => {
return logStore.getFilteredLogs(logLevel, undefined, searchQuery);
}, [logLevel, searchQuery]);
const getLevelColor = (level: LogEntry['level']) => {
switch (level) {
case 'info':
@ -76,15 +100,23 @@ export default function EventLogsTab() {
return (
<div className="p-4">
<div className="flex flex-col space-y-4 mb-4">
<div className="flex justify-between items-center">
{/* Title and Toggles Row */}
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
<h3 className="text-lg font-medium text-bolt-elements-textPrimary">Event Logs</h3>
<div className="flex items-center space-x-2">
<span className="text-sm text-bolt-elements-textSecondary">Auto-scroll</span>
<Switch checked={autoScroll} onCheckedChange={setAutoScroll} />
<div className="flex flex-wrap items-center gap-4">
<div className="flex items-center space-x-2">
<span className="text-sm text-bolt-elements-textSecondary whitespace-nowrap">Show Actions</span>
<Switch checked={showLogs} onCheckedChange={(checked) => logStore.showLogs.set(checked)} />
</div>
<div className="flex items-center space-x-2">
<span className="text-sm text-bolt-elements-textSecondary whitespace-nowrap">Auto-scroll</span>
<Switch checked={autoScroll} onCheckedChange={setAutoScroll} />
</div>
</div>
</div>
<div className="flex items-center space-x-2">
{/* Controls Row */}
<div className="flex flex-wrap items-center gap-2">
<select
value={logLevel}
onChange={(e) => setLogLevel(e.target.value as LogEntry['level'])}
@ -95,29 +127,35 @@ export default function EventLogsTab() {
<option value="error">Error</option>
<option value="debug">Debug</option>
</select>
<input
type="text"
placeholder="Search logs..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="bg-bolt-elements-bg-depth-2 text-bolt-elements-textPrimary rounded-lg px-3 py-1.5 text-sm flex-1"
/>
<button
onClick={handleExportLogs}
className="bg-blue-500 text-white rounded-lg px-3 py-1.5 hover:bg-blue-600 transition-colors duration-200 text-sm whitespace-nowrap"
>
Export Logs
</button>
<button
onClick={handleClearLogs}
className="bg-red-500 text-white rounded-lg px-3 py-1.5 hover:bg-red-600 transition-colors duration-200 text-sm whitespace-nowrap"
>
Clear Logs
</button>
<div className="flex-1 min-w-[200px]">
<input
type="text"
placeholder="Search logs..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full bg-bolt-elements-bg-depth-2 text-bolt-elements-textPrimary rounded-lg px-3 py-1.5 text-sm"
/>
</div>
{showLogs && (
<div className="flex items-center gap-2 flex-nowrap">
<button
onClick={handleExportLogs}
className="bg-blue-500 text-white rounded-lg px-3 py-1.5 hover:bg-blue-600 transition-colors duration-200 text-sm whitespace-nowrap"
>
Export Logs
</button>
<button
onClick={handleClearLogs}
className="bg-red-500 text-white rounded-lg px-3 py-1.5 hover:bg-red-600 transition-colors duration-200 text-sm whitespace-nowrap"
>
Clear Logs
</button>
</div>
)}
</div>
</div>
<div className="bg-bolt-elements-bg-depth-1 rounded-lg p-4 h-[500px] overflow-y-auto">
<div className="bg-bolt-elements-bg-depth-1 rounded-lg p-4 h-[calc(100vh-250px)] min-h-[400px] overflow-y-auto logs-container">
{filteredLogs.length === 0 ? (
<div className="text-center text-bolt-elements-textSecondary py-8">No logs found</div>
) : (
@ -126,13 +164,17 @@ export default function EventLogsTab() {
key={index}
className="text-sm mb-3 font-mono border-b border-bolt-elements-borderColor pb-2 last:border-0"
>
<div className="flex items-center space-x-2 flex-wrap">
<span className={`font-bold ${getLevelColor(log.level)}`}>[{log.level.toUpperCase()}]</span>
<span className="text-bolt-elements-textSecondary">{new Date(log.timestamp).toLocaleString()}</span>
<span className="text-bolt-elements-textPrimary">{log.message}</span>
<div className="flex items-start space-x-2 flex-wrap">
<span className={`font-bold ${getLevelColor(log.level)} whitespace-nowrap`}>
[{log.level.toUpperCase()}]
</span>
<span className="text-bolt-elements-textSecondary whitespace-nowrap">
{new Date(log.timestamp).toLocaleString()}
</span>
<span className="text-bolt-elements-textPrimary break-all">{log.message}</span>
</div>
{log.details && (
<pre className="mt-2 text-xs text-bolt-elements-textSecondary overflow-x-auto">
<pre className="mt-2 text-xs text-bolt-elements-textSecondary overflow-x-auto whitespace-pre-wrap break-all">
{JSON.stringify(log.details, null, 2)}
</pre>
)}

View File

@ -51,11 +51,7 @@ export default function ProvidersTab() {
>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<img
src={`/icons/${provider.name.toLowerCase()}.svg`}
alt={`${provider.name} icon`}
className="w-6 h-6 dark:invert"
/>
<img src={`/icons/${provider.name}.svg`} alt={`${provider.name} icon`} className="w-6 h-6 dark:invert" />
<span className="text-bolt-elements-textPrimary">{provider.name}</span>
</div>
<Switch

View File

@ -17,7 +17,7 @@ const MAX_LOGS = 1000; // Maximum number of logs to keep in memory
class LogStore {
private _logs = map<Record<string, LogEntry>>({});
showLogs = atom(false);
showLogs = atom(true);
constructor() {
// Load saved logs from cookies on initialization