From 0831d198410ab60f85f67ae0f1437ff9ca37d8b9 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sun, 16 May 2021 15:45:05 +0200 Subject: [PATCH] feat(core): improve logging handling and create daemon skeleton --- espanso/src/cli/daemon/mod.rs | 43 +++++++++++++++++++++++++++++ espanso/src/cli/mod.rs | 10 +++++++ espanso/src/cli/worker/mod.rs | 1 + espanso/src/logging/mod.rs | 51 ++++++++++++++++++++++++----------- espanso/src/main.rs | 41 +++++++++++++++++++--------- 5 files changed, 117 insertions(+), 29 deletions(-) create mode 100644 espanso/src/cli/daemon/mod.rs diff --git a/espanso/src/cli/daemon/mod.rs b/espanso/src/cli/daemon/mod.rs new file mode 100644 index 0000000..a38a7d0 --- /dev/null +++ b/espanso/src/cli/daemon/mod.rs @@ -0,0 +1,43 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use log::info; + +use super::{CliModule, CliModuleArgs}; + +pub fn new() -> CliModule { + #[allow(clippy::needless_update)] + CliModule { + requires_paths: true, + requires_config: true, + enable_logs: true, + log_mode: super::LogMode::Write, + subcommand: "daemon".to_string(), + entry: daemon_main, + ..Default::default() + } +} + +const VERSION: &str = env!("CARGO_PKG_VERSION"); + +fn daemon_main(args: CliModuleArgs) { + let paths = args.paths.expect("missing paths in worker main"); + + info!("espanso version: {}", VERSION); +} diff --git a/espanso/src/cli/mod.rs b/espanso/src/cli/mod.rs index fa68778..4c0af1f 100644 --- a/espanso/src/cli/mod.rs +++ b/espanso/src/cli/mod.rs @@ -21,12 +21,14 @@ use clap::ArgMatches; use espanso_config::{config::ConfigStore, matches::store::MatchStore}; use espanso_path::Paths; +pub mod daemon; pub mod log; pub mod path; pub mod worker; pub struct CliModule { pub enable_logs: bool, + pub log_mode: LogMode, pub requires_paths: bool, pub requires_config: bool, pub subcommand: String, @@ -37,6 +39,7 @@ impl Default for CliModule { fn default() -> Self { Self { enable_logs: false, + log_mode: LogMode::Read, requires_paths: false, requires_config: false, subcommand: "".to_string(), @@ -45,6 +48,13 @@ impl Default for CliModule { } } +#[derive(Debug, PartialEq)] +pub enum LogMode { + Read, + Write, + Append, +} + pub struct CliModuleArgs { pub config_store: Option>, pub match_store: Option>, diff --git a/espanso/src/cli/worker/mod.rs b/espanso/src/cli/worker/mod.rs index 561b84f..8ad11d0 100644 --- a/espanso/src/cli/worker/mod.rs +++ b/espanso/src/cli/worker/mod.rs @@ -31,6 +31,7 @@ pub fn new() -> CliModule { requires_paths: true, requires_config: true, enable_logs: true, + log_mode: super::LogMode::Append, subcommand: "worker".to_string(), entry: worker_main, ..Default::default() diff --git a/espanso/src/logging/mod.rs b/espanso/src/logging/mod.rs index ff3c52d..05f5bb2 100644 --- a/espanso/src/logging/mod.rs +++ b/espanso/src/logging/mod.rs @@ -31,25 +31,37 @@ use std::{ /// decision of the output file #[derive(Clone)] pub(crate) struct FileProxy { - output: Arc>>, + output: Arc>, +} + +enum Output { + Memory(Vec), + File(File), } impl FileProxy { pub fn new() -> Self { Self { - output: Arc::new(Mutex::new(None)), + output: Arc::new(Mutex::new(Output::Memory(Vec::new()))), } } - pub fn set_output_file(&self, path: &Path) -> Result<()> { - let log_file = OpenOptions::new() + pub fn set_output_file(&self, path: &Path, truncate: bool, append: bool) -> Result<()> { + let mut log_file = OpenOptions::new() .read(true) .write(true) .create(true) - .append(true) + .truncate(truncate) + .append(append) .open(path)?; let mut lock = self.output.lock().expect("unable to obtain FileProxy lock"); - *lock = Some(log_file); + + // Transfer the log content that has been buffered into the file + if let Output::Memory(buffered) = &mut (*lock) { + log_file.write_all(&buffered)?; + buffered.clear(); + } + *lock = Output::File(log_file); Ok(()) } } @@ -57,11 +69,15 @@ impl FileProxy { impl Write for FileProxy { fn write(&mut self, buf: &[u8]) -> std::io::Result { match self.output.lock() { - Ok(lock) => { - if let Some(mut output) = lock.as_ref() { - output.write(buf) - } else { - Ok(0) + Ok(mut lock) => { + match &mut (*lock) { + // Write to the memory buffer until a file is ready + Output::Memory(buffer) => { + buffer.write(buf) + } + Output::File(output) => { + output.write(buf) + } } } Err(_) => Err(std::io::Error::new( @@ -73,11 +89,14 @@ impl Write for FileProxy { fn flush(&mut self) -> std::io::Result<()> { match self.output.lock() { - Ok(lock) => { - if let Some(mut output) = lock.as_ref() { - output.flush() - } else { - Ok(()) + Ok(mut lock) => { + match &mut (*lock) { + Output::Memory(buffer) => { + buffer.flush() + } + Output::File(output) => { + output.flush() + } } } Err(_) => Err(std::io::Error::new( diff --git a/espanso/src/main.rs b/espanso/src/main.rs index e23fcf9..c210732 100644 --- a/espanso/src/main.rs +++ b/espanso/src/main.rs @@ -30,6 +30,8 @@ use simplelog::{ CombinedLogger, ConfigBuilder, LevelFilter, TermLogger, TerminalMode, WriteLogger, }; +use crate::cli::LogMode; + mod cli; mod engine; mod gui; @@ -40,8 +42,12 @@ const VERSION: &str = env!("CARGO_PKG_VERSION"); const LOG_FILE_NAME: &str = "espanso.log"; lazy_static! { - static ref CLI_HANDLERS: Vec = - vec![cli::path::new(), cli::log::new(), cli::worker::new(),]; + static ref CLI_HANDLERS: Vec = vec![ + cli::path::new(), + cli::log::new(), + cli::worker::new(), + cli::daemon::new() + ]; } fn main() { @@ -128,8 +134,9 @@ fn main() { // ) // .subcommand(SubCommand::with_name("detect") // .about("Tool to detect current window properties, to simplify filters creation.")) - // .subcommand(SubCommand::with_name("daemon") - // .about("Start the daemon without spawning a new process.")) + .subcommand(SubCommand::with_name("daemon") + .setting(AppSettings::Hidden) + .about("Start the daemon without spawning a new process.")) // .subcommand(SubCommand::with_name("register") // .about("MacOS and Linux only. Register espanso in the system daemon manager.")) // .subcommand(SubCommand::with_name("unregister") @@ -228,8 +235,7 @@ fn main() { let matches = clap_instance.clone().get_matches(); let log_level = match matches.occurrences_of("v") { - 0 => LevelFilter::Warn, - 1 => LevelFilter::Info, + 0 | 1 => LevelFilter::Info, // Trace mode is only available in debug mode for security reasons #[cfg(debug_assertions)] @@ -247,13 +253,14 @@ fn main() { if handler.enable_logs { let config = ConfigBuilder::new() .set_time_to_local(true) + .set_time_format(format!("%H:%M:%S [{}]", handler.subcommand)) .set_location_level(LevelFilter::Off) .add_filter_ignore_str("html5ever") .build(); CombinedLogger::init(vec![ TermLogger::new(log_level, config.clone(), TerminalMode::Mixed), - WriteLogger::new(log_level, config, log_proxy.clone()), + WriteLogger::new(LevelFilter::Info, config, log_proxy.clone()), ]) .expect("unable to initialize logs"); } @@ -264,8 +271,12 @@ fn main() { let force_config_path = get_path_override(&matches, "config_dir", "ESPANSO_CONFIG_DIR"); let force_package_path = get_path_override(&matches, "package_dir", "ESPANSO_PACKAGE_DIR"); let force_runtime_path = get_path_override(&matches, "runtime_dir", "ESPANSO_RUNTIME_DIR"); - - let paths = espanso_path::resolve_paths(force_config_path.as_deref(), force_package_path.as_deref(), force_runtime_path.as_deref()); + + let paths = espanso_path::resolve_paths( + force_config_path.as_deref(), + force_package_path.as_deref(), + force_runtime_path.as_deref(), + ); info!("reading configs from: {:?}", paths.config); info!("reading packages from: {:?}", paths.packages); @@ -291,7 +302,11 @@ fn main() { if handler.enable_logs { log_proxy - .set_output_file(&paths.runtime.join(LOG_FILE_NAME)) + .set_output_file( + &paths.runtime.join(LOG_FILE_NAME), + handler.log_mode == LogMode::Write, + handler.log_mode == LogMode::Append, + ) .expect("unable to set up log output file"); } @@ -315,7 +330,7 @@ fn get_path_override(matches: &ArgMatches, argument: &str, env_var: &str) -> Opt if let Some(path) = matches.value_of(argument) { let path = PathBuf::from(path.trim()); if path.is_dir() { - return Some(path) + return Some(path); } else { error!("{} argument was specified, but it doesn't point to a valid directory. Make sure to create it first.", argument); std::process::exit(1); @@ -323,7 +338,7 @@ fn get_path_override(matches: &ArgMatches, argument: &str, env_var: &str) -> Opt } else if let Ok(path) = std::env::var(env_var) { let path = PathBuf::from(path.trim()); if path.is_dir() { - return Some(path) + return Some(path); } else { error!("{} env variable was specified, but it doesn't point to a valid directory. Make sure to create it first.", env_var); std::process::exit(1); @@ -331,4 +346,4 @@ fn get_path_override(matches: &ArgMatches, argument: &str, env_var: &str) -> Opt } else { None } -} \ No newline at end of file +}