diff --git a/Cargo.lock b/Cargo.lock index 68632415..71199edd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2190,10 +2190,12 @@ dependencies = [ "async-std", "bitflags 2.9.0", "derivative", + "file-rotate", "glob", "lazy_static", "libc", "log", + "log-panics", "normpath", "rand", "regex", @@ -2202,8 +2204,10 @@ dependencies = [ "serde_json", "sha1", "sha2", + "simplelog", "tempfile", "thiserror 2.0.12", + "time 0.3.37", "ts-rs", "ureq", "url", diff --git a/Cargo.toml b/Cargo.toml index 96c7ec9c..d8a73801 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ edition = "2021" rust-version = "1.75" [workspace.dependencies] -velopack = { path = "src/lib-rust" } +velopack = { path = "src/lib-rust", features = ["file-logging"] } log = "0.4" log-derive = "0.4.1" ureq = "3.0" diff --git a/src/bins/src/commands/install.rs b/src/bins/src/commands/install.rs index 3fa45174..0bb0c3e7 100644 --- a/src/bins/src/commands/install.rs +++ b/src/bins/src/commands/install.rs @@ -116,7 +116,7 @@ pub fn install(pkg: &mut BundleZip, install_to: Option<&PathBuf>, start_args: Op info!("Acquiring lock..."); 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 tx = if dialogs::get_silent() { diff --git a/src/bins/src/commands/start_windows_impl.rs b/src/bins/src/commands/start_windows_impl.rs index d5fe9651..aad60dad 100644 --- a/src/bins/src/commands/start_windows_impl.rs +++ b/src/bins/src/commands/start_windows_impl.rs @@ -156,7 +156,7 @@ fn try_legacy_migration(root_dir: &PathBuf, manifest: &Manifest) -> Result, console: bool, verbose: bool) -> Result<()> { - let mut loggers: Vec> = 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() -} diff --git a/src/bins/src/setup.rs b/src/bins/src/setup.rs index 41a405a5..0d3febd6 100644 --- a/src/bins/src/setup.rs +++ b/src/bins/src/setup.rs @@ -79,7 +79,7 @@ fn main_inner() -> Result<()> { let verbose = matches.get_flag("verbose"); let logfile = matches.get_one::("log"); - logging::setup_logging("setup", logfile, true, verbose)?; + velopack::logging::init_logging("setup", logfile, true, verbose); let debug = matches.get_one::("debug"); let install_to = matches.get_one::("installto"); diff --git a/src/bins/src/stub.rs b/src/bins/src/stub.rs index 57c684a9..7537fd01 100644 --- a/src/bins/src/stub.rs +++ b/src/bins/src/stub.rs @@ -1,8 +1,6 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![allow(dead_code)] -mod logging; - #[macro_use] extern crate log; @@ -21,7 +19,7 @@ fn main() -> ExitCode { 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!("Starting Velopack Stub (at {:?})", my_path); diff --git a/src/bins/src/update.rs b/src/bins/src/update.rs index 4dfd6425..e7911142 100644 --- a/src/bins/src/update.rs +++ b/src/bins/src/update.rs @@ -7,8 +7,9 @@ extern crate log; use anyhow::{anyhow, bail, Result}; use clap::{arg, value_parser, ArgMatches, Command}; use std::{env, path::PathBuf}; -use velopack::locator::{self, auto_locate_app_manifest, LocationContext}; -use velopack_bins::{*, shared::OperationWait}; +use velopack::locator::{auto_locate_app_manifest, LocationContext}; +use velopack::logging::*; +use velopack_bins::{shared::OperationWait, *}; #[rustfmt::skip] fn root_command() -> Command { @@ -132,8 +133,8 @@ fn main() -> Result<()> { let verbose = get_flag_or_false(&matches, "verbose"); let log_file = matches.get_one("log"); - let desired_log_file = log_file.cloned().unwrap_or(locator::default_log_location(LocationContext::IAmUpdateExe)); - logging::setup_logging("update", Some(&desired_log_file), true, verbose)?; + let desired_log_file = log_file.cloned().unwrap_or(default_logfile_from_context(LocationContext::IAmUpdateExe)); + init_logging("update", Some(&desired_log_file), true, verbose); // change working directory to the parent directory of the exe let mut containing_dir = env::current_exe()?; @@ -212,7 +213,6 @@ fn apply(matches: &ArgMatches) -> Result<()> { Ok(()) } - fn get_start_args(matches: &ArgMatches) -> (OperationWait, Option<&String>, Option<&String>, Option>) { let legacy_args = matches.get_one::("args"); let exe_name = matches.get_one::("EXE_NAME"); @@ -246,10 +246,10 @@ fn uninstall(_matches: &ArgMatches) -> Result<()> { #[cfg(target_os = "windows")] #[test] 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 (wait, restart, package, exe_args) = get_apply_args(matches.subcommand_matches("apply").unwrap()); - + assert_eq!(wait, OperationWait::NoWait); assert_eq!(restart, true); assert_eq!(package, Some(&PathBuf::from("C:\\Some Path\\With = Spaces\\Package.zip"))); diff --git a/src/lib-cpp/include/Velopack.h b/src/lib-cpp/include/Velopack.h index 94910a05..3c44efb5 100644 --- a/src/lib-cpp/include/Velopack.h +++ b/src/lib-cpp/include/Velopack.h @@ -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); -/** - * Log callback function. - */ -typedef void (*vpkc_log_callback_t)(void *p_user_data, - const char *psz_level, - const char *psz_message); - #ifdef __cplusplus extern "C" { #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); -/** - * 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 } // extern "C" #endif // __cplusplus diff --git a/src/lib-cpp/src/lib.rs b/src/lib-cpp/src/lib.rs index be3c756c..3af48c3f 100644 --- a/src/lib-cpp/src/lib.rs +++ b/src/lib-cpp/src/lib.rs @@ -16,6 +16,8 @@ use libc::{c_char, c_void, size_t}; use log_derive::{logfn, logfn_inputs}; use std::{ffi::CString, ptr}; 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. #[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()); }); } - + + // 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(); } @@ -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 { let error = get_last_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); -} +} \ No newline at end of file diff --git a/src/lib-cpp/src/statics.rs b/src/lib-cpp/src/statics.rs index 4cba48ce..a5cbab47 100644 --- a/src/lib-cpp/src/statics.rs +++ b/src/lib-cpp/src/statics.rs @@ -1,7 +1,5 @@ use anyhow::Result; -use log::{Level, Log, Metadata, Record}; -use std::ffi::{c_void, CString}; -use std::sync::{Mutex, RwLock}; +use std::sync::RwLock; use velopack::locator::VelopackLocatorConfig; use crate::types::*; @@ -21,7 +19,6 @@ pub struct AppOptions { lazy_static::lazy_static! { static ref LAST_ERROR: RwLock = RwLock::new(String::new()); - static ref LOG_CALLBACK: Mutex<(vpkc_log_callback_t, usize)> = Mutex::new((None, 0)); pub static ref VELOPACK_APP: RwLock = 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) {} -} diff --git a/src/lib-cpp/src/types.rs b/src/lib-cpp/src/types.rs index 7a0f7584..151b52bd 100644 --- a/src/lib-cpp/src/types.rs +++ b/src/lib-cpp/src/types.rs @@ -22,9 +22,6 @@ pub type vpkc_update_source_t = c_void; /// Progress callback function. pub type vpkc_progress_callback_t = Option; -/// Log callback function. -pub type vpkc_log_callback_t = Option; - /// VelopackApp startup hook callback function. pub type vpkc_hook_callback_t = Option; diff --git a/src/lib-nodejs/src/index.ts b/src/lib-nodejs/src/index.ts index b50c55db..6fbe569e 100644 --- a/src/lib-nodejs/src/index.ts +++ b/src/lib-nodejs/src/index.ts @@ -47,10 +47,6 @@ declare module "./load" { locator: string | null, autoApply: boolean, ): void; - - function js_set_logger_callback( - cb: (loglevel: LogLevel, msg: string) => void, - ): void; } type VelopackHookType = @@ -63,8 +59,6 @@ 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. @@ -300,13 +294,4 @@ export class UpdateManager { 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); -} +} \ No newline at end of file diff --git a/src/lib-nodejs/velopack_nodeffi/src/lib.rs b/src/lib-nodejs/velopack_nodeffi/src/lib.rs index 3abed6fc..96479539 100644 --- a/src/lib-nodejs/velopack_nodeffi/src/lib.rs +++ b/src/lib-nodejs/velopack_nodeffi/src/lib.rs @@ -7,8 +7,7 @@ use std::sync::{Arc, Mutex}; use std::thread; use velopack::sources::*; use velopack::*; - -mod logger; +use velopack::logging::{default_logfile_from_config, default_logfile_from_context}; struct UpdateManagerWrapper { manager: UpdateManager, @@ -262,30 +261,29 @@ fn js_appbuilder_run(mut cx: FunctionContext) -> JsResult { .on_after_update_fast_callback(|semver| hook_handler("after-update", semver)); } - if let Some(locator) = locator { - builder = builder.set_locator(locator); + if let Some(locator) = &locator { + builder = builder.set_locator(locator.clone()); } if let Some(arg_array) = argarray { 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(); Ok(undefined) } -fn js_set_logger_callback(mut cx: FunctionContext) -> JsResult { - let arg_cb = cx.argument::(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<()> { - logger::init_logger(&mut cx); - 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_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_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(()) } diff --git a/src/lib-nodejs/velopack_nodeffi/src/logger.rs b/src/lib-nodejs/velopack_nodeffi/src/logger.rs deleted file mode 100644 index 9c5af878..00000000 --- a/src/lib-nodejs/velopack_nodeffi/src/logger.rs +++ /dev/null @@ -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>>> = Arc::new(Mutex::new(None)); - static ref LOGGER_CONSOLE_LOG: Arc>>> = Arc::new(Mutex::new(None)); - static ref LOGGER_CHANNEL: Arc>> = 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::("console") { - if let Ok(log_fn) = console.get::(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>, 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; - } -} diff --git a/src/lib-rust/Cargo.toml b/src/lib-rust/Cargo.toml index b608a332..158f1752 100644 --- a/src/lib-rust/Cargo.toml +++ b/src/lib-rust/Cargo.toml @@ -18,6 +18,7 @@ default = ["zstd"] delta = ["zstd"] async = ["async-std"] typescript = ["ts-rs"] +file-logging = ["log-panics", "simplelog", "file-rotate", "time"] [package.metadata.docs.rs] features = ["async", "delta"] @@ -59,6 +60,12 @@ zstd = { workspace = true, optional = true } # async 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] windows = { workspace = true, features = ["Win32_Foundation", "Win32_Storage", "Win32_Storage_FileSystem", "Win32_System_IO"] } diff --git a/src/lib-rust/src/lib.rs b/src/lib-rust/src/lib.rs index 77555f19..e2bf7580 100644 --- a/src/lib-rust/src/lib.rs +++ b/src/lib-rust/src/lib.rs @@ -103,6 +103,9 @@ pub mod delta; /// Acquire and manage file-system based lock files. pub mod lockfile; +/// Logging utilities and setup. +pub mod logging; + pub use app::*; pub use manager::*; diff --git a/src/lib-rust/src/locator.rs b/src/lib-rust/src/locator.rs index 9df5d18d..35d36122 100644 --- a/src/lib-rust/src/locator.rs +++ b/src/lib-rust/src/locator.rs @@ -18,37 +18,6 @@ pub fn default_channel_name() -> String { 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! { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] /// ShortcutLocationFlags is a bitflags enumeration of system shortcut locations. @@ -121,8 +90,22 @@ pub struct VelopackLocator { } impl VelopackLocator { + /// Creates a new VelopackLocator from the given paths, trying to auto-detect the manifest. + pub fn new(config: &VelopackLocatorConfig) -> Result + { + 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. - pub fn new(paths: VelopackLocatorConfig, manifest: Manifest) -> Self { + pub fn new_with_manifest(paths: VelopackLocatorConfig, manifest: Manifest) -> Self { Self { paths, manifest } } @@ -333,19 +316,6 @@ pub fn create_config_from_root_dir>(root_dir: P) -> Ve } } -fn config_to_locator(config: &VelopackLocatorConfig) -> Result -{ - 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. pub enum LocationContext { @@ -381,7 +351,7 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result { let config = create_config_from_root_dir(&root_dir); - let locator = config_to_locator(&config)?; + let locator = VelopackLocator::new(&config)?; return Ok(locator); } LocationContext::FromSpecifiedAppExecutable(exe_path) => { @@ -390,7 +360,7 @@ pub fn auto_locate_app_manifest(context: LocationContext) -> Result Result Result Result Result Result { @@ -569,7 +539,7 @@ fn test_locator_staged_id_for_new_user() { //Esure the packages directory exists 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(); @@ -596,7 +566,7 @@ fn test_locator_staged_id_for_existing_user() { //Esure the packages directory exists 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 beta_id_path = packages_dir.join(".betaId"); diff --git a/src/lib-rust/src/logging.rs b/src/lib-rust/src/logging.rs new file mode 100644 index 00000000..9282efd7 --- /dev/null +++ b/src/lib-rust/src/logging.rs @@ -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> = 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() +} diff --git a/src/lib-rust/src/manager.rs b/src/lib-rust/src/manager.rs index 1c25d1f4..30b007ed 100644 --- a/src/lib-rust/src/manager.rs +++ b/src/lib-rust/src/manager.rs @@ -173,8 +173,7 @@ impl UpdateManager { ) -> Result { let locator = if let Some(config) = locator { warn!("Using explicit locator configuration, ignoring auto-locate."); - let manifest = config.load_manifest()?; - VelopackLocator::new(config.clone(), manifest) + VelopackLocator::new(&config)? } else { locator::auto_locate_app_manifest(LocationContext::FromCurrentExe)? };