Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: filters to logs Eliza UI sections to select run and date range #328

Merged
merged 2 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Chat from "./routes/chat";
import Overview from "./routes/overview";
import Home from "./routes/home";
import useVersion from "./hooks/use-version";
import Logs from "./components/ui/logs";
import Logs from "./components/ui/logging";

const queryClient = new QueryClient({
defaultOptions: {
Expand Down Expand Up @@ -45,10 +45,7 @@ function App() {
path="settings/:agentId"
element={<Overview />}
/>
<Route
path="logs"
element={<Logs />}
/>
<Route path="logs" element={<Logs />} />
</Routes>
</div>
</SidebarInset>
Expand Down
72 changes: 72 additions & 0 deletions client/src/components/ui/logging/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useState } from "react";

export const use = () => {
const [uniqueRuns, setUniqueRuns] = useState<string[]>([]);
const [traceData, setTraceData] = useState<any[]>([]);
const [selectedRun, setSelectedRun] = useState<string | null>(null);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
const [isRun, setIsRun] = useState<boolean>(false);

// Fetch all unique runs
const fetchUniqueRuns = async () => {
setLoading(true);
try {
const response = await fetch(
"http://localhost:4000/api/traces/unique-runs"
);
if (!response.ok)
throw new Error(`HTTP error! Status: ${response.status}`);
const data = await response.json();
setUniqueRuns(data.unique_runs || []);
} catch (err: any) {
setError(err.message);
} finally {
setLoading(false);
}
};

// Fetch traces for a selected RUN ID with filters
const fetchTraceData = async (
runId: string | null,
filters: any,
page = 1
) => {
if (!runId) return;

setLoading(true);
setSelectedRun(runId);
try {
const queryParams = new URLSearchParams({
page: page.toString(),
...(filters.name && { name: filters.name }),
...(filters.start_date && { start_date: filters.start_date }),
...(filters.end_date && { end_date: filters.end_date }),
});

const response = await fetch(
`http://localhost:4000/api/traces/by-run/${runId}?${queryParams.toString()}`
);
if (!response.ok)
throw new Error(`HTTP error! Status: ${response.status}`);
const data = await response.json();
setTraceData(data.data || []);
} catch (err: any) {
setError(err.message);
} finally {
setLoading(false);
}
};

return {
uniqueRuns,
traceData,
selectedRun,
loading,
error,
fetchUniqueRuns,
fetchTraceData,
isRun,
setIsRun,
};
};
185 changes: 185 additions & 0 deletions client/src/components/ui/logging/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { useEffect, useState } from "react";
import { Label } from "../label";
import { use } from "./hooks";

function Logs() {
const {
uniqueRuns,
traceData,
selectedRun,
loading,
error,
fetchUniqueRuns,
fetchTraceData,
isRun,
setIsRun,
} = use();

// Get today's date in YYYY-MM-DD format
const today = new Date().toISOString().split("T")[0];

// State for filters (default start_date and end_date to today)
const [filters, setFilters] = useState({
name: "",
start_date: today,
end_date: today,
});

useEffect(() => {
fetchUniqueRuns();
}, []);

return (
<div className="flex flex-col p-6 h-[100vh]">
{/* Filter Section at the Top */}
<div className="p-4 rounded-lg bg-[#161616] shadow-lg sticky top-0">
<Label className="text-white text-lg font-semibold">
Filters
</Label>
<div className="grid grid-cols-4 gap-4 mt-2">
<input
type="text"
placeholder="Name"
className="p-2 rounded-lg bg-gray-800 text-white border border-gray-600"
value={filters.name}
onChange={(e) =>
setFilters({ ...filters, name: e.target.value })
}
/>
<input
type="date"
className="p-2 rounded-lg bg-gray-800 text-white border border-gray-600"
value={filters.start_date}
onChange={(e) =>
setFilters({
...filters,
start_date: e.target.value,
})
}
/>
<input
type="date"
className="p-2 rounded-lg bg-gray-800 text-white border border-gray-600"
value={filters.end_date}
onChange={(e) =>
setFilters({ ...filters, end_date: e.target.value })
}
/>
<button
className="px-4 py-2 bg-gray-600 text-white rounded-lg shadow-md hover:bg-blue-700 transition"
onClick={() => fetchTraceData(selectedRun, filters)}
>
Apply Filters
</button>
</div>
</div>

<div className="flex flex-grow h-full overflow-hidden mt-4">
{/* Left: Unique Runs List (Scrollable) */}
<div className="w-1/3 p-4 rounded-lg shadow-lg overflow-y-auto h-full">
<Label className="text-xl font-semibold text-white">
Unique Runs
</Label>

{loading && (
<div className="flex justify-center items-center mt-4">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
</div>
)}

{error && (
<div className="mt-4 p-3 bg-red-500 text-white text-sm rounded-lg">
❌ Error: {error}
</div>
)}

<div className="mt-4 grid grid-cols-1 gap-4 text-xs">
{uniqueRuns.map((run, index) => (
<div
key={index}
onClick={() => {
setIsRun(true);
fetchTraceData(run, filters);
}}
className={`p-4 bg-gray-800 rounded-lg shadow-md hover:shadow-xl transition duration-300 cursor-pointer ${
selectedRun === run
? "border-2 border-blue-500"
: ""
}`}
>
<p className="text-white font-medium">{run}</p>
</div>
))}
</div>
</div>

{/* Right: Trace Details (Scrollable) */}
{isRun && (
<div className="w-2/3 p-4 rounded-lg shadow-lg overflow-y-auto h-full">
<Label className="text-xl font-semibold text-white">
{selectedRun
? `Traces for: ${selectedRun}`
: "Select a Run"}
</Label>

{loading && (
<p className="mt-4 text-white">Loading traces...</p>
)}

{traceData.length > 0 ? (
<ul className="mt-4 space-y-4">
{traceData.map((trace, index) => (
<li
key={index}
className="p-4 bg-gray-700 rounded-lg shadow-md"
>
<p className="text-gray-300 font-semibold">
ID:{" "}
<span className="text-white">
{trace.id}
</span>
</p>
<p className="text-gray-300 font-semibold">
Run:{" "}
<span className="text-white">
{trace.run}
</span>
</p>
<p className="text-gray-300 font-semibold">
Time:{" "}
<span className="text-white">
{trace.time}
</span>
</p>
<p className="text-gray-300 font-semibold">
Name:{" "}
<span className="text-white">
{trace.name}
</span>
</p>
<p className="text-gray-300 font-semibold">
Data:
</p>
<pre className="text-white bg-gray-900 p-2 rounded-md overflow-x-auto text-sm">
{JSON.stringify(
trace.data,
null,
2
)}
</pre>
</li>
))}
</ul>
) : selectedRun && !loading ? (
<p className="mt-4 text-gray-400">
No trace data available.
</p>
) : null}
</div>
)}
</div>
</div>
);
}

export default Logs;
Loading