import { Fragment, useEffect, useRef, useState } from "react"; import { ExclamationTriangleIcon } from "@heroicons/react/24/solid"; import format from "date-fns/format"; import { DebounceInput } from "react-debounce-input"; import { APIClient } from "../api/APIClient"; import { Checkbox } from "../components/Checkbox"; import { classNames, simplifyDate } from "../utils"; import { SettingsContext } from "../utils/Context"; import { EmptySimple } from "../components/emptystates"; import { Cog6ToothIcon, DocumentArrowDownIcon } from "@heroicons/react/24/outline"; import { useQuery } from "react-query"; import { Menu, Transition } from "@headlessui/react"; import { baseUrl } from "../utils"; type LogEvent = { time: string; level: string; message: string; }; type LogLevel = "TRACE" | "DEBUG" | "INFO" | "ERROR" | "WARN"; const LogColors: Record = { "TRACE": "text-purple-300", "DEBUG": "text-yellow-500", "INFO": "text-green-500", "ERROR": "text-red-500", "WARN": "text-yellow-500" }; export const Logs = () => { const [settings] = SettingsContext.use(); const messagesEndRef = useRef(null); const [logs, setLogs] = useState([]); const [searchFilter, setSearchFilter] = useState(""); const [filteredLogs, setFilteredLogs] = useState([]); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth", block: "end", inline: "end" }); }; useEffect(() => { const es = APIClient.events.logs(); es.onmessage = (event) => { const newData = JSON.parse(event.data) as LogEvent; setLogs((prevState) => [...prevState, newData]); if (settings.scrollOnNewLog) scrollToBottom(); }; return () => es.close(); }, [setLogs, settings]); useEffect(() => { if (!searchFilter.length) { setFilteredLogs(logs); return; } const newLogs: LogEvent[] = []; logs.forEach((log) => { if (log.message.indexOf(searchFilter) !== -1) newLogs.push(log); }); setFilteredLogs(newLogs); }, [logs, searchFilter]); return (

Logs

setSearchFilter(event.target.value.toLowerCase().trim())} id="filter" type="text" autoComplete="off" 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", "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..." />
{filteredLogs.map((entry, idx) => (
{format(new Date(entry.time), "HH:mm:ss.SSS")} {entry.level in LogColors ? ( {entry.level} {" "} ) : null} {entry.message}
))}
); }; export const LogFiles = () => { const { isLoading, data } = useQuery( ["log-files"], () => APIClient.logs.files(), { retry: false, refetchOnWindowFocus: false, onError: err => console.log(err) } ); return (

Log files

Download old log files.

{data && data.files.length > 0 ? (
  1. Name
    Size
    Last modified
  2. {data && data.files.map((f, idx) => )}
) : ( )}
); }; interface LogFilesItemProps { file: LogFile; } const Dots = () => { const [step, setStep] = useState(1); useEffect(() => { const interval = setInterval(() => { setStep((prevStep) => (prevStep % 3) + 1); }, 300); return () => clearInterval(interval); }, []); return (
); }; const LogFilesItem = ({ file }: LogFilesItemProps) => { const [isDownloading, setIsDownloading] = useState(false); const handleDownload = async () => { setIsDownloading(true); const response = await fetch(`${baseUrl()}api/logs/files/${file.filename}`); const blob = await response.blob(); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = file.filename; link.click(); URL.revokeObjectURL(url); setIsDownloading(false); }; return (
  • {file.filename}
    {file.size}
    {simplifyDate(file.updated_at)}
  • ); }; // interface LogsDropdownProps {} const LogsDropdown = () => { const [settings, setSettings] = SettingsContext.use(); const onSetValue = ( key: "scrollOnNewLog" | "indentLogLines" | "hideWrappedText", newValue: boolean ) => setSettings((prevState) => ({ ...prevState, [key]: newValue })); return (
    {({ active }) => ( onSetValue("scrollOnNewLog", newValue)} /> )} {({ active }) => ( onSetValue("indentLogLines", newValue)} /> )} {({ active }) => ( onSetValue("hideWrappedText", newValue)} /> )}
    ); };