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
+}