Remove logging callbacks from C++ / NodeJS

This commit is contained in:
Caelan Sayler
2025-03-03 21:06:48 +00:00
parent 453446eeee
commit 961c63746a
21 changed files with 239 additions and 337 deletions

4
Cargo.lock generated
View File

@@ -2190,10 +2190,12 @@ dependencies = [
"async-std", "async-std",
"bitflags 2.9.0", "bitflags 2.9.0",
"derivative", "derivative",
"file-rotate",
"glob", "glob",
"lazy_static", "lazy_static",
"libc", "libc",
"log", "log",
"log-panics",
"normpath", "normpath",
"rand", "rand",
"regex", "regex",
@@ -2202,8 +2204,10 @@ dependencies = [
"serde_json", "serde_json",
"sha1", "sha1",
"sha2", "sha2",
"simplelog",
"tempfile", "tempfile",
"thiserror 2.0.12", "thiserror 2.0.12",
"time 0.3.37",
"ts-rs", "ts-rs",
"ureq", "ureq",
"url", "url",

View File

@@ -23,7 +23,7 @@ edition = "2021"
rust-version = "1.75" rust-version = "1.75"
[workspace.dependencies] [workspace.dependencies]
velopack = { path = "src/lib-rust" } velopack = { path = "src/lib-rust", features = ["file-logging"] }
log = "0.4" log = "0.4"
log-derive = "0.4.1" log-derive = "0.4.1"
ureq = "3.0" ureq = "3.0"

View File

@@ -116,7 +116,7 @@ pub fn install(pkg: &mut BundleZip, install_to: Option<&PathBuf>, start_args: Op
info!("Acquiring lock..."); info!("Acquiring lock...");
let paths = create_config_from_root_dir(&root_path); let paths = create_config_from_root_dir(&root_path);
let locator = VelopackLocator::new(paths, app); let locator = VelopackLocator::new_with_manifest(paths, app);
let _mutex = locator.try_get_exclusive_lock()?; let _mutex = locator.try_get_exclusive_lock()?;
let tx = if dialogs::get_silent() { let tx = if dialogs::get_silent() {

View File

@@ -156,7 +156,7 @@ fn try_legacy_migration(root_dir: &PathBuf, manifest: &Manifest) -> Result<Velop
// new shortcuts will be force-created // new shortcuts will be force-created
let mut modified_manifest = manifest.clone(); let mut modified_manifest = manifest.clone();
modified_manifest.shortcut_locations = String::new(); modified_manifest.shortcut_locations = String::new();
let locator = VelopackLocator::new(path_config, modified_manifest); let locator = VelopackLocator::new_with_manifest(path_config, modified_manifest);
let _mutex = locator.try_get_exclusive_lock()?; let _mutex = locator.try_get_exclusive_lock()?;
if !locator.get_current_bin_dir().exists() { if !locator.get_current_bin_dir().exists() {

View File

@@ -1,5 +1,4 @@
pub mod commands; pub mod commands;
pub mod logging;
pub mod shared; pub mod shared;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub mod windows; pub mod windows;
@@ -8,6 +7,5 @@ pub use shared::dialogs;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate simplelog;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;

View File

@@ -1,58 +0,0 @@
use anyhow::Result;
use simplelog::*;
use std::path::PathBuf;
use time::format_description::{modifier, Component, FormatItem};
pub fn trace_logger() {
TermLogger::init(LevelFilter::Trace, get_config(None), TerminalMode::Mixed, ColorChoice::Never).unwrap();
}
pub fn setup_logging(process_name: &str, file: Option<&PathBuf>, console: bool, verbose: bool) -> Result<()> {
let mut loggers: Vec<Box<dyn SharedLogger>> = Vec::new();
let color_choice = ColorChoice::Never;
if console {
let console_level = if verbose { LevelFilter::Debug } else { LevelFilter::Info };
loggers.push(TermLogger::new(console_level, get_config(None), TerminalMode::Mixed, color_choice));
}
if let Some(f) = file {
let file_level = if verbose { LevelFilter::Trace } else { LevelFilter::Info };
let writer = file_rotate::FileRotate::new(
f.clone(),
file_rotate::suffix::AppendCount::new(1), // keep 1 old log file
file_rotate::ContentLimit::Bytes(1 * 1024 * 1024), // 1MB max log file size
file_rotate::compression::Compression::None,
None,
);
loggers.push(WriteLogger::new(file_level, get_config(Some(process_name)), writer));
}
CombinedLogger::init(loggers)?;
log_panics::init();
Ok(())
}
fn get_config(process_name: Option<&str>) -> Config {
let mut c = ConfigBuilder::default();
let mut prefix = "".to_owned();
if let Some(pn) = process_name {
prefix = format!("[{}:{}] ", pn, std::process::id());
}
let prefix_heaped = Box::leak(prefix.into_boxed_str());
let time_format: &'static [FormatItem<'static>] = Box::leak(Box::new([
FormatItem::Literal(prefix_heaped.as_bytes()),
FormatItem::Literal(b"["),
FormatItem::Component(Component::Hour(modifier::Hour::default())),
FormatItem::Literal(b":"),
FormatItem::Component(Component::Minute(modifier::Minute::default())),
FormatItem::Literal(b":"),
FormatItem::Component(Component::Second(modifier::Second::default())),
FormatItem::Literal(b"]"),
]));
c.set_time_format_custom(time_format);
let _ = c.set_time_offset_to_local(); // might fail if local tz can't be determined
c.build()
}

View File

@@ -79,7 +79,7 @@ fn main_inner() -> Result<()> {
let verbose = matches.get_flag("verbose"); let verbose = matches.get_flag("verbose");
let logfile = matches.get_one::<PathBuf>("log"); let logfile = matches.get_one::<PathBuf>("log");
logging::setup_logging("setup", logfile, true, verbose)?; velopack::logging::init_logging("setup", logfile, true, verbose);
let debug = matches.get_one::<PathBuf>("debug"); let debug = matches.get_one::<PathBuf>("debug");
let install_to = matches.get_one::<PathBuf>("installto"); let install_to = matches.get_one::<PathBuf>("installto");

View File

@@ -1,8 +1,6 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#![allow(dead_code)] #![allow(dead_code)]
mod logging;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
@@ -21,7 +19,7 @@ fn main() -> ExitCode {
my_dir.join("Velopack.log") my_dir.join("Velopack.log")
}; };
let _ = logging::setup_logging("stub", Some(&default_log_file), false, false); let _ = velopack::logging::init_logging("stub", Some(&default_log_file), false, false);
info!("--"); info!("--");
info!("Starting Velopack Stub (at {:?})", my_path); info!("Starting Velopack Stub (at {:?})", my_path);

View File

@@ -7,8 +7,9 @@ extern crate log;
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use clap::{arg, value_parser, ArgMatches, Command}; use clap::{arg, value_parser, ArgMatches, Command};
use std::{env, path::PathBuf}; use std::{env, path::PathBuf};
use velopack::locator::{self, auto_locate_app_manifest, LocationContext}; use velopack::locator::{auto_locate_app_manifest, LocationContext};
use velopack_bins::{*, shared::OperationWait}; use velopack::logging::*;
use velopack_bins::{shared::OperationWait, *};
#[rustfmt::skip] #[rustfmt::skip]
fn root_command() -> Command { fn root_command() -> Command {
@@ -132,8 +133,8 @@ fn main() -> Result<()> {
let verbose = get_flag_or_false(&matches, "verbose"); let verbose = get_flag_or_false(&matches, "verbose");
let log_file = matches.get_one("log"); let log_file = matches.get_one("log");
let desired_log_file = log_file.cloned().unwrap_or(locator::default_log_location(LocationContext::IAmUpdateExe)); let desired_log_file = log_file.cloned().unwrap_or(default_logfile_from_context(LocationContext::IAmUpdateExe));
logging::setup_logging("update", Some(&desired_log_file), true, verbose)?; init_logging("update", Some(&desired_log_file), true, verbose);
// change working directory to the parent directory of the exe // change working directory to the parent directory of the exe
let mut containing_dir = env::current_exe()?; let mut containing_dir = env::current_exe()?;
@@ -212,7 +213,6 @@ fn apply(matches: &ArgMatches) -> Result<()> {
Ok(()) Ok(())
} }
fn get_start_args(matches: &ArgMatches) -> (OperationWait, Option<&String>, Option<&String>, Option<Vec<&str>>) { fn get_start_args(matches: &ArgMatches) -> (OperationWait, Option<&String>, Option<&String>, Option<Vec<&str>>) {
let legacy_args = matches.get_one::<String>("args"); let legacy_args = matches.get_one::<String>("args");
let exe_name = matches.get_one::<String>("EXE_NAME"); let exe_name = matches.get_one::<String>("EXE_NAME");
@@ -246,10 +246,10 @@ fn uninstall(_matches: &ArgMatches) -> Result<()> {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
#[test] #[test]
fn test_cli_parse_handles_equals_spaces() { fn test_cli_parse_handles_equals_spaces() {
let command = vec!["C:\\Some Path\\With = Spaces\\Update.exe", "apply" , "--package", "C:\\Some Path\\With = Spaces\\Package.zip"]; let command = vec!["C:\\Some Path\\With = Spaces\\Update.exe", "apply", "--package", "C:\\Some Path\\With = Spaces\\Package.zip"];
let matches = try_parse_command_line_matches(command.iter().map(|s| s.to_string()).collect()).unwrap(); let matches = try_parse_command_line_matches(command.iter().map(|s| s.to_string()).collect()).unwrap();
let (wait, restart, package, exe_args) = get_apply_args(matches.subcommand_matches("apply").unwrap()); let (wait, restart, package, exe_args) = get_apply_args(matches.subcommand_matches("apply").unwrap());
assert_eq!(wait, OperationWait::NoWait); assert_eq!(wait, OperationWait::NoWait);
assert_eq!(restart, true); assert_eq!(restart, true);
assert_eq!(package, Some(&PathBuf::from("C:\\Some Path\\With = Spaces\\Package.zip"))); assert_eq!(package, Some(&PathBuf::from("C:\\Some Path\\With = Spaces\\Package.zip")));

View File

@@ -179,13 +179,6 @@ typedef void (*vpkc_progress_callback_t)(void *p_user_data, size_t progress);
*/ */
typedef void (*vpkc_hook_callback_t)(void *p_user_data, const char *psz_app_version); typedef void (*vpkc_hook_callback_t)(void *p_user_data, const char *psz_app_version);
/**
* Log callback function.
*/
typedef void (*vpkc_log_callback_t)(void *p_user_data,
const char *psz_level,
const char *psz_message);
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif // __cplusplus #endif // __cplusplus
@@ -399,12 +392,6 @@ void vpkc_app_set_hook_restarted(vpkc_hook_callback_t cb_restarted);
*/ */
size_t vpkc_get_last_error(char *psz_error, size_t c_error); size_t vpkc_get_last_error(char *psz_error, size_t c_error);
/**
* Set a custom log callback. This will be called for all log messages generated by the Velopack library.
*/
void vpkc_set_logger(vpkc_log_callback_t cb_log,
void *p_user_data);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif // __cplusplus #endif // __cplusplus

View File

@@ -16,6 +16,8 @@ use libc::{c_char, c_void, size_t};
use log_derive::{logfn, logfn_inputs}; use log_derive::{logfn, logfn_inputs};
use std::{ffi::CString, ptr}; use std::{ffi::CString, ptr};
use velopack::{sources, ApplyWaitMode, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp}; use velopack::{sources, ApplyWaitMode, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp};
use velopack::locator::{LocationContext};
use velopack::logging::{default_logfile_from_config, default_logfile_from_context, init_logging};
/// Create a new FileSource update source for a given file path. /// Create a new FileSource update source for a given file path.
#[no_mangle] #[no_mangle]
@@ -437,7 +439,15 @@ pub extern "C" fn vpkc_app_run(p_user_data: *mut c_void) {
hook(p_user_data, c_string.as_ptr()); hook(p_user_data, c_string.as_ptr());
}); });
} }
// init logging
let log_file = if let Some(locator) = &app_options.locator {
default_logfile_from_config(locator)
} else {
default_logfile_from_context(LocationContext::FromCurrentExe)
};
init_logging("lib-cpp", Some(&log_file), false, false);
app.run(); app.run();
} }
@@ -530,10 +540,4 @@ pub extern "C" fn vpkc_app_set_hook_restarted(cb_restarted: vpkc_hook_callback_t
pub extern "C" fn vpkc_get_last_error(psz_error: *mut c_char, c_error: size_t) -> size_t { pub extern "C" fn vpkc_get_last_error(psz_error: *mut c_char, c_error: size_t) -> size_t {
let error = get_last_error(); let error = get_last_error();
return_cstr(psz_error, c_error, &error) return_cstr(psz_error, c_error, &error)
} }
/// Set a custom log callback. This will be called for all log messages generated by the Velopack library.
#[no_mangle]
pub extern "C" fn vpkc_set_logger(cb_log: vpkc_log_callback_t, p_user_data: *mut c_void) {
set_log_callback(cb_log, p_user_data);
}

View File

@@ -1,7 +1,5 @@
use anyhow::Result; use anyhow::Result;
use log::{Level, Log, Metadata, Record}; use std::sync::RwLock;
use std::ffi::{c_void, CString};
use std::sync::{Mutex, RwLock};
use velopack::locator::VelopackLocatorConfig; use velopack::locator::VelopackLocatorConfig;
use crate::types::*; use crate::types::*;
@@ -21,7 +19,6 @@ pub struct AppOptions {
lazy_static::lazy_static! { lazy_static::lazy_static! {
static ref LAST_ERROR: RwLock<String> = RwLock::new(String::new()); static ref LAST_ERROR: RwLock<String> = RwLock::new(String::new());
static ref LOG_CALLBACK: Mutex<(vpkc_log_callback_t, usize)> = Mutex::new((None, 0));
pub static ref VELOPACK_APP: RwLock<AppOptions> = RwLock::new(Default::default()); pub static ref VELOPACK_APP: RwLock<AppOptions> = RwLock::new(Default::default());
} }
@@ -64,53 +61,3 @@ where
} }
} }
} }
pub fn set_log_callback(callback: vpkc_log_callback_t, user_data: *mut c_void) {
// Initialize the logger if it hasn't been set yet
let _ = log::set_logger(&LOGGER);
log::set_max_level(log::LevelFilter::Trace);
let mut log_callback = LOG_CALLBACK.lock().unwrap();
*log_callback = (callback, user_data as usize);
}
pub fn log_message(level: &str, message: &str) {
let log_callback = LOG_CALLBACK.lock().unwrap();
let (callback, user_data) = *log_callback;
if let Some(callback) = callback {
let c_level = CString::new(level).unwrap();
let c_message = CString::new(message).unwrap();
callback(user_data as *mut c_void, c_level.as_ptr(), c_message.as_ptr());
}
}
struct LoggerImpl {}
static LOGGER: LoggerImpl = LoggerImpl {};
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",
}
.to_string();
log_message(&level, &text);
}
fn flush(&self) {}
}

