Add button for downloading full set of logs (#13188)

This commit is contained in:
Nicolas Mowen 2024-08-19 08:53:33 -06:00
parent c268a126dc
commit a77436eec3
2 changed files with 93 additions and 26 deletions

View File

@ -456,6 +456,19 @@ def vainfo():
@bp.route("/logs/<service>", methods=["GET"])
def logs(service: str):
def download_logs(service_location: str):
try:
file = open(service_location, "r")
contents = file.read()
file.close()
return jsonify(contents)
except FileNotFoundError as e:
logger.error(e)
return make_response(
jsonify({"success": False, "message": "Could not find log file"}),
500,
)
log_locations = {
"frigate": "/dev/shm/logs/frigate/current",
"go2rtc": "/dev/shm/logs/go2rtc/current",
@ -470,6 +483,9 @@ def logs(service: str):
404,
)
if request.args.get("download", type=bool, default=False):
return download_logs(service_location)
start = request.args.get("start", type=int, default=0)
end = request.args.get("end", type=int)

View File

@ -21,16 +21,34 @@ import { cn } from "@/lib/utils";
import { MdVerticalAlignBottom } from "react-icons/md";
import { parseLogLines } from "@/utils/logUtil";
import useKeyboardListener from "@/hooks/use-keyboard-listener";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import scrollIntoView from "scroll-into-view-if-needed";
import { FaDownload } from "react-icons/fa";
type LogRange = { start: number; end: number };
function Logs() {
const [logService, setLogService] = useState<LogType>("frigate");
const tabsRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
document.title = `${logService[0].toUpperCase()}${logService.substring(1)} Logs - Frigate`;
}, [logService]);
useEffect(() => {
if (tabsRef.current) {
const element = tabsRef.current.querySelector(
`[data-nav-item="${logService}"]`,
);
if (element instanceof HTMLElement) {
scrollIntoView(element, {
behavior: "smooth",
inline: "start",
});
}
}
}, [tabsRef, logService]);
// log data handling
const logPageSize = useMemo(() => {
@ -118,6 +136,27 @@ function Logs() {
}
}, [logs, logRange]);
const handleDownloadLogs = useCallback(() => {
axios
.get(`logs/${logService}?download=true`)
.then((resp) => {
const element = document.createElement("a");
element.setAttribute(
"href",
"data:text/plain;charset=utf-8," + encodeURIComponent(resp.data),
);
element.setAttribute("download", `${logService}-logs.txt`);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
})
.catch(() => {});
}, [logService]);
// scroll to bottom
const [initialScroll, setInitialScroll] = useState(false);
@ -266,33 +305,37 @@ function Logs() {
<Toaster position="top-center" closeButton={true} />
<LogInfoDialog logLine={selectedLog} setLogLine={setSelectedLog} />
<div className="flex items-center justify-between">
<ToggleGroup
className="*:rounded-md *:px-3 *:py-4"
type="single"
size="sm"
value={logService}
onValueChange={(value: LogType) => {
if (value) {
setLogs([]);
setLogLines([]);
setFilterSeverity(undefined);
setLogService(value);
}
}} // don't allow the severity to be unselected
>
{Object.values(logTypes).map((item) => (
<ToggleGroupItem
key={item}
className={`flex items-center justify-between gap-2 ${logService == item ? "" : "text-muted-foreground"}`}
value={item}
data-nav-item={item}
aria-label={`Select ${item}`}
<div className="relative flex h-11 w-full items-center justify-between">
<ScrollArea className="w-full whitespace-nowrap">
<div ref={tabsRef} className="flex flex-row">
<ToggleGroup
type="single"
size="sm"
value={logService}
onValueChange={(value: LogType) => {
if (value) {
setLogs([]);
setLogLines([]);
setFilterSeverity(undefined);
setLogService(value);
}
}} // don't allow the severity to be unselected
>
<div className="capitalize">{item}</div>
</ToggleGroupItem>
))}
</ToggleGroup>
{Object.values(logTypes).map((item) => (
<ToggleGroupItem
key={item}
className={`flex items-center justify-between gap-2 ${logService == item ? "" : "text-muted-foreground"}`}
value={item}
data-nav-item={item}
aria-label={`Select ${item}`}
>
<div className="capitalize">{item}</div>
</ToggleGroupItem>
))}
</ToggleGroup>
<ScrollBar orientation="horizontal" className="h-0" />
</div>
</ScrollArea>
<div className="flex items-center gap-2">
<Button
className="flex items-center justify-between gap-2"
@ -304,6 +347,14 @@ function Logs() {
Copy to Clipboard
</div>
</Button>
<Button
className="flex items-center justify-between gap-2"
size="sm"
onClick={handleDownloadLogs}
>
<FaDownload className="text-secondary-foreground" />
<div className="hidden text-primary md:block">Download</div>
</Button>
<LogLevelFilterButton
selectedLabels={filterSeverity}
updateLabelFilter={setFilterSeverity}