Send front-end log messages to log file

This commit is contained in:
NGnius (Graham) 2022-10-16 20:38:12 -04:00
parent 4724576f72
commit 044f768768
7 changed files with 112 additions and 14 deletions

35
backend/src/api/log_it.rs Normal file
View file

@ -0,0 +1,35 @@
use usdpl_back::core::serdes::Primitive;
use super::ApiParameterType;
/// API web method to send log messages to the back-end log, callable from the front-end
pub fn log_it() -> impl Fn(ApiParameterType) -> ApiParameterType {
move |params| {
if let Some(Primitive::F64(level)) = params.get(0) {
if let Some(Primitive::String(msg)) = params.get(1) {
log_msg_by_level(*level as u8, msg);
vec![true.into()]
} else if let Some(Primitive::Json(msg)) = params.get(1) {
log_msg_by_level(*level as u8, msg);
vec![true.into()]
} else {
log::warn!("Got log_it call with wrong/missing 2nd parameter");
vec![false.into()]
}
} else {
log::warn!("Got log_it call with wrong/missing 1st parameter");
vec![false.into()]
}
}
}
fn log_msg_by_level(level: u8, msg: &str) {
match level {
1 => log::trace!("FRONT-END: {}", msg),
2 => log::debug!("FRONT-END: {}", msg),
3 => log::info!("FRONT-END: {}", msg),
4 => log::warn!("FRONT-END: {}", msg),
5 => log::error!("FRONT-END: {}", msg),
_ => log::trace!("FRONT-END: {}", msg),
}
}

View file

@ -2,6 +2,7 @@ mod about;
pub(crate) mod async_utils; pub(crate) mod async_utils;
mod get_display; mod get_display;
mod get_items; mod get_items;
mod log_it;
mod on_event; mod on_event;
mod on_javascript_result; mod on_javascript_result;
mod on_update; mod on_update;
@ -13,6 +14,7 @@ mod types;
pub use about::get_about; pub use about::get_about;
pub use get_display::GetDisplayEndpoint; pub use get_display::GetDisplayEndpoint;
pub use get_items::get_items; pub use get_items::get_items;
pub use log_it::log_it;
pub use on_event::on_event; pub use on_event::on_event;
pub use on_javascript_result::on_javascript_result; pub use on_javascript_result::on_javascript_result;
pub use on_update::on_update; pub use on_update::on_update;

View file

@ -25,7 +25,7 @@ pub fn on_event(sender: Sender<QueueItem>) -> impl Fn(ApiParameterType) -> ApiPa
vec![true.into()] vec![true.into()]
}, },
Err(e) => { Err(e) => {
log::warn!("Failed to parse event json: {}", e); log::error!("Failed to parse event json: {}", e);
vec![false.into()] vec![false.into()]
} }
} }

View file

@ -31,6 +31,7 @@ fn main() -> Result<(), ()> {
.register_async("get_display", api::GetDisplayEndpoint::new(sender.clone())) .register_async("get_display", api::GetDisplayEndpoint::new(sender.clone()))
.register_async("get_javascript_to_run", api::GetJavascriptEndpoint::new(sender.clone())) .register_async("get_javascript_to_run", api::GetJavascriptEndpoint::new(sender.clone()))
.register_blocking("get_items", api::get_items(sender.clone())) .register_blocking("get_items", api::get_items(sender.clone()))
.register("log", api::log_it())
.register("on_javascript_result", api::on_javascript_result(sender.clone())) .register("on_javascript_result", api::on_javascript_result(sender.clone()))
.register("on_update", api::on_update(sender.clone())) .register("on_update", api::on_update(sender.clone()))
.register("on_steam_event", api::on_event(sender.clone())) .register("on_steam_event", api::on_event(sender.clone()))

View file

@ -11,6 +11,6 @@ class Plugin:
# Asyncio-compatible long-running code, executed in a task when the plugin is loaded # Asyncio-compatible long-running code, executed in a task when the plugin is loaded
async def _main(self): async def _main(self):
# startup # startup
#self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend", "--config", ""]) self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend", "--config", ""])
while True: while True:
await asyncio.sleep(1) await asyncio.sleep(1)

View file

@ -98,6 +98,14 @@ export type CJavascriptResult = {
export type CJavascriptResponse = CJavascriptResult | CErrorResult; export type CJavascriptResponse = CJavascriptResult | CErrorResult;
export enum CLogLevel {
TRACE = 1,
DEBUG = 2,
INFO = 3,
WARN = 4,
ERROR = 5,
}
export async function getElements(): Promise<CElement[]> { export async function getElements(): Promise<CElement[]> {
return (await call_backend("get_items", []))[0]; return (await call_backend("get_items", []))[0];
} }
@ -129,3 +137,7 @@ export async function onJavascriptResult(id: number, value: any): Promise<boolea
export async function onSteamEvent(data: CSteamEvent): Promise<boolean> { export async function onSteamEvent(data: CSteamEvent): Promise<boolean> {
return (await call_backend("on_steam_event", [data]))[0]; return (await call_backend("on_steam_event", [data]))[0];
} }
export async function log(level: CLogLevel, msg: string): Promise<boolean> {
return (await call_backend("log", [level, msg]))[0];
}

View file

@ -35,36 +35,58 @@ let update = () => {};
let updateTasks: (() => void)[] = []; let updateTasks: (() => void)[] = [];
let displayErrors: number[] = [];
const DISPLAY_ERROR_ABORT_THRESHHOLD = 8;
function displayCallback(index: number) { function displayCallback(index: number) {
return (newVal: backend.CDisplayResponse) => { return (newVal: backend.CDisplayResponse) => {
if (newVal != null) { if (newVal != null) {
switch (newVal.result) { switch (newVal.result) {
case "value": case "value":
displayErrors[index] = 0;
let val = newVal as backend.CValueResult; let val = newVal as backend.CValueResult;
console.log("CAYLON: Got display for " + index.toString(), val); console.log("CAYLON: Got display for " + index.toString(), val);
backend.log(backend.CLogLevel.DEBUG, "Got display for " + index.toString());
set_value(DISPLAY_KEY + index.toString(), val.value); set_value(DISPLAY_KEY + index.toString(), val.value);
break; break;
case "error": case "error":
displayErrors[index]++;
let err = newVal as backend.CErrorResult; let err = newVal as backend.CErrorResult;
console.warn("CAYLON: Got display error for " + index.toString(), err); console.warn("CAYLON: Got display error for " + index.toString(), err);
backend.log(backend.CLogLevel.WARN, "Got display error for " + index.toString());
break; break;
default: default:
console.error("CAYLON: Got invalid display response for " + index.toString(), newVal); console.error("CAYLON: Got invalid display response for " + index.toString(), newVal);
backend.log(backend.CLogLevel.ERROR, "Got invalid display response for " + index.toString());
break; break;
} }
} else { } else {
displayErrors[index]++;
console.warn("CAYLON: Ignoring null display result for " + index.toString()); console.warn("CAYLON: Ignoring null display result for " + index.toString());
backend.log(backend.CLogLevel.WARN, "Ignoring null display result for " + index.toString());
}
if (displayErrors[index] < DISPLAY_ERROR_ABORT_THRESHHOLD) {
updateTasks.push(() => backend.resolve(backend.getDisplay(index), displayCallback(index)));
update();
} else {
console.error("CAYLON: Got too many display errors for " + index.toString() + ", stopping display updates for element");
backend.log(backend.CLogLevel.ERROR, "Got too many display errors for " + index.toString() + ", stopping display updates for element");
} }
updateTasks.push(() => backend.resolve(backend.getDisplay(index), displayCallback(index)));
update();
} }
} }
let jsErrors: number = 0;
const JAVASCRIPT_ERROR_ABORT_THRESHHOLD = 16;
function onGetElements() { function onGetElements() {
displayErrors = [];
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
console.log("CAYLON: req display for item #" + i.toString()); console.log("CAYLON: req display for item #" + i.toString());
backend.log(backend.CLogLevel.DEBUG, "req display for item #" + i.toString());
displayErrors.push(0);
backend.resolve(backend.getDisplay(i), displayCallback(i)); backend.resolve(backend.getDisplay(i), displayCallback(i));
} }
jsErrors = 0;
backend.resolve(backend.getJavascriptToRun(), jsCallback()); backend.resolve(backend.getJavascriptToRun(), jsCallback());
register_for_steam_events(); register_for_steam_events();
} }
@ -73,24 +95,43 @@ const eval2 = eval;
function jsCallback() { function jsCallback() {
return (script: backend.CJavascriptResponse) => { return (script: backend.CJavascriptResponse) => {
// register next callback (before running JS, in case that crashes)
if (jsErrors < JAVASCRIPT_ERROR_ABORT_THRESHHOLD) {
backend.resolve(backend.getJavascriptToRun(), jsCallback());
} else {
console.error("CAYLON: Got too many javascript errors, stopping remote javascript execution");
backend.log(backend.CLogLevel.ERROR, "Got too many javascript errors, stopping remote javascript execution");
}
if (script != null) { if (script != null) {
switch (script.result) { switch (script.result) {
case "javascript": case "javascript":
// register next callback (before running JS, in case that crashes)
backend.resolve(backend.getJavascriptToRun(), jsCallback());
let toRun = script as backend.CJavascriptResult; let toRun = script as backend.CJavascriptResult;
console.log("CAYLON: Got javascript " + toRun.id.toString(), toRun); console.log("CAYLON: Got javascript " + toRun.id.toString(), toRun);
let result = eval2(toRun.raw); backend.log(backend.CLogLevel.DEBUG, "Got javascript " + toRun.id.toString());
backend.onJavascriptResult(toRun.id, result); try {
let result = eval2(toRun.raw);
backend.onJavascriptResult(toRun.id, result);
jsErrors = 0;
} catch (err) {
jsErrors++;
console.warn("CAYLON: Javascript " + toRun.id.toString() + "failed", err);
backend.log(backend.CLogLevel.WARN, "Javascript " + toRun.id.toString() + "failed");
}
break; break;
case "error": case "error":
jsErrors++;
let err = script as backend.CErrorResult; let err = script as backend.CErrorResult;
console.warn("CAYLON: Got javascript retrieval error", err); console.warn("CAYLON: Got javascript retrieval error", err);
backend.log(backend.CLogLevel.WARN, "Got javascript retrieval error");
break; break;
default: default:
jsErrors++;
console.error("CAYLON: Got invalid javascript response", script); console.error("CAYLON: Got invalid javascript response", script);
backend.log(backend.CLogLevel.ERROR, "Got invalid javascript response");
break; break;
} }
} else {
jsErrors++;
} }
} }
} }
@ -102,14 +143,17 @@ function jsCallback() {
let about_promise = backend.getAbout(); let about_promise = backend.getAbout();
let elements_promise = backend.getElements(); let elements_promise = backend.getElements();
about = await about_promise; about = await about_promise;
console.log("CAYLON: got about", about); console.log("CAYLON: Got about", about);
backend.log(backend.CLogLevel.DEBUG, "Got about");
let result = await elements_promise; let result = await elements_promise;
console.log("CAYLON: got elements", result); console.log("CAYLON: Got elements", result);
backend.log(backend.CLogLevel.DEBUG, "Got elements");
if (result != null) { if (result != null) {
items = result; items = result;
onGetElements(); onGetElements();
} else { } else {
console.warn("CAYLON: backend connection failed"); console.error("CAYLON: Backend connection failed");
backend.log(backend.CLogLevel.ERROR, "Backend connection failed");
} }
backend.resolve(backend.getJavascriptToRun(), jsCallback()); backend.resolve(backend.getJavascriptToRun(), jsCallback());
register_for_steam_events(); register_for_steam_events();
@ -149,19 +193,22 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
backend.resolve(backend.reload(), backend.resolve(backend.reload(),
(reload_items: backend.CElement[]) => { (reload_items: backend.CElement[]) => {
items = reload_items; items = reload_items;
console.log("CAYLON: got elements", reload_items); console.log("CAYLON: Got elements", reload_items);
backend.log(backend.CLogLevel.DEBUG, "Got elements for reload");
if (reload_items != null) { if (reload_items != null) {
items = reload_items; items = reload_items;
onGetElements(); onGetElements();
} else { } else {
console.warn("CAYLON: backend connection failed"); console.error("CAYLON: Backend connection failed");
backend.log(backend.CLogLevel.ERROR, "Backend connection failed on reload");
} }
update(); update();
}); });
backend.resolve(backend.getAbout(), backend.resolve(backend.getAbout(),
(new_about: backend.CAbout) => { (new_about: backend.CAbout) => {
about = new_about; about = new_about;
console.log("CAYLON: got about", about); console.log("CAYLON: Got about", about);
backend.log(backend.CLogLevel.DEBUG, "Got about for reload");
update(); update();
}); });
}}> }}>
@ -188,6 +235,7 @@ function buildHtmlElement(element: backend.CElement, index: number, updateIdc: a
return buildEventDisplay(element as backend.CEventDisplay, index, updateIdc); return buildEventDisplay(element as backend.CEventDisplay, index, updateIdc);
} }
console.error("CAYLON: Unsupported element", element); console.error("CAYLON: Unsupported element", element);
backend.log(backend.CLogLevel.ERROR, "Unsupported element " + element.element);
return <div>Unsupported</div>; return <div>Unsupported</div>;
} }