View File

@@ -22,9 +22,6 @@ pub type vpkc_update_source_t = c_void;
/// Progress callback function. /// Progress callback function.
pub type vpkc_progress_callback_t = Option<extern "C" fn(p_user_data: *mut c_void, progress: size_t)>; pub type vpkc_progress_callback_t = Option<extern "C" fn(p_user_data: *mut c_void, progress: size_t)>;
/// Log callback function.
pub type vpkc_log_callback_t = Option<extern "C" fn(p_user_data: *mut c_void, psz_level: *const c_char, psz_message: *const c_char)>;
/// VelopackApp startup hook callback function. /// VelopackApp startup hook callback function.
pub type vpkc_hook_callback_t = Option<extern "C" fn(p_user_data: *mut c_void, psz_app_version: *const c_char)>; pub type vpkc_hook_callback_t = Option<extern "C" fn(p_user_data: *mut c_void, psz_app_version: *const c_char)>;

View File

@@ -47,10 +47,6 @@ declare module "./load" {
locator: string | null, locator: string | null,
autoApply: boolean, autoApply: boolean,
): void; ): void;
function js_set_logger_callback(
cb: (loglevel: LogLevel, msg: string) => void,
): void;
} }
type VelopackHookType = type VelopackHookType =
@@ -63,8 +59,6 @@ type VelopackHookType =
type VelopackHook = (version: string) => void; type VelopackHook = (version: string) => void;
type LogLevel = "info" | "warn" | "error" | "debug" | "trace";
/** /**
* VelopackApp helps you to handle app activation events correctly. * VelopackApp helps you to handle app activation events correctly.
* This should be used as early as possible in your application startup code. * This should be used as early as possible in your application startup code.
@@ -300,13 +294,4 @@ export class UpdateManager {
restartArgs, restartArgs,
); );
} }
} }
/**
* Set a custom logger callback to receive log messages from Velopack. The default behavior is to log to console.log.
*/
export function setVelopackLogger(
callback: (loglevel: LogLevel, msg: string) => void,
) {
addon.js_set_logger_callback(callback);
}

