mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-26 02:40:44 -06:00
Add button for downloading full set of logs (#13188)
This commit is contained in:
parent
c268a126dc
commit
a77436eec3
@ -456,6 +456,19 @@ def vainfo():
|
|||||||
|
|
||||||
@bp.route("/logs/<service>", methods=["GET"])
|
@bp.route("/logs/<service>", methods=["GET"])
|
||||||
def logs(service: str):
|
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 = {
|
log_locations = {
|
||||||
"frigate": "/dev/shm/logs/frigate/current",
|
"frigate": "/dev/shm/logs/frigate/current",
|
||||||
"go2rtc": "/dev/shm/logs/go2rtc/current",
|
"go2rtc": "/dev/shm/logs/go2rtc/current",
|
||||||
@ -470,6 +483,9 @@ def logs(service: str):
|
|||||||
404,
|
404,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if request.args.get("download", type=bool, default=False):
|
||||||
|
return download_logs(service_location)
|
||||||
|
|
||||||
start = request.args.get("start", type=int, default=0)
|
start = request.args.get("start", type=int, default=0)
|
||||||
end = request.args.get("end", type=int)
|
end = request.args.get("end", type=int)
|
||||||
|
|
||||||
|
@ -21,16 +21,34 @@ import { cn } from "@/lib/utils";
|
|||||||
import { MdVerticalAlignBottom } from "react-icons/md";
|
import { MdVerticalAlignBottom } from "react-icons/md";
|
||||||
import { parseLogLines } from "@/utils/logUtil";
|
import { parseLogLines } from "@/utils/logUtil";
|
||||||
import useKeyboardListener from "@/hooks/use-keyboard-listener";
|
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 };
|
type LogRange = { start: number; end: number };
|
||||||
|
|
||||||
function Logs() {
|
function Logs() {
|
||||||
const [logService, setLogService] = useState<LogType>("frigate");
|
const [logService, setLogService] = useState<LogType>("frigate");
|
||||||
|
const tabsRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = `${logService[0].toUpperCase()}${logService.substring(1)} Logs - Frigate`;
|
document.title = `${logService[0].toUpperCase()}${logService.substring(1)} Logs - Frigate`;
|
||||||
}, [logService]);
|
}, [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
|
// log data handling
|
||||||
|
|
||||||
const logPageSize = useMemo(() => {
|
const logPageSize = useMemo(() => {
|
||||||
@ -118,6 +136,27 @@ function Logs() {
|
|||||||
}
|
}
|
||||||
}, [logs, logRange]);
|
}, [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
|
// scroll to bottom
|
||||||
|
|
||||||
const [initialScroll, setInitialScroll] = useState(false);
|
const [initialScroll, setInitialScroll] = useState(false);
|
||||||
@ -266,33 +305,37 @@ function Logs() {
|
|||||||
<Toaster position="top-center" closeButton={true} />
|
<Toaster position="top-center" closeButton={true} />
|
||||||
<LogInfoDialog logLine={selectedLog} setLogLine={setSelectedLog} />
|
<LogInfoDialog logLine={selectedLog} setLogLine={setSelectedLog} />
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="relative flex h-11 w-full items-center justify-between">
|
||||||
<ToggleGroup
|
<ScrollArea className="w-full whitespace-nowrap">
|
||||||
className="*:rounded-md *:px-3 *:py-4"
|
<div ref={tabsRef} className="flex flex-row">
|
||||||
type="single"
|
<ToggleGroup
|
||||||
size="sm"
|
type="single"
|
||||||
value={logService}
|
size="sm"
|
||||||
onValueChange={(value: LogType) => {
|
value={logService}
|
||||||
if (value) {
|
onValueChange={(value: LogType) => {
|
||||||
setLogs([]);
|
if (value) {
|
||||||
setLogLines([]);
|
setLogs([]);
|
||||||
setFilterSeverity(undefined);
|
setLogLines([]);
|
||||||
setLogService(value);
|
setFilterSeverity(undefined);
|
||||||
}
|
setLogService(value);
|
||||||
}} // don't allow the severity to be unselected
|
}
|
||||||
>
|
}} // 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="capitalize">{item}</div>
|
{Object.values(logTypes).map((item) => (
|
||||||
</ToggleGroupItem>
|
<ToggleGroupItem
|
||||||
))}
|
key={item}
|
||||||
</ToggleGroup>
|
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">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
className="flex items-center justify-between gap-2"
|
className="flex items-center justify-between gap-2"
|
||||||
@ -304,6 +347,14 @@ function Logs() {
|
|||||||
Copy to Clipboard
|
Copy to Clipboard
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</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
|
<LogLevelFilterButton
|
||||||
selectedLabels={filterSeverity}
|
selectedLabels={filterSeverity}
|
||||||
updateLabelFilter={setFilterSeverity}
|
updateLabelFilter={setFilterSeverity}
|
||||||
|
Loading…
Reference in New Issue
Block a user