diff --git a/README.md b/README.md index a4fecf9..1f4a1c1 100644 --- a/README.md +++ b/README.md @@ -78,8 +78,8 @@ Options: -r, --reset Reset stored values to default values. -n, --notification Toggle desktop notifications. Experimental. [possible values: on, off] --blink Toggle blink mode to animate a clock when it reaches its finished mode. [possible values: on, off] + --log [] Directory to store log file. If not set, standard application log directory is used (check README for details). -h, --help Print help - -V, --version Print version ``` Extra option (if `--features sound` is enabled by local build only): @@ -185,7 +185,9 @@ C:/Users/{user}/AppData/Local/timr-tui/data/app.data ## Logs -In `debug` mode only. Locations: +To get log output, start the app by passing `--log` to `timr-tui`. See [CLI](./#cli) for details. + +Logs will be stored in an `app.log` file at following locations: ```sh # Linux @@ -195,3 +197,5 @@ In `debug` mode only. Locations: # `Windows` C:/Users/{user}/AppData/Local/timr-tui/logs/app.log ``` + +Optional: You can use a custom directory by passing it via `--log` arg. diff --git a/src/args.rs b/src/args.rs index a78783c..dd5f860 100644 --- a/src/args.rs +++ b/src/args.rs @@ -5,10 +5,11 @@ use crate::{ #[cfg(feature = "sound")] use crate::{sound, sound::SoundError}; use clap::Parser; -#[cfg(feature = "sound")] use std::path::PathBuf; use std::time::Duration; +pub const LOG_DIRECTORY_DEFAULT_MISSING_VALUE: &str = " "; // empty string + #[derive(Parser)] #[command(version)] pub struct Args { @@ -66,6 +67,19 @@ pub struct Args { value_parser = sound_file_parser, )] pub sound: Option, + + #[arg( + long, + // allows both --log=path and --log path syntax + num_args = 0..=1, + // Note: If no value is passed, use a " " by default, + // this value will be checked later in `main` + // to use another (default) log directory instead + default_missing_value=LOG_DIRECTORY_DEFAULT_MISSING_VALUE, + help = "Directory to store log file. If not set, standard application log directory is used (check README for details).", + value_hint = clap::ValueHint::DirPath, + )] + pub log: Option, } #[cfg(feature = "sound")] diff --git a/src/config.rs b/src/config.rs index 064ec43..9434f3c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,16 +5,17 @@ use std::fs; use std::path::PathBuf; pub struct Config { - // silence `field `log_dir` is never read` the easy way - #[cfg_attr(not(debug_assertions), allow(dead_code))] pub log_dir: PathBuf, pub data_dir: PathBuf, } impl Config { pub fn init() -> Result { + // default logs dir let log_dir = get_default_state_dir()?.join("logs"); fs::create_dir_all(&log_dir)?; + + // default data dir let data_dir = get_default_state_dir()?.join("data"); fs::create_dir_all(&data_dir)?; diff --git a/src/logging.rs b/src/logging.rs index 80fb68b..0d8942c 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -1,4 +1,4 @@ -use color_eyre::eyre::Result; +use color_eyre::eyre::{eyre, Result}; use std::fs; use std::path::PathBuf; use tracing::level_filters::LevelFilter; @@ -17,7 +17,13 @@ impl Logger { pub fn init(&self) -> Result<()> { let log_path = self.log_dir.join("app.log"); - let log_file = fs::File::create(log_path)?; + let log_file = fs::File::create(log_path).map_err(|err| { + eyre!( + "Could not create a log file in {:?} : {}", + self.log_dir, + err + ) + })?; let fmt_layer = tracing_subscriber::fmt::layer() .with_file(true) .with_line_number(true) diff --git a/src/main.rs b/src/main.rs index 34fd765..e859b73 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ mod common; mod config; mod constants; mod events; -#[cfg(debug_assertions)] mod logging; mod args; @@ -17,21 +16,44 @@ mod widgets; mod sound; use app::{App, FromAppArgs}; -use args::Args; +use args::{Args, LOG_DIRECTORY_DEFAULT_MISSING_VALUE}; use clap::Parser; use color_eyre::Result; use config::Config; +use std::path::PathBuf; use storage::{AppStorage, Storage}; #[tokio::main] async fn main() -> Result<()> { + // init `Config` let cfg = Config::init()?; - #[cfg(debug_assertions)] - logging::Logger::new(cfg.log_dir).init()?; color_eyre::install()?; + // get args given by CLI let args = Args::parse(); + // Note: + // `log` arg can have three different values: + // (1) not set => None + // (2) set with path => Some(Some(path)) + // (3) set without path => Some(None) + let custom_log_dir: Option> = if let Some(path) = &args.log { + if path.ne(PathBuf::from(LOG_DIRECTORY_DEFAULT_MISSING_VALUE).as_os_str()) { + // (2) + Some(Some(path)) + } else { + // (3) + Some(None) + } + } else { + // (1) + None + }; + + if let Some(log_dir) = custom_log_dir { + let dir: PathBuf = log_dir.unwrap_or(&cfg.log_dir).to_path_buf(); + logging::Logger::new(dir).init()?; + } let mut terminal = terminal::setup()?; let events = events::Events::new();