View File

@@ -7,8 +7,7 @@ use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use velopack::sources::*; use velopack::sources::*;
use velopack::*; use velopack::*;
use velopack::logging::{default_logfile_from_config, default_logfile_from_context};
mod logger;
struct UpdateManagerWrapper { struct UpdateManagerWrapper {
manager: UpdateManager, manager: UpdateManager,
@@ -262,30 +261,29 @@ fn js_appbuilder_run(mut cx: FunctionContext) -> JsResult<JsUndefined> {
.on_after_update_fast_callback(|semver| hook_handler("after-update", semver)); .on_after_update_fast_callback(|semver| hook_handler("after-update", semver));
} }
if let Some(locator) = locator { if let Some(locator) = &locator {
builder = builder.set_locator(locator); builder = builder.set_locator(locator.clone());
} }
if let Some(arg_array) = argarray { if let Some(arg_array) = argarray {
builder = builder.set_args(arg_array); builder = builder.set_args(arg_array);
} }
// init logging
let log_file = if let Some(locator) = &locator {
default_logfile_from_config(locator)
} else {
default_logfile_from_context(LocationContext::FromCurrentExe)
};
logging::init_logging("lib-nodejs", Some(&log_file), false, false);
builder.run(); builder.run();
Ok(undefined) 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] #[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> { fn main(mut cx: ModuleContext) -> NeonResult<()> {
logger::init_logger(&mut cx);
cx.export_function("js_new_update_manager", js_new_update_manager)?; cx.export_function("js_new_update_manager", js_new_update_manager)?;
cx.export_function("js_get_current_version", js_get_current_version)?; cx.export_function("js_get_current_version", js_get_current_version)?;
cx.export_function("js_get_app_id", js_get_app_id)?; cx.export_function("js_get_app_id", js_get_app_id)?;
@@ -295,6 +293,5 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("js_download_update_async", js_download_update_async)?; 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_wait_exit_then_apply_update", js_wait_exit_then_apply_update)?;
cx.export_function("js_appbuilder_run", js_appbuilder_run)?; cx.export_function("js_appbuilder_run", js_appbuilder_run)?;
cx.export_function("js_set_logger_callback", js_set_logger_callback)?;
Ok(()) Ok(())
} }

View File

@@ -1,98 +0,0 @@
use std::sync::{Arc, Mutex};
use lazy_static::lazy_static;
use log::{Level, Log, Metadata, Record};
use neon::{event::Channel, prelude::*};
static LOGGER: LoggerImpl = LoggerImpl {};
lazy_static! {
static ref LOGGER_CB: Arc<Mutex<Option<Root<JsFunction>>>> = Arc::new(Mutex::new(None));
static ref LOGGER_CONSOLE_LOG: 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 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 custom callback exists, then use that.
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(());
}
}
// If no custom callback, then use console.log
if let Ok(console) = LOGGER_CONSOLE_LOG.lock() {
if let Some(cb) = &*console {
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 init_logger(cx: &mut ModuleContext) {
let _ = log::set_logger(&LOGGER);
log::set_max_level(log::LevelFilter::Trace);
if let Ok(mut ch) = LOGGER_CONSOLE_LOG.lock() {
if let Ok(console) = cx.global::<JsObject>("console") {
if let Ok(log_fn) = console.get::<JsFunction, ModuleContext, &str>(cx, "log") {
*ch = Some(log_fn.root(cx));
}
}
}
if let Ok(mut ch) = LOGGER_CHANNEL.lock() {
// let mut log_channel = Channel::new(cx);
let mut log_channel = cx.channel();
log_channel.unref(cx); // Unref the channel so that the event loop can exit while this channel is open
*ch = Some(log_channel);
}
}
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;
}
}

View File

@@ -18,6 +18,7 @@ default = ["zstd"]
delta = ["zstd"] delta = ["zstd"]
async = ["async-std"] async = ["async-std"]
typescript = ["ts-rs"] typescript = ["ts-rs"]
file-logging = ["log-panics", "simplelog", "file-rotate", "time"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["async", "delta"] features = ["async", "delta"]
@@ -59,6 +60,12 @@ zstd = { workspace = true, optional = true }
# async # async
async-std = { workspace = true, optional = true } async-std = { workspace = true, optional = true }
# file logging
log-panics = { workspace = true, optional = true }
simplelog = { workspace = true, optional = true }
file-rotate = { workspace = true, optional = true }
time = { workspace = true, optional = true }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
windows = { workspace = true, features = ["Win32_Foundation", "Win32_Storage", "Win32_Storage_FileSystem", "Win32_System_IO"] } windows = { workspace = true, features = ["Win32_Foundation", "Win32_Storage", "Win32_Storage_FileSystem", "Win32_System_IO"] }

View File

@@ -103,6 +103,9 @@ pub mod delta;
/// Acquire and manage file-system based lock files. /// Acquire and manage file-system based lock files.
pub mod lockfile; pub mod lockfile;
/// Logging utilities and setup.
pub mod logging;
pub use app::*; pub use app::*;
pub use manager::*; pub use manager::*;

View File

@@ -18,37 +18,6 @@ pub fn default_channel_name() -> String {
return "osx".to_owned(); return "osx".to_owned();
} }
/// Default log location for Velopack on the current OS.
#[allow(unused_variables)]
pub fn default_log_location(context: LocationContext) -> PathBuf {
#[cfg(target_os = "windows")]
{
if let Ok(locator) = auto_locate_app_manifest(context) {
return locator.get_root_dir().join("Velopack.log");
}
warn!("Could not auto-locate app manifest, writing log to current directory.");
// If we can't locate the current app, we write to the current directory.
let mut my_exe = std::env::current_exe().expect("Could not locate current executable");
my_exe.pop();
return my_exe.join("Velopack.log");
}
#[cfg(target_os = "linux")]
{
return std::path::Path::new("/tmp/velopack.log").to_path_buf();
}
#[cfg(target_os = "macos")]
{
#[allow(deprecated)]
let mut user_home = std::env::home_dir().expect("Could not locate user home directory via $HOME or /etc/passwd");
user_home.push("Library");
user_home.push("Logs");
user_home.push("velopack.log");
return user_home;
}
}
bitflags::bitflags! { bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
/// ShortcutLocationFlags is a bitflags enumeration of system shortcut locations. /// ShortcutLocationFlags is a bitflags enumeration of system shortcut locations.
@@ -121,8 +90,22 @@ pub struct VelopackLocator {
} }
impl VelopackLocator { impl VelopackLocator {
/// Creates a new VelopackLocator from the given paths, trying to auto-detect the manifest.
pub fn new(config: &VelopackLocatorConfig) -> Result<VelopackLocator, Error>
{
if !config.UpdateExePath.exists() {
return Err(Error::MissingUpdateExe);
}
if !config.ManifestPath.exists() {
return Err(Error::MissingNuspec);
}
let manifest = read_current_manifest(&config.ManifestPath)?;
Ok(Self { paths: config.clone(), manifest })
}
/// Creates a new VelopackLocator from the given paths and manifest. /// Creates a new VelopackLocator from the given paths and manifest.
pub fn new(paths: VelopackLocatorConfig, manifest: Manifest) -> Self { pub fn new_with_manifest(paths: VelopackLocatorConfig, manifest: Manifest) -> Self {
Self { paths, manifest } Self { paths, manifest }
} }
@@ -333,19 +316,6 @@ pub fn create_config_from_root_dir<P: AsRef<std::path::Path>>(root_dir: P) -> Ve
} }
} }
fn config_to_locator(config: &VelopackLocatorConfig) -> Result<VelopackLocator, Error>
{
if !config.UpdateExePath.exists() {
return Err(Error::MissingUpdateExe);
}
if !config.ManifestPath.exists() {
return Err(Error::MissingNuspec);
}
let manifest = read_current_manifest(&config.ManifestPath)?;
Ok(VelopackLocator::new(config.clone(), manifest))
}
/// LocationContext is an enumeration of possible contexts for locating the current app manifest. /// LocationContext is an enumeration of possible contexts for locating the current app manifest.
pub enum LocationContext pub enum LocationContext
{ {
@@ -381,7 +351,7 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLoca
} }
LocationContext::FromSpecifiedRootDir(root_dir) => { LocationContext::FromSpecifiedRootDir(root_dir) => {
let config = create_config_from_root_dir(&root_dir); let config = create_config_from_root_dir(&root_dir);
let locator = config_to_locator(&config)?; let locator = VelopackLocator::new(&config)?;
return Ok(locator); return Ok(locator);
} }
LocationContext::FromSpecifiedAppExecutable(exe_path) => { LocationContext::FromSpecifiedAppExecutable(exe_path) => {
@@ -390,7 +360,7 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLoca
if parent_dir.join("Update.exe").exists() { if parent_dir.join("Update.exe").exists() {
info!("Found Update.exe in parent directory: {}", parent_dir.to_string_lossy()); info!("Found Update.exe in parent directory: {}", parent_dir.to_string_lossy());
let config = create_config_from_root_dir(&parent_dir); let config = create_config_from_root_dir(&parent_dir);
let locator = config_to_locator(&config)?; let locator = VelopackLocator::new(&config)?;
return Ok(locator); return Ok(locator);
} }
} }
@@ -404,7 +374,7 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLoca
if maybe_root.join("Update.exe").exists() { if maybe_root.join("Update.exe").exists() {
info!("Found Update.exe by current path pattern search in directory: {}", maybe_root.to_string_lossy()); info!("Found Update.exe by current path pattern search in directory: {}", maybe_root.to_string_lossy());
let config = create_config_from_root_dir(&maybe_root); let config = create_config_from_root_dir(&maybe_root);
let locator = config_to_locator(&config)?; let locator = VelopackLocator::new(&config)?;
return Ok(locator); return Ok(locator);
} }
} }
@@ -413,12 +383,12 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLoca
let exe_path = std::env::current_exe()?; let exe_path = std::env::current_exe()?;
if let Some(parent_dir) = exe_path.parent() { if let Some(parent_dir) = exe_path.parent() {
let config = create_config_from_root_dir(&parent_dir); let config = create_config_from_root_dir(&parent_dir);
let locator = config_to_locator(&config)?; let locator = VelopackLocator::new(&config)?;
return Ok(locator); return Ok(locator);
} }
} }
}; };
Err(Error::NotInstalled("Could not auto-locate app manifest".to_owned())) Err(Error::NotInstalled("Could not auto-locate app manifest".to_owned()))
} }
@@ -472,7 +442,7 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLoca
IsPortable: true, IsPortable: true,
}; };
config_to_locator(&config) Ok(VelopackLocator::new_with_manifest(config, app))
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@@ -520,8 +490,8 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result<VelopackLoca
CurrentBinaryDir: contents_dir, CurrentBinaryDir: contents_dir,
IsPortable: true, IsPortable: true,
}; };
config_to_locator(&config) Ok(VelopackLocator::new_with_manifest(config, app))
} }
fn read_current_manifest(nuspec_path: &PathBuf) -> Result<Manifest, Error> { fn read_current_manifest(nuspec_path: &PathBuf) -> Result<Manifest, Error> {
@@ -569,7 +539,7 @@ fn test_locator_staged_id_for_new_user() {
//Esure the packages directory exists //Esure the packages directory exists
assert!(std::fs::create_dir_all(&paths.PackagesDir).is_ok()); assert!(std::fs::create_dir_all(&paths.PackagesDir).is_ok());
let locator = VelopackLocator::new(paths, Manifest::default()); let locator = VelopackLocator::new_with_manifest(paths, Manifest::default());
let staged_user_id = locator.get_staged_user_id(); let staged_user_id = locator.get_staged_user_id();
@@ -596,7 +566,7 @@ fn test_locator_staged_id_for_existing_user() {
//Esure the packages directory exists //Esure the packages directory exists
assert!(std::fs::create_dir_all(&paths.PackagesDir).is_ok()); assert!(std::fs::create_dir_all(&paths.PackagesDir).is_ok());
let locator = VelopackLocator::new(paths, Manifest::default()); let locator = VelopackLocator::new_with_manifest(paths, Manifest::default());
let packages_dir = locator.get_packages_dir(); let packages_dir = locator.get_packages_dir();
let beta_id_path = packages_dir.join(".betaId"); let beta_id_path = packages_dir.join(".betaId");

162
src/lib-rust/src/logging.rs Normal file
View File

@@ -0,0 +1,162 @@
#![allow(unused_variables)]
use crate::locator::{LocationContext, VelopackLocator, VelopackLocatorConfig};
use std::path::PathBuf;
#[cfg(not(target_os = "windows"))]
use std::path::Path;
#[cfg(feature = "file-logging")]
use simplelog::*;
#[cfg(feature = "file-logging")]
use time::format_description::{modifier, Component, FormatItem};
#[cfg(target_os = "linux")]
fn default_file_linux() -> PathBuf {
Path::new("/tmp/velopack.log").to_path_buf()
}
#[cfg(target_os = "macos")]
fn default_file_macos() -> PathBuf {
#[allow(deprecated)]
let user_home = std::env::home_dir();
if let Some(home) = user_home {
let mut lib_logs = home.clone();
lib_logs.push("Library");
lib_logs.push("Logs");
if lib_logs.exists() {
lib_logs.push("velopack.log");
return lib_logs;
}
}
return Path::new("/tmp/velopack.log").to_path_buf();
}
/// Default log location for Velopack on the current OS.
#[allow(unused_variables)]
pub fn default_logfile_from_locator(locator: VelopackLocator) -> PathBuf {
#[cfg(target_os = "windows")]
{
return locator.get_root_dir().join("Velopack.log");
}
#[cfg(target_os = "linux")]
{
return default_file_linux();
}
#[cfg(target_os = "macos")]
{
return default_file_macos();
}
}
/// Default log location for Velopack on the current OS.
pub fn default_logfile_from_config(config: &VelopackLocatorConfig) -> PathBuf {
#[cfg(target_os = "windows")]
{
match VelopackLocator::new(config) {
Ok(locator) => {
return default_logfile_from_locator(locator);
}
Err(e) => warn!("Could not auto-locate app manifest, writing log to current directory. ({})", e),
}
// If we can't locate the current app, we write to the current directory.
let mut my_exe = std::env::current_exe().expect("Could not locate current executable");
my_exe.pop();
return my_exe.join("Velopack.log");
}
#[cfg(target_os = "linux")]
{
return default_file_linux();
}
#[cfg(target_os = "macos")]
{
return default_file_macos();
}
}
/// Default log location for Velopack on the current OS.
#[allow(unused_variables)]
pub fn default_logfile_from_context(context: LocationContext) -> PathBuf {
use crate::locator::{auto_locate_app_manifest};
#[cfg(target_os = "windows")]
{
match auto_locate_app_manifest(context) {
Ok(locator) => {
return default_logfile_from_locator(locator);
}
Err(e) => warn!("Could not auto-locate app manifest, writing log to current directory. ({})", e),
}
// If we can't locate the current app, we write to the current directory.
let mut my_exe = std::env::current_exe().expect("Could not locate current executable");
my_exe.pop();
return my_exe.join("Velopack.log");
}
#[cfg(target_os = "linux")]
{
return default_file_linux();
}
#[cfg(target_os = "macos")]
{
return default_file_macos();
}
}
/// Initialize logging for the current process. This will log optionally to a file and/or the console.
/// It can only be called once per process, and should be called early in the process lifecycle.
/// Future calls to this function will fail.
#[cfg(feature = "file-logging")]
pub fn init_logging(process_name: &str, file: Option<&PathBuf>, console: bool, verbose: bool) {
let mut loggers: Vec<Box<dyn SharedLogger>> = Vec::new();
let color_choice = ColorChoice::Never;
if console {
let console_level = if verbose { LevelFilter::Debug } else { LevelFilter::Info };
loggers.push(TermLogger::new(console_level, get_config(None), TerminalMode::Mixed, color_choice));
}
if let Some(f) = file {
let file_level = if verbose { LevelFilter::Trace } else { LevelFilter::Info };
let writer = file_rotate::FileRotate::new(
f.clone(),
file_rotate::suffix::AppendCount::new(1), // keep 1 old log file
file_rotate::ContentLimit::Bytes(1 * 1024 * 1024), // 1MB max log file size
file_rotate::compression::Compression::None,
None,
);
loggers.push(WriteLogger::new(file_level, get_config(Some(process_name)), writer));
}
if let Ok(()) = CombinedLogger::init(loggers) {
log_panics::init();
}
}
#[cfg(feature = "file-logging")]
fn get_config(process_name: Option<&str>) -> Config {
let mut c = ConfigBuilder::default();
let mut prefix = "".to_owned();
if let Some(pn) = process_name {
prefix = format!("[{}:{}] ", pn, std::process::id());
}
let prefix_heaped = Box::leak(prefix.into_boxed_str());
let time_format: &'static [FormatItem<'static>] = Box::leak(Box::new([
FormatItem::Literal(prefix_heaped.as_bytes()),
FormatItem::Literal(b"["),
FormatItem::Component(Component::Hour(modifier::Hour::default())),
FormatItem::Literal(b":"),
FormatItem::Component(Component::Minute(modifier::Minute::default())),
FormatItem::Literal(b":"),
FormatItem::Component(Component::Second(modifier::Second::default())),
FormatItem::Literal(b"]"),
]));
c.set_time_format_custom(time_format);
let _ = c.set_time_offset_to_local(); // might fail if local tz can't be determined
c.build()
}

View File

@@ -173,8 +173,7 @@ impl UpdateManager {
) -> Result<UpdateManager, Error> { ) -> Result<UpdateManager, Error> {
let locator = if let Some(config) = locator { let locator = if let Some(config) = locator {
warn!("Using explicit locator configuration, ignoring auto-locate."); warn!("Using explicit locator configuration, ignoring auto-locate.");
let manifest = config.load_manifest()?; VelopackLocator::new(&config)?
VelopackLocator::new(config.clone(), manifest)
} else { } else {
locator::auto_locate_app_manifest(LocationContext::FromCurrentExe)? locator::auto_locate_app_manifest(LocationContext::FromCurrentExe)?
}; };