import React, { useEffect, useContext, useRef, useState, ReactElement } from "react"; import { Typography } from "antd"; import * as lib from "@clusterio/lib"; import { useAccount } from "../model/account"; import ControlContext from "./ControlContext"; const { Paragraph } = Typography; type Parsed = { format: string; type: string; level: string; time: string; file: string; action: string; message: string; }; function formatParsedOutput(parsed: Parsed, key: number) { let time: ReactElement|string = ""; if (parsed.format === "seconds") { time = {parsed.time.padStart(8)} ; } else if (parsed.format === "date") { time = {parsed.time} ; } let info: ReactElement|string = ""; if (parsed.type === "log") { let level: ReactElement|string = parsed.level; if (level === "Script") { level = {level}; } else if (level === "Verbose") { level = {level}; } else if (level === "Info") { level = {level}; } else if (parsed.level === "Warning") { level = {level}; } else if (parsed.level === "Error") { level = {level}; } info = <>{level} {parsed.file}: ; } else if (parsed.type === "action") { info = <>[{parsed.action}] ; } return {time}{info}{parsed.message}
; } type Info = { level: string; message: string; parsed?: Parsed; }; function formatLog(info: Info, key: number): ReactElement { if (info.level === "server" && info.parsed) { return formatParsedOutput(info.parsed, key); } let level = {info.level}; return [{level}] {info.message}
; } type LogConsoleProps = { all?: boolean; controller?: boolean; hosts?: number[]; instances?: number[]; }; export default function LogConsole(props: LogConsoleProps) { let account = useAccount(); let control = useContext(ControlContext); let anchor = useRef(); let [pastLines, setPastLines] = useState([{"Loading past entries..."}
]); let [lines, setLines] = useState([]); useEffect(() => { let logFilter = { all: props.all || false, controller: props.controller || false, hostIds: props.hosts || [], instanceIds: props.instances || [], }; // Scroll view to the anchor so it sticks to the bottom let parent = anchor.current.parentElement; parent.scrollTop = parent.scrollHeight - parent.clientHeight; if (account.hasPermission("core.log.query")) { setPastLines([{"Loading past entries..."}
]); control.send(new lib.LogQueryRequest( logFilter.all, logFilter.controller, logFilter.hostIds, logFilter.instanceIds, undefined, 400, "desc", )).then(result => { setPastLines(result.log.map((info, index) => formatLog(info as Info, -index - 1)).reverse()); }).catch(err => { setPastLines([{`Error loading log: ${err.message}`}
]); }); } else { setPastLines([]); } function logHandler(info: Info) { setLines(currentLines => currentLines.concat( [formatLog(info, currentLines.length)] )); } control.onLog(logFilter, logHandler); return () => { control.offLog(logFilter, logHandler); }; }, [props.all, props.controller, (props.hosts || []).join(), (props.instances || []).join()]); return <> {pastLines} {lines}
; }