Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions bin/florestad/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,20 @@ pub struct Cli {
/// Turn debugging information on
pub debug: bool,

#[arg(long)]
/// Option for saving log into data_Dir
#[arg(long, value_name = "FILE")]
/// Specify location of debug log file
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think debug log file is redundant. Just log file is fine

///
/// Specify location of debug log file (default: debug.log). Relative paths
/// will be prefixed by the net-specific datadir location.
/// Pass --nodebuglogfile to disable writing the log to a file.
pub debuglogfile: Option<String>,

#[arg(long, default_value_t = false)]
/// Disable writing the log to a file
///
/// if set, log will be saved into $DATA_DIR/debug.log.
pub log_to_file: bool,
/// When set, no log file will be written.
/// This overrides --debuglogfile.
pub nodebuglogfile: bool,

#[arg(long, value_name = "PATH")]
/// Where should we store data. This is the directory where we'll store the chainstate,
Expand Down
45 changes: 23 additions & 22 deletions bin/florestad/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
//!
//! This module configures [`tracing_subscriber`](https://docs.rs/tracing_subscriber) with up to two output layers:
//! - **stdout** – human-friendly, ANSI-coloured when attached to a real TTY.
//! - **file** – plain-text, appended to [`LOG_FILE`] inside `data_dir` via a
//! non-blocking writer.
//! - **file** – plain-text, appended to a caller-specified path (defaulting to
//! [`LOG_FILE`] inside the data directory) via a non-blocking writer.
//!
//! The active log level is controlled (in descending priority) by:
//! 1. The `RUST_LOG` environment variable.
Expand Down Expand Up @@ -222,12 +222,11 @@ where
///
/// # Arguments
///
/// * `data_dir` – Directory in which [`LOG_FILE`] is created when
/// `log_to_file` is `true`. The directory must already exist.
/// * `log_to_file` – Append structured log output to `<data_dir>/`[`LOG_FILE`].
/// * `log_file` – The absolute path to the log file. When `Some`, log output is
/// appended to this file via a non-blocking writer. When `None`, file logging
/// is disabled.
/// * `log_to_stdout` – Emit log output to stdout.
/// * `debug` – Set the default log level to `debug`. When `false` the
/// level defaults to `info`. In both cases `RUST_LOG` overrides the default.
/// * `log_level` – Set the default log level. `RUST_LOG` overrides this default.
///
/// # Returns
///
Expand All @@ -238,16 +237,15 @@ where
///
/// # Errors
///
/// Returns [`io::Error`] if `log_to_file` is `true` and [`LOG_FILE`] cannot be
/// created or opened for appending inside `data_dir`.
/// Returns [`io::Error`] if `log_file` is `Some` and the file cannot be
/// created or opened for appending.
///
/// # Panics
///
/// Panics if a global [`tracing`] subscriber has already been
/// installed (e.g. if this function is called more than once).
pub fn start_logger(
data_directory: &String,
log_to_file: bool,
log_file: Option<&str>,
log_to_stdout: bool,
log_level: Level,
) -> Result<Option<WorkerGuard>, io::Error> {
Expand All @@ -266,27 +264,30 @@ pub fn start_logger(
.with_filter(make_filter())
});

if log_to_file {
let file_path = format!("{}/{}", data_directory, LOG_FILE);

// Validate the log file path (`<data_directory>/<LOG_FILE>`).
if let Some(path) = log_file {
// Validate the log file path.
let _ = fs::OpenOptions::new()
.create(true)
.append(true)
.open(&file_path)
.open(path)
.map_err(|e| {
eprintln!(
"Failed to create log file at {}/{LOG_FILE}: {e}",
data_directory
);
eprintln!("Failed to create log file at {path}: {e}");
exit(1)
});
}

// Formatter for events destined to the log file.
let mut guard = None;
let fmt_layer_logfile = log_to_file.then(|| {
let file_appender = tracing_appender::rolling::never(data_directory, LOG_FILE);
let fmt_layer_logfile = log_file.map(|path| {
let log_path = std::path::Path::new(path);
let directory = log_path
.parent()
.unwrap_or_else(|| std::path::Path::new("."));
let file_name = log_path
.file_name()
.and_then(|f| f.to_str())
.unwrap_or(LOG_FILE);
let file_appender = tracing_appender::rolling::never(directory, file_name);
let (non_blocking, file_guard) = tracing_appender::non_blocking(file_appender);
guard = Some(file_guard);
layer()
Expand Down
100 changes: 94 additions & 6 deletions bin/florestad/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use tracing::Level;
#[cfg(unix)]
use crate::daemonize::Daemon;
use crate::logger::start_logger;
use crate::logger::LOG_FILE;

fn main() {
let params = Cli::parse();
Expand All @@ -56,6 +57,9 @@ fn main() {
exit(1);
});

let debug_log_file =
resolve_debug_log_file(params.nodebuglogfile, params.debuglogfile.as_deref(), &data_dir);

let config = Config {
data_dir,
disable_dns_seeds: params.connect.is_some() || params.disable_dns_seeds,
Expand All @@ -71,10 +75,7 @@ fn main() {
log_to_stdout: !params.daemon,
#[cfg(not(unix))]
log_to_stdout: true,
#[cfg(unix)]
log_to_file: params.log_to_file || params.daemon,
#[cfg(not(unix))]
log_to_file: params.log_to_file,
debug_log_file,
assume_valid: params.assume_valid,
#[cfg(feature = "zmq-server")]
zmq_address: params.zmq_address,
Expand Down Expand Up @@ -111,8 +112,7 @@ fn main() {

// The guard must stay alive until the end of `main` to flush file logs when dropped.
let _logger_guard = start_logger(
&config.data_dir,
config.log_to_file,
config.debug_log_file.as_deref(),
config.log_to_stdout,
log_level,
);
Expand Down Expand Up @@ -189,6 +189,33 @@ fn data_dir_path(dir: Option<String>, network: Network) -> String {
base.to_string_lossy().into_owned()
}

/// Resolves the debug log file path from the CLI flags.
///
/// File logging is enabled by default (matching Bitcoin Core). The user can:
/// - Override the path with `--debuglogfile=<path>`
/// - Disable file logging entirely with `--nodebuglogfile`
///
/// Relative paths are prefixed by the net-specific `data_dir`.
/// Returns `None` when file logging is disabled, or `Some(absolute_path)` otherwise.
fn resolve_debug_log_file(
no_debug_log_file: bool,
debug_log_file: Option<&str>,
data_dir: &str,
) -> Option<String> {
if no_debug_log_file {
return None;
}

let raw = debug_log_file.unwrap_or(LOG_FILE);
let path = PathBuf::from(raw);
let absolute = if path.is_absolute() {
path
} else {
PathBuf::from(data_dir).join(path)
};
Some(absolute.to_string_lossy().into_owned())
}

Comment on lines +192 to +218
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can live inside logger.rs

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -235,4 +262,65 @@ mod tests {
);
}
}

/// Default: no flags → file logging enabled at `<data_dir>/debug.log`.
#[test]
fn test_resolve_debug_log_file_default() {
let result = resolve_debug_log_file(false, None, "/home/user/.floresta");
let expected = PathBuf::from("/home/user/.floresta").join(LOG_FILE);
assert_eq!(result, Some(expected.to_string_lossy().into_owned()));
}

/// `--nodebuglogfile` → file logging disabled.
#[test]
fn test_resolve_debug_log_file_disabled() {
let result = resolve_debug_log_file(true, None, "/home/user/.floresta");
assert_eq!(result, None);
}

/// `--nodebuglogfile` overrides `--debuglogfile`.
#[test]
fn test_resolve_debug_log_file_disabled_overrides_custom() {
let result =
resolve_debug_log_file(true, Some("custom.log"), "/home/user/.floresta");
assert_eq!(result, None);
}

/// `--debuglogfile custom.log` (relative) → `<data_dir>/custom.log`.
#[test]
fn test_resolve_debug_log_file_relative_path() {
let result =
resolve_debug_log_file(false, Some("custom.log"), "/home/user/.floresta");
let expected = PathBuf::from("/home/user/.floresta").join("custom.log");
assert_eq!(result, Some(expected.to_string_lossy().into_owned()));
}

/// `--debuglogfile /tmp/floresta.log` (absolute) → used as-is.
#[test]
fn test_resolve_debug_log_file_absolute_path() {
let result = resolve_debug_log_file(
false,
Some("/tmp/floresta.log"),
"/home/user/.floresta",
);
assert_eq!(result, Some("/tmp/floresta.log".to_string()));
}

/// Relative path with subdirectory: `--debuglogfile logs/node.log`.
#[test]
fn test_resolve_debug_log_file_relative_subdir() {
let result =
resolve_debug_log_file(false, Some("logs/node.log"), "/home/user/.floresta");
let expected = PathBuf::from("/home/user/.floresta").join("logs/node.log");
assert_eq!(result, Some(expected.to_string_lossy().into_owned()));
}

/// Works correctly with a network-specific data directory (e.g. signet).
#[test]
fn test_resolve_debug_log_file_network_specific_datadir() {
let data_dir = data_dir_path(Some("/home/user/.floresta".into()), Network::Signet);
let result = resolve_debug_log_file(false, None, &data_dir);
let expected = PathBuf::from("/home/user/.floresta/signet").join(LOG_FILE);
assert_eq!(result, Some(expected.to_string_lossy().into_owned()));
}
}
14 changes: 10 additions & 4 deletions crates/floresta-node/src/florestad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,11 @@ pub struct Config {
/// Whether we should write logs to `stdout`.
pub log_to_stdout: bool,

/// Whether we should log to a fs file
pub log_to_file: bool,
/// The resolved absolute path to the debug log file, or `None` if file logging is disabled.
///
/// When set, log output is appended to this file. When `None`, no log file is written.
/// By default this is `<data_dir>/debug.log`.
pub debug_log_file: Option<String>,

/// Whether we should use assume utreexo
pub assume_utreexo: bool,
Expand Down Expand Up @@ -238,7 +241,7 @@ impl Config {
#[cfg(feature = "json-rpc")]
json_rpc_address: None,
log_to_stdout: false,
log_to_file: false,
debug_log_file: None,
assume_utreexo: false,
debug: false,
user_agent: String::new(),
Expand Down Expand Up @@ -477,7 +480,10 @@ impl Florestad {
.as_ref()
.map(|x| Self::resolve_hostname(x, 8332))
.transpose()?,
format!("{data_dir}/debug.log"),
self.config
.debug_log_file
.clone()
.unwrap_or_default(),
));

if self.json_rpc.set(server).is_err() {
Expand Down