Add back logging callbacks to JS

This commit is contained in:
Caelan Sayler
2025-03-12 23:17:34 +00:00
committed by Caelan
parent 0cd6c3db3f
commit 24de4be961
4 changed files with 109 additions and 1 deletions

View File

@@ -47,6 +47,10 @@ declare module "./load" {
locator: string | null,
autoApply: boolean,
): void;
function js_set_logger_callback(
cb: (loglevel: LogLevel, msg: string) => void,
): void;
}
type VelopackHookType =
@@ -59,6 +63,8 @@ type VelopackHookType =
type VelopackHook = (version: string) => void;
type LogLevel = "info" | "warn" | "error" | "debug" | "trace";
/**
* VelopackApp helps you to handle app activation events correctly.
* This should be used as early as possible in your application startup code.
@@ -153,6 +159,14 @@ export class VelopackApp {
return this;
}
/**
* Set a callback to receive log messages from VelopackApp.
*/
setLogger(callback: (loglevel: LogLevel, msg: string) => void): VelopackApp {
addon.js_set_logger_callback(callback);
return this;
}
/**
* Set whether to automatically apply downloaded updates on startup. This is ON by default.
*/

View File

@@ -21,6 +21,7 @@ velopack.workspace = true
serde_json.workspace = true
semver.workspace = true
log.workspace = true
simplelog.workspace = true
lazy_static.workspace = true
neon.workspace = true

View File

@@ -8,6 +8,8 @@ use std::thread;
use velopack::sources::*;
use velopack::*;
mod logger;
struct UpdateManagerWrapper {
manager: UpdateManager,
}
@@ -275,12 +277,19 @@ fn js_appbuilder_run(mut cx: FunctionContext) -> JsResult<JsUndefined> {
logging::default_logfile_path(LocationContext::FromCurrentExe)
};
logging::init_logging("lib-nodejs", Some(&log_file), false, false);
logging::init_logging("lib-nodejs", Some(&log_file), false, false, Some(logger::create_shared_logger()));
builder.run();
Ok(undefined)
}
fn js_set_logger_callback(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let arg_cb = cx.argument::<JsFunction>(0)?;
let cb_root = arg_cb.root(&mut cx);
logger::set_logger_callback(Some(cb_root), &mut cx);
Ok(cx.undefined())
}
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("js_new_update_manager", js_new_update_manager)?;
@@ -292,5 +301,6 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("js_download_update_async", js_download_update_async)?;
cx.export_function("js_wait_exit_then_apply_update", js_wait_exit_then_apply_update)?;
cx.export_function("js_appbuilder_run", js_appbuilder_run)?;
cx.export_function("js_set_logger_callback", js_set_logger_callback)?;
Ok(())
}

View File

@@ -0,0 +1,83 @@
use std::sync::{Arc, Mutex};
use lazy_static::lazy_static;
use log::{Level, LevelFilter, Log, Metadata, Record};
use neon::{event::Channel, prelude::*};
use simplelog::{Config, SharedLogger};
lazy_static! {
static ref LOGGER_CB: Arc<Mutex<Option<Root<JsFunction>>>> = Arc::new(Mutex::new(None));
static ref LOGGER_CHANNEL: Arc<Mutex<Option<Channel>>> = Arc::new(Mutex::new(None));
}
struct LoggerImpl {}
impl SharedLogger for LoggerImpl {
fn level(&self) -> LevelFilter {
LevelFilter::max()
}
fn config(&self) -> Option<&Config> {
None
}
fn as_log(self: Box<Self>) -> Box<dyn Log> {
Box::new(*self)
}
}
impl Log for LoggerImpl {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= log::max_level()
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
let text = format!("{}", record.args());
let level = match record.level() {
Level::Error => "error",
Level::Warn => "warn",
Level::Info => "info",
Level::Debug => "debug",
Level::Trace => "trace",
};
if let Ok(channel_opt) = LOGGER_CHANNEL.lock() {
if channel_opt.is_some() {
let channel = channel_opt.as_ref().unwrap();
channel.send(move |mut cx| {
if let Ok(cb_lock) = LOGGER_CB.lock() {
if let Some(cb) = &*cb_lock {
let undefined = cx.undefined();
let args = vec![cx.string(level).upcast(), cx.string(text).upcast()];
cb.to_inner(&mut cx).call(&mut cx, undefined, args)?;
return Ok(());
}
}
Ok(())
});
}
}
}
fn flush(&self) {}
}
pub fn create_shared_logger() -> Box<dyn SharedLogger> {
Box::new(LoggerImpl {})
}
pub fn set_logger_callback(callback: Option<Root<JsFunction>>, cx: &mut FunctionContext) {
if let Ok(mut cb_lock) = LOGGER_CB.lock() {
let cb_taken = cb_lock.take();
if let Some(cb_exist) = cb_taken {
cb_exist.drop(cx);
}
*cb_lock = callback;
}
}