feat(logs): improve log search with regex (#920)

* improve log search with regex

* show empty log if regex invalid

* show red icon if regex is invalid
This commit is contained in:
soup 2023-05-08 22:56:11 +02:00 committed by GitHub
parent 759b17c9f0
commit 8347d6ded1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -23,6 +23,7 @@ import { baseUrl } from "@utils";
import { RingResizeSpinner } from "@components/Icons"; import { RingResizeSpinner } from "@components/Icons";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import Toast from "@components/notifications/Toast"; import Toast from "@components/notifications/Toast";
import { ExclamationCircleIcon } from "@heroicons/react/24/solid";
type LogEvent = { type LogEvent = {
@ -48,7 +49,9 @@ export const Logs = () => {
const [logs, setLogs] = useState<LogEvent[]>([]); const [logs, setLogs] = useState<LogEvent[]>([]);
const [searchFilter, setSearchFilter] = useState(""); const [searchFilter, setSearchFilter] = useState("");
const [regexPattern, setRegexPattern] = useState<RegExp | null>(null);
const [filteredLogs, setFilteredLogs] = useState<LogEvent[]>([]); const [filteredLogs, setFilteredLogs] = useState<LogEvent[]>([]);
const [isInvalidRegex, setIsInvalidRegex] = useState(false);
const scrollToBottom = () => { const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth", block: "end", inline: "end" }); messagesEndRef.current?.scrollIntoView({ behavior: "smooth", block: "end", inline: "end" });
@ -71,16 +74,21 @@ export const Logs = () => {
useEffect(() => { useEffect(() => {
if (!searchFilter.length) { if (!searchFilter.length) {
setFilteredLogs(logs); setFilteredLogs(logs);
setIsInvalidRegex(false);
return; return;
} }
const newLogs: LogEvent[] = []; try {
logs.forEach((log) => { const pattern = new RegExp(searchFilter, "i");
if (log.message.indexOf(searchFilter) !== -1) setRegexPattern(pattern);
newLogs.push(log); const newLogs = logs.filter(log => pattern.test(log.message));
}); setFilteredLogs(newLogs);
setIsInvalidRegex(false);
setFilteredLogs(newLogs); } catch (error) {
// Handle regex errors by showing nothing when the regex pattern is invalid
setFilteredLogs([]);
setIsInvalidRegex(true);
}
}, [logs, searchFilter]); }, [logs, searchFilter]);
return ( return (
@ -104,17 +112,21 @@ export const Logs = () => {
<DebounceInput <DebounceInput
minLength={2} minLength={2}
debounceTimeout={200} debounceTimeout={200}
onChange={(event) => setSearchFilter(event.target.value.toLowerCase().trim())} onChange={(event) => {
id="filter" const inputValue = event.target.value.toLowerCase().trim();
type="text" setSearchFilter(inputValue);
autoComplete="off" }}
className={classNames( className={classNames(
"focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
"block w-full dark:bg-gray-900 shadow-sm dark:text-gray-100 sm:text-sm rounded-md" "block w-full dark:bg-gray-900 shadow-sm dark:text-gray-100 sm:text-sm rounded-md"
)} )}
placeholder="Enter a string to filter logs by..." placeholder="Enter a regex pattern to filter logs by..."
/> />
{isInvalidRegex && (
<div className="absolute mt-1.5 right-14 items-center text-xs text-red-500">
<ExclamationCircleIcon className="h-6 w-6 inline mr-1" />
</div>
)}
<LogsDropdown /> <LogsDropdown />
</div> </div>
@ -178,7 +190,7 @@ export const LogFiles = () => {
<div className="mt-2"> <div className="mt-2">
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Log files</h2> <h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Log files</h2>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400"> <p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
Download old log files. Download old log files.
</p> </p>
</div> </div>
@ -187,13 +199,13 @@ export const LogFiles = () => {
<ol className="min-w-full relative"> <ol className="min-w-full relative">
<li className="grid grid-cols-12 mb-2 border-b border-gray-200 dark:border-gray-700"> <li className="grid grid-cols-12 mb-2 border-b border-gray-200 dark:border-gray-700">
<div className="hidden sm:block col-span-5 px-2 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"> <div className="hidden sm:block col-span-5 px-2 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Name Name
</div> </div>
<div className="col-span-8 sm:col-span-4 px-1 sm:px-2 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"> <div className="col-span-8 sm:col-span-4 px-1 sm:px-2 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Last modified Last modified
</div> </div>
<div className="col-span-3 sm:col-span-2 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"> <div className="col-span-3 sm:col-span-2 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Size Size
</div> </div>
</li> </li>