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 { toast } from 'react-toastify';
import { Switch } from '~/components/ui/Switch'; import { Switch } from '~/components/ui/Switch';
import { logStore, type LogEntry } from '~/lib/stores/logs'; import { logStore, type LogEntry } from '~/lib/stores/logs';
import { useStore } from '@nanostores/react';
export default function EventLogsTab() { export default function EventLogsTab() {
const {} = useSettings(); const {} = useSettings();
const showLogs = useStore(logStore.showLogs);
const [logLevel, setLogLevel] = useState<LogEntry['level']>('info'); const [logLevel, setLogLevel] = useState<LogEntry['level']>('info');
const [autoScroll, setAutoScroll] = useState(true); const [autoScroll, setAutoScroll] = useState(true);
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const [, forceUpdate] = 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(() => { useEffect(() => {
// Add some initial logs for testing // Add some initial logs for testing
logStore.logSystem('System started', { version: '1.0.0' }); 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' }); 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(() => { const handleClearLogs = useCallback(() => {
if (confirm('Are you sure you want to clear all logs?')) { if (confirm('Are you sure you want to clear all logs?')) {
logStore.clearLogs(); 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']) => { const getLevelColor = (level: LogEntry['level']) => {
switch (level) { switch (level) {
case 'info': case 'info':
@ -76,15 +100,23 @@ export default function EventLogsTab() {
return ( return (
<div className="p-4"> <div className="p-4">
<div className="flex flex-col space-y-4 mb-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> <h3 className="text-lg font-medium text-bolt-elements-textPrimary">Event Logs</h3>
<div className="flex items-center space-x-2"> <div className="flex flex-wrap items-center gap-4">
<span className="text-sm text-bolt-elements-textSecondary">Auto-scroll</span> <div className="flex items-center space-x-2">
<Switch checked={autoScroll} onCheckedChange={setAutoScroll} /> <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> </div>
<div className="flex items-center space-x-2"> {/* Controls Row */}
<div className="flex flex-wrap items-center gap-2">
<select <select
value={logLevel} value={logLevel}
onChange={(e) => setLogLevel(e.target.value as LogEntry['level'])} onChange={(e) => setLogLevel(e.target.value as LogEntry['level'])}
@ -95,29 +127,35 @@ export default function EventLogsTab() {
<option value="error">Error</option> <option value="error">Error</option>
<option value="debug">Debug</option> <option value="debug">Debug</option>
</select> </select>
<input <div className="flex-1 min-w-[200px]">
type="text" <input
placeholder="Search logs..." type="text"
value={searchQuery} placeholder="Search logs..."
onChange={(e) => setSearchQuery(e.target.value)} value={searchQuery}
className="bg-bolt-elements-bg-depth-2 text-bolt-elements-textPrimary rounded-lg px-3 py-1.5 text-sm flex-1" 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"
<button />
onClick={handleExportLogs} </div>
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" {showLogs && (
> <div className="flex items-center gap-2 flex-nowrap">
Export Logs <button
</button> onClick={handleExportLogs}
<button 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"
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" Export Logs
> </button>
Clear 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> </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 ? ( {filteredLogs.length === 0 ? (
<div className="text-center text-bolt-elements-textSecondary py-8">No logs found</div> <div className="text-center text-bolt-elements-textSecondary py-8">No logs found</div>
) : ( ) : (
@ -126,13 +164,17 @@ export default function EventLogsTab() {
key={index} key={index}
className="text-sm mb-3 font-mono border-b border-bolt-elements-borderColor pb-2 last:border-0" 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"> <div className="flex items-start space-x-2 flex-wrap">
<span className={`font-bold ${getLevelColor(log.level)}`}>[{log.level.toUpperCase()}]</span> <span className={`font-bold ${getLevelColor(log.level)} whitespace-nowrap`}>
<span className="text-bolt-elements-textSecondary">{new Date(log.timestamp).toLocaleString()}</span> [{log.level.toUpperCase()}]
<span className="text-bolt-elements-textPrimary">{log.message}</span> </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> </div>
{log.details && ( {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)} {JSON.stringify(log.details, null, 2)}
</pre> </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 justify-between mb-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<img <img src={`/icons/${provider.name}.svg`} alt={`${provider.name} icon`} className="w-6 h-6 dark:invert" />
src={`/icons/${provider.name.toLowerCase()}.svg`}
alt={`${provider.name} icon`}
className="w-6 h-6 dark:invert"
/>
<span className="text-bolt-elements-textPrimary">{provider.name}</span> <span className="text-bolt-elements-textPrimary">{provider.name}</span>
</div> </div>
<Switch <Switch

View File

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