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;
mod get_display;
mod get_items;
mod log_it;
mod on_event;
mod on_javascript_result;
mod on_update;
@ -13,6 +14,7 @@ mod types;
pub use about::get_about;
pub use get_display::GetDisplayEndpoint;
pub use get_items::get_items;
pub use log_it::log_it;
pub use on_event::on_event;
pub use on_javascript_result::on_javascript_result;
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()]
},
Err(e) => {
log::warn!("Failed to parse event json: {}", e);
log::error!("Failed to parse event json: {}", e);
vec![false.into()]
}
}

View file

@ -31,6 +31,7 @@ fn main() -> Result<(), ()> {
.register_async("get_display", api::GetDisplayEndpoint::new(sender.clone()))
.register_async("get_javascript_to_run", api::GetJavascriptEndpoint::new(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_update", api::on_update(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
async def _main(self):
# startup
#self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend", "--config", ""])
self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend", "--config", ""])
while True:
await asyncio.sleep(1)

View file

@ -98,6 +98,14 @@ export type CJavascriptResult = {
export type CJavascriptResponse = CJavascriptResult | CErrorResult;
export enum CLogLevel {
TRACE = 1,
DEBUG = 2,
INFO = 3,
WARN = 4,
ERROR = 5,
}
export async function getElements(): Promise<CElement[]> {
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> {
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 displayErrors: number[] = [];
const DISPLAY_ERROR_ABORT_THRESHHOLD = 8;
function displayCallback(index: number) {
return (newVal: backend.CDisplayResponse) => {
if (newVal != null) {
switch (newVal.result) {
case "value":
displayErrors[index] = 0;
let val = newVal as backend.CValueResult;
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);
break;
case "error":
displayErrors[index]++;
let err = newVal as backend.CErrorResult;
console.warn("CAYLON: Got display error for " + index.toString(), err);
backend.log(backend.CLogLevel.WARN, "Got display error for " + index.toString());
break;
default:
console.error("CAYLON: Got invalid display response for " + index.toString(), newVal);
backend.log(backend.CLogLevel.ERROR, "Got invalid display response for " + index.toString());
break;
}
} else {
displayErrors[index]++;
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() {
displayErrors = [];
for (let i = 0; i < items.length; i++) {
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));
}
jsErrors = 0;
backend.resolve(backend.getJavascriptToRun(), jsCallback());
register_for_steam_events();
}
@ -73,24 +95,43 @@ const eval2 = eval;
function jsCallback() {
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) {
switch (script.result) {
case "javascript":
// register next callback (before running JS, in case that crashes)
backend.resolve(backend.getJavascriptToRun(), jsCallback());
let toRun = script as backend.CJavascriptResult;
console.log("CAYLON: Got javascript " + toRun.id.toString(), toRun);
let result = eval2(toRun.raw);
backend.onJavascriptResult(toRun.id, result);
backend.log(backend.CLogLevel.DEBUG, "Got javascript " + toRun.id.toString());
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;
case "error":
jsErrors++;
let err = script as backend.CErrorResult;
console.warn("CAYLON: Got javascript retrieval error", err);
backend.log(backend.CLogLevel.WARN, "Got javascript retrieval error");
break;
default:
jsErrors++;
console.error("CAYLON: Got invalid javascript response", script);
backend.log(backend.CLogLevel.ERROR, "Got invalid javascript response");
break;
}
} else {
jsErrors++;
}
}
}
@ -102,14 +143,17 @@ function jsCallback() {
let about_promise = backend.getAbout();
let elements_promise = backend.getElements();
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;
console.log("CAYLON: got elements", result);
console.log("CAYLON: Got elements", result);
backend.log(backend.CLogLevel.DEBUG, "Got elements");
if (result != null) {
items = result;
onGetElements();
} 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());
register_for_steam_events();
@ -149,19 +193,22 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
backend.resolve(backend.reload(),
(reload_items: backend.CElement[]) => {
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) {
items = reload_items;
onGetElements();
} else {
console.warn("CAYLON: backend connection failed");
console.error("CAYLON: Backend connection failed");
backend.log(backend.CLogLevel.ERROR, "Backend connection failed on reload");
}
update();
});
backend.resolve(backend.getAbout(),
(new_about: backend.CAbout) => {
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();
});
}}>
@ -188,6 +235,7 @@ function buildHtmlElement(element: backend.CElement, index: number, updateIdc: a
return buildEventDisplay(element as backend.CEventDisplay, index, updateIdc);
}
console.error("CAYLON: Unsupported element", element);
backend.log(backend.CLogLevel.ERROR, "Unsupported element " + element.element);
return <div>Unsupported</div>;
}