change(ui): Enable the progress bar feature by default, but only show progress bars when the config is enabled (#7615)

* Add a progress bar config that is disabled unless the feature is on

* Simplify the default config

* Enable the progress bar feature by default, but require the config

* Rename progress bars config to avoid merge conflicts

* Use a log file when the progress bar is activated

* Document how to configure progress bars

* Handle log files in config_tests and check config path

* Fix doc link

* Fix path check

* Fix config log matching

* Fix clippy warning

* Add tracing to config tests

* It's zebrad not zebra

* cargo fmt --all

* Update release for config file changes

* Fix config test failures

* Allow printing to stdout in a method
This commit is contained in:
teor 2023-10-12 10:25:37 +10:00 committed by GitHub
parent 0cffae5dd0
commit ae52e3d23d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 319 additions and 57 deletions

View File

@ -5779,6 +5779,7 @@ dependencies = [
"humantime", "humantime",
"indexmap 2.0.1", "indexmap 2.0.1",
"insta", "insta",
"itertools 0.11.0",
"lazy_static", "lazy_static",
"once_cell", "once_cell",
"owo-colors", "owo-colors",

View File

@ -11,7 +11,7 @@
- [Getting Started](#getting-started) - [Getting Started](#getting-started)
- [Docker](#docker) - [Docker](#docker)
- [Building Zebra](#building-zebra) - [Building Zebra](#building-zebra)
- [Optional Features](#optional-features) - [Optional Configs & Features](#optional-features)
- [Known Issues](#known-issues) - [Known Issues](#known-issues)
- [Future Work](#future-work) - [Future Work](#future-work)
- [Documentation](#documentation) - [Documentation](#documentation)
@ -116,13 +116,24 @@ zebrad start
See the [Installing Zebra](https://zebra.zfnd.org/user/install.html) and [Running Zebra](https://zebra.zfnd.org/user/run.html) See the [Installing Zebra](https://zebra.zfnd.org/user/install.html) and [Running Zebra](https://zebra.zfnd.org/user/run.html)
sections in the book for more details. sections in the book for more details.
#### Optional Features #### Optional Configs & Features
##### Configuring Progress Bars
Configure `tracing.progress_bar` in your `zebrad.toml` to
[show key metrics in the terminal using progress bars](https://zfnd.org/experimental-zebra-progress-bars/).
When progress bars are active, Zebra automatically sends logs to a file.
In future releases, the `progress_bar = "summary"` config will show a few key metrics,
and the "detailed" config will show all available metrics. Please let us know which metrics are
important to you!
##### Custom Build Features
You can also build Zebra with additional [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options): You can also build Zebra with additional [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options):
- `getblocktemplate-rpcs` for [mining support](https://zebra.zfnd.org/user/mining.html) - `getblocktemplate-rpcs` for [mining support](https://zebra.zfnd.org/user/mining.html)
- `prometheus` for [Prometheus metrics](https://zebra.zfnd.org/user/metrics.html) - `prometheus` for [Prometheus metrics](https://zebra.zfnd.org/user/metrics.html)
- `progress-bar` [experimental progress bars](https://zfnd.org/experimental-zebra-progress-bars/)
- `sentry` for [Sentry monitoring](https://zebra.zfnd.org/user/tracing.html#sentry-production-monitoring) - `sentry` for [Sentry monitoring](https://zebra.zfnd.org/user/tracing.html#sentry-production-monitoring)
- `elasticsearch` for [experimental Elasticsearch support](https://zebra.zfnd.org/user/elasticsearch.html) - `elasticsearch` for [experimental Elasticsearch support](https://zebra.zfnd.org/user/elasticsearch.html)

View File

@ -19,6 +19,7 @@ hex = "0.4.3"
indexmap = "2.0.1" indexmap = "2.0.1"
lazy_static = "1.4.0" lazy_static = "1.4.0"
insta = "1.33.0" insta = "1.33.0"
itertools = "0.11.0"
proptest = "1.3.1" proptest = "1.3.1"
once_cell = "1.18.0" once_cell = "1.18.0"
rand = "0.8.5" rand = "0.8.5"

View File

@ -21,10 +21,11 @@ use tracing::instrument;
#[macro_use] #[macro_use]
mod arguments; mod arguments;
pub mod to_regex; pub mod to_regex;
pub use self::arguments::Arguments; pub use self::arguments::Arguments;
use self::to_regex::{CollectRegexSet, ToRegex, ToRegexSet}; use self::to_regex::{CollectRegexSet, ToRegexSet};
/// A super-trait for [`Iterator`] + [`Debug`]. /// A super-trait for [`Iterator`] + [`Debug`].
pub trait IteratorDebug: Iterator + Debug {} pub trait IteratorDebug: Iterator + Debug {}
@ -791,7 +792,7 @@ impl<T> TestChild<T> {
#[allow(clippy::unwrap_in_result)] #[allow(clippy::unwrap_in_result)]
pub fn expect_stdout_line_matches<R>(&mut self, success_regex: R) -> Result<String> pub fn expect_stdout_line_matches<R>(&mut self, success_regex: R) -> Result<String>
where where
R: ToRegex + Debug, R: ToRegexSet + Debug,
{ {
self.apply_failure_regexes_to_outputs(); self.apply_failure_regexes_to_outputs();
@ -823,7 +824,7 @@ impl<T> TestChild<T> {
#[allow(clippy::unwrap_in_result)] #[allow(clippy::unwrap_in_result)]
pub fn expect_stderr_line_matches<R>(&mut self, success_regex: R) -> Result<String> pub fn expect_stderr_line_matches<R>(&mut self, success_regex: R) -> Result<String>
where where
R: ToRegex + Debug, R: ToRegexSet + Debug,
{ {
self.apply_failure_regexes_to_outputs(); self.apply_failure_regexes_to_outputs();
@ -855,7 +856,7 @@ impl<T> TestChild<T> {
#[allow(clippy::unwrap_in_result)] #[allow(clippy::unwrap_in_result)]
pub fn expect_stdout_line_matches_silent<R>(&mut self, success_regex: R) -> Result<String> pub fn expect_stdout_line_matches_silent<R>(&mut self, success_regex: R) -> Result<String>
where where
R: ToRegex + Debug, R: ToRegexSet + Debug,
{ {
self.apply_failure_regexes_to_outputs(); self.apply_failure_regexes_to_outputs();
@ -887,7 +888,7 @@ impl<T> TestChild<T> {
#[allow(clippy::unwrap_in_result)] #[allow(clippy::unwrap_in_result)]
pub fn expect_stderr_line_matches_silent<R>(&mut self, success_regex: R) -> Result<String> pub fn expect_stderr_line_matches_silent<R>(&mut self, success_regex: R) -> Result<String>
where where
R: ToRegex + Debug, R: ToRegexSet + Debug,
{ {
self.apply_failure_regexes_to_outputs(); self.apply_failure_regexes_to_outputs();
@ -1246,9 +1247,9 @@ impl<T> TestOutput<T> {
#[allow(clippy::unwrap_in_result)] #[allow(clippy::unwrap_in_result)]
pub fn stdout_matches<R>(&self, regex: R) -> Result<&Self> pub fn stdout_matches<R>(&self, regex: R) -> Result<&Self>
where where
R: ToRegex + Debug, R: ToRegexSet + Debug,
{ {
let re = regex.to_regex().expect("regex must be valid"); let re = regex.to_regex_set().expect("regex must be valid");
self.output_check( self.output_check(
|stdout| re.is_match(stdout), |stdout| re.is_match(stdout),
@ -1270,9 +1271,9 @@ impl<T> TestOutput<T> {
#[allow(clippy::unwrap_in_result)] #[allow(clippy::unwrap_in_result)]
pub fn stdout_line_matches<R>(&self, regex: R) -> Result<&Self> pub fn stdout_line_matches<R>(&self, regex: R) -> Result<&Self>
where where
R: ToRegex + Debug, R: ToRegexSet + Debug,
{ {
let re = regex.to_regex().expect("regex must be valid"); let re = regex.to_regex_set().expect("regex must be valid");
self.any_output_line( self.any_output_line(
|line| re.is_match(line), |line| re.is_match(line),
@ -1300,9 +1301,9 @@ impl<T> TestOutput<T> {
#[allow(clippy::unwrap_in_result)] #[allow(clippy::unwrap_in_result)]
pub fn stderr_matches<R>(&self, regex: R) -> Result<&Self> pub fn stderr_matches<R>(&self, regex: R) -> Result<&Self>
where where
R: ToRegex + Debug, R: ToRegexSet + Debug,
{ {
let re = regex.to_regex().expect("regex must be valid"); let re = regex.to_regex_set().expect("regex must be valid");
self.output_check( self.output_check(
|stderr| re.is_match(stderr), |stderr| re.is_match(stderr),
@ -1324,9 +1325,9 @@ impl<T> TestOutput<T> {
#[allow(clippy::unwrap_in_result)] #[allow(clippy::unwrap_in_result)]
pub fn stderr_line_matches<R>(&self, regex: R) -> Result<&Self> pub fn stderr_line_matches<R>(&self, regex: R) -> Result<&Self>
where where
R: ToRegex + Debug, R: ToRegexSet + Debug,
{ {
let re = regex.to_regex().expect("regex must be valid"); let re = regex.to_regex_set().expect("regex must be valid");
self.any_output_line( self.any_output_line(
|line| re.is_match(line), |line| re.is_match(line),

View File

@ -2,6 +2,7 @@
use std::iter; use std::iter;
use itertools::Itertools;
use regex::{Error, Regex, RegexBuilder, RegexSet, RegexSetBuilder}; use regex::{Error, Regex, RegexBuilder, RegexSet, RegexSetBuilder};
/// A trait for converting a value to a [`Regex`]. /// A trait for converting a value to a [`Regex`].
@ -135,15 +136,17 @@ pub trait CollectRegexSet {
impl<I> CollectRegexSet for I impl<I> CollectRegexSet for I
where where
I: IntoIterator, I: IntoIterator,
I::Item: ToRegex, I::Item: ToRegexSet,
{ {
fn collect_regex_set(self) -> Result<RegexSet, Error> { fn collect_regex_set(self) -> Result<RegexSet, Error> {
let regexes: Result<Vec<Regex>, Error> = let regexes: Result<Vec<RegexSet>, Error> = self
self.into_iter().map(|item| item.to_regex()).collect(); .into_iter()
.map(|item| item.to_regex_set())
.try_collect();
let regexes = regexes?; let regexes = regexes?;
// This conversion discards flags and limits from Regex and RegexBuilder. // This conversion discards flags and limits from Regex and RegexBuilder.
let regexes = regexes.iter().map(|regex| regex.as_str()); let regexes = regexes.iter().flat_map(|regex_set| regex_set.patterns());
RegexSet::new(regexes) RegexSet::new(regexes)
} }

View File

@ -52,7 +52,7 @@ features = [
[features] [features]
# In release builds, don't compile debug logging code, to improve performance. # In release builds, don't compile debug logging code, to improve performance.
default = ["release_max_level_info"] default = ["release_max_level_info", "progress-bar"]
# Default features for official ZF binary release builds # Default features for official ZF binary release builds
default-release-binaries = ["default", "sentry"] default-release-binaries = ["default", "sentry"]

View File

@ -434,6 +434,7 @@ impl Application for ZebradApp {
// Override the default tracing filter based on the command-line verbosity. // Override the default tracing filter based on the command-line verbosity.
tracing_config.filter = tracing_config tracing_config.filter = tracing_config
.filter .filter
.clone()
.or_else(|| Some(default_filter.to_owned())); .or_else(|| Some(default_filter.to_owned()));
} else { } else {
// Don't apply the configured filter for short-lived commands. // Don't apply the configured filter for short-lived commands.

View File

@ -1,6 +1,10 @@
//! Tracing and logging infrastructure for Zebra. //! Tracing and logging infrastructure for Zebra.
use std::{net::SocketAddr, path::PathBuf}; use std::{
net::SocketAddr,
ops::{Deref, DerefMut},
path::PathBuf,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -16,10 +20,59 @@ pub use endpoint::TracingEndpoint;
#[cfg(feature = "flamegraph")] #[cfg(feature = "flamegraph")]
pub use flame::{layer, Grapher}; pub use flame::{layer, Grapher};
/// Tracing configuration section. /// Tracing configuration section: outer config after cross-field defaults are applied.
#[derive(Clone, Debug, Deserialize, Serialize)] ///
#[serde(deny_unknown_fields, default)] /// This is a wrapper type that dereferences to the inner config type.
///
//
// TODO: replace with serde's finalizer attribute when that feature is implemented.
// we currently use the recommended workaround of a wrapper struct with from/into attributes.
// https://github.com/serde-rs/serde/issues/642#issuecomment-525432907
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(
deny_unknown_fields,
default,
from = "InnerConfig",
into = "InnerConfig"
)]
pub struct Config { pub struct Config {
inner: InnerConfig,
}
impl Deref for Config {
type Target = InnerConfig;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for Config {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl From<InnerConfig> for Config {
fn from(mut inner: InnerConfig) -> Self {
inner.log_file = runtime_default_log_file(inner.log_file, inner.progress_bar);
Self { inner }
}
}
impl From<Config> for InnerConfig {
fn from(mut config: Config) -> Self {
config.log_file = disk_default_log_file(config.log_file.clone(), config.progress_bar);
config.inner
}
}
/// Tracing configuration section: inner config used to deserialize and apply cross-field defaults.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields, default)]
pub struct InnerConfig {
/// Whether to use colored terminal output, if available. /// Whether to use colored terminal output, if available.
/// ///
/// Colored terminal output is automatically disabled if an output stream /// Colored terminal output is automatically disabled if an output stream
@ -108,10 +161,16 @@ pub struct Config {
/// replaced with `.folded` and `.svg` for the respective files. /// replaced with `.folded` and `.svg` for the respective files.
pub flamegraph: Option<PathBuf>, pub flamegraph: Option<PathBuf>,
/// Shows progress bars for block syncing, and mempool transactions, and peer networking.
/// Also sends logs to the default log file path.
///
/// This config field is ignored unless the `progress-bar` feature is enabled.
pub progress_bar: Option<ProgressConfig>,
/// If set to a path, write the tracing logs to that path. /// If set to a path, write the tracing logs to that path.
/// ///
/// By default, logs are sent to the terminal standard output. /// By default, logs are sent to the terminal standard output.
/// But if the `progress-bar` feature is activated, logs are sent to the standard log file path: /// But if the `progress_bar` config is activated, logs are sent to the standard log file path:
/// - Linux: `$XDG_STATE_HOME/zebrad.log` or `$HOME/.local/state/zebrad.log` /// - Linux: `$XDG_STATE_HOME/zebrad.log` or `$HOME/.local/state/zebrad.log`
/// - macOS: `$HOME/Library/Application Support/zebrad.log` /// - macOS: `$HOME/Library/Application Support/zebrad.log`
/// - Windows: `%LOCALAPPDATA%\zebrad.log` or `C:\Users\%USERNAME%\AppData\Local\zebrad.log` /// - Windows: `%LOCALAPPDATA%\zebrad.log` or `C:\Users\%USERNAME%\AppData\Local\zebrad.log`
@ -131,6 +190,21 @@ pub struct Config {
pub use_journald: bool, pub use_journald: bool,
} }
/// The progress bars that Zebra will show while running.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum ProgressConfig {
/// Show a lot of progress bars.
Detailed,
/// Show a few important progress bars.
//
// TODO: actually hide some progress bars in this mode.
#[default]
#[serde(other)]
Summary,
}
impl Config { impl Config {
/// Returns `true` if standard output should use color escapes. /// Returns `true` if standard output should use color escapes.
/// Automatically checks if Zebra is running in a terminal. /// Automatically checks if Zebra is running in a terminal.
@ -152,12 +226,10 @@ impl Config {
} }
} }
impl Default for Config { impl Default for InnerConfig {
fn default() -> Self { fn default() -> Self {
#[cfg(feature = "progress-bar")] // TODO: enable progress bars by default once they have been tested
let default_log_file = dirs::state_dir() let progress_bar = None;
.or_else(dirs::data_local_dir)
.map(|dir| dir.join("zebrad.log"));
Self { Self {
use_color: true, use_color: true,
@ -166,11 +238,50 @@ impl Default for Config {
buffer_limit: 128_000, buffer_limit: 128_000,
endpoint_addr: None, endpoint_addr: None,
flamegraph: None, flamegraph: None,
#[cfg(not(feature = "progress-bar"))] progress_bar,
log_file: None, log_file: runtime_default_log_file(None, progress_bar),
#[cfg(feature = "progress-bar")]
log_file: default_log_file,
use_journald: false, use_journald: false,
} }
} }
} }
/// Returns the runtime default log file path based on the `log_file` and `progress_bar` configs.
fn runtime_default_log_file(
log_file: Option<PathBuf>,
progress_bar: Option<ProgressConfig>,
) -> Option<PathBuf> {
if let Some(log_file) = log_file {
return Some(log_file);
}
// If the progress bar is active, we want to use a log file regardless of the config.
// (Logging to a terminal erases parts of the progress bars, making both unreadable.)
if progress_bar.is_some() {
return default_log_file();
}
None
}
/// Returns the configured log file path using the runtime `log_file` and `progress_bar` config.
///
/// This is the inverse of [`runtime_default_log_file()`].
fn disk_default_log_file(
log_file: Option<PathBuf>,
progress_bar: Option<ProgressConfig>,
) -> Option<PathBuf> {
// If the progress bar is active, and we've likely substituted the default log file path,
// don't write that substitute to the config on disk.
if progress_bar.is_some() && log_file == default_log_file() {
return None;
}
log_file
}
/// Returns the default log file path.
fn default_log_file() -> Option<PathBuf> {
dirs::state_dir()
.or_else(dirs::data_local_dir)
.map(|dir| dir.join("zebrad.log"))
}

View File

@ -79,14 +79,14 @@ impl Tracing {
/// and the Zebra logo on startup. (If the terminal supports it.) /// and the Zebra logo on startup. (If the terminal supports it.)
// //
// This method should only print to stderr, because stdout is for tracing logs. // This method should only print to stderr, because stdout is for tracing logs.
#[allow(clippy::print_stderr, clippy::unwrap_in_result)] #[allow(clippy::print_stdout, clippy::print_stderr, clippy::unwrap_in_result)]
pub fn new(network: Network, config: Config, uses_intro: bool) -> Result<Self, FrameworkError> { pub fn new(network: Network, config: Config, uses_intro: bool) -> Result<Self, FrameworkError> {
// Only use color if tracing output is being sent to a terminal or if it was explicitly // Only use color if tracing output is being sent to a terminal or if it was explicitly
// forced to. // forced to.
let use_color = config.use_color_stdout(); let use_color = config.use_color_stdout();
let use_color_stderr = config.use_color_stderr(); let use_color_stderr = config.use_color_stderr();
let filter = config.filter.unwrap_or_default(); let filter = config.filter.clone().unwrap_or_default();
let flame_root = &config.flamegraph; let flame_root = &config.flamegraph;
// Only show the intro for user-focused node server commands like `start` // Only show the intro for user-focused node server commands like `start`
@ -139,7 +139,8 @@ impl Tracing {
} }
if uses_intro { if uses_intro {
eprintln!("Sending logs to {log_file:?}..."); // We want this to appear on stdout instead of the usual log messages.
println!("Sending logs to {log_file:?}...");
} }
let log_file = File::options().append(true).create(true).open(log_file)?; let log_file = File::options().append(true).create(true).open(log_file)?;
Box::new(log_file) as BoxWrite Box::new(log_file) as BoxWrite
@ -305,7 +306,7 @@ impl Tracing {
// //
// TODO: move this to its own module? // TODO: move this to its own module?
#[cfg(feature = "progress-bar")] #[cfg(feature = "progress-bar")]
{ if let Some(progress_bar_config) = config.progress_bar.as_ref() {
use howudoin::consumers::TermLine; use howudoin::consumers::TermLine;
use std::time::Duration; use std::time::Duration;
@ -315,7 +316,11 @@ impl Tracing {
let terminal_consumer = TermLine::with_debounce(PROGRESS_BAR_DEBOUNCE); let terminal_consumer = TermLine::with_debounce(PROGRESS_BAR_DEBOUNCE);
howudoin::init(terminal_consumer); howudoin::init(terminal_consumer);
info!("activated progress bar"); info!(?progress_bar_config, "activated progress bars");
} else {
info!(
"set 'tracing.progress_bar =\"summary\"' in zebrad.toml to activate progress bars"
);
} }
Ok(Self { Ok(Self {

View File

@ -61,9 +61,10 @@
//! //!
//! ### Metrics //! ### Metrics
//! //!
//! * `prometheus`: export metrics to prometheus. //! * configuring a `tracing.progress_bar`: shows key metrics in the terminal using progress bars,
//! * `progress-bar`: shows key metrics in the terminal using progress bars,
//! and automatically configures Zebra to send logs to a file. //! and automatically configures Zebra to send logs to a file.
//! (The `progress-bar` feature is activated by default.)
//! * `prometheus`: export metrics to prometheus.
//! //!
//! Read the [metrics](https://zebra.zfnd.org/user/metrics.html) section of the book //! Read the [metrics](https://zebra.zfnd.org/user/metrics.html) section of the book
//! for more details. //! for more details.

View File

@ -161,7 +161,12 @@ use zebra_network::constants::PORT_IN_USE_ERROR;
use zebra_node_services::rpc_client::RpcRequestClient; use zebra_node_services::rpc_client::RpcRequestClient;
use zebra_state::{constants::LOCK_FILE_ERROR, database_format_version_in_code}; use zebra_state::{constants::LOCK_FILE_ERROR, database_format_version_in_code};
use zebra_test::{args, command::ContextFrom, net::random_known_port, prelude::*}; use zebra_test::{
args,
command::{to_regex::CollectRegexSet, ContextFrom},
net::random_known_port,
prelude::*,
};
mod common; mod common;
@ -605,7 +610,7 @@ fn config_tests() -> Result<()> {
// Check that Zebra's previous configurations still work // Check that Zebra's previous configurations still work
stored_configs_work()?; stored_configs_work()?;
// Runs `zebrad` serially to avoid potential port conflicts // We run the `zebrad` app test after the config tests, to avoid potential port conflicts
app_no_args()?; app_no_args()?;
Ok(()) Ok(())
@ -619,6 +624,8 @@ fn app_no_args() -> Result<()> {
// start caches state, so run one of the start tests with persistent state // start caches state, so run one of the start tests with persistent state
let testdir = testdir()?.with_config(&mut persistent_test_config()?)?; let testdir = testdir()?.with_config(&mut persistent_test_config()?)?;
tracing::info!(?testdir, "running zebrad with no config (default settings)");
let mut child = testdir.spawn_child(args![])?; let mut child = testdir.spawn_child(args![])?;
// Run the program and kill it after a few seconds // Run the program and kill it after a few seconds
@ -651,6 +658,8 @@ fn valid_generated_config(command: &str, expect_stdout_line_contains: &str) -> R
// Add a config file name to tempdir path // Add a config file name to tempdir path
let generated_config_path = testdir.path().join("zebrad.toml"); let generated_config_path = testdir.path().join("zebrad.toml");
tracing::info!(?generated_config_path, "generating valid config");
// Generate configuration in temp dir path // Generate configuration in temp dir path
let child = let child =
testdir.spawn_child(args!["generate", "-o": generated_config_path.to_str().unwrap()])?; testdir.spawn_child(args!["generate", "-o": generated_config_path.to_str().unwrap()])?;
@ -664,6 +673,8 @@ fn valid_generated_config(command: &str, expect_stdout_line_contains: &str) -> R
"generated config file not found" "generated config file not found"
); );
tracing::info!(?generated_config_path, "testing valid config parsing");
// Run command using temp dir and kill it after a few seconds // Run command using temp dir and kill it after a few seconds
let mut child = testdir.spawn_child(args![command])?; let mut child = testdir.spawn_child(args![command])?;
std::thread::sleep(LAUNCH_DELAY); std::thread::sleep(LAUNCH_DELAY);
@ -702,6 +713,8 @@ fn last_config_is_stored() -> Result<()> {
// Add a config file name to tempdir path // Add a config file name to tempdir path
let generated_config_path = testdir.path().join("zebrad.toml"); let generated_config_path = testdir.path().join("zebrad.toml");
tracing::info!(?generated_config_path, "generated current config");
// Generate configuration in temp dir path // Generate configuration in temp dir path
let child = let child =
testdir.spawn_child(args!["generate", "-o": generated_config_path.to_str().unwrap()])?; testdir.spawn_child(args!["generate", "-o": generated_config_path.to_str().unwrap()])?;
@ -715,6 +728,11 @@ fn last_config_is_stored() -> Result<()> {
"generated config file not found" "generated config file not found"
); );
tracing::info!(
?generated_config_path,
"testing current config is in stored configs"
);
// Get the contents of the generated config file // Get the contents of the generated config file
let generated_content = let generated_content =
fs::read_to_string(generated_config_path).expect("Should have been able to read the file"); fs::read_to_string(generated_config_path).expect("Should have been able to read the file");
@ -815,6 +833,11 @@ fn invalid_generated_config() -> Result<()> {
// Add a config file name to tempdir path. // Add a config file name to tempdir path.
let config_path = testdir.path().join("zebrad.toml"); let config_path = testdir.path().join("zebrad.toml");
tracing::info!(
?config_path,
"testing invalid config parsing: generating valid config"
);
// Generate a valid config file in the temp dir. // Generate a valid config file in the temp dir.
let child = testdir.spawn_child(args!["generate", "-o": config_path.to_str().unwrap()])?; let child = testdir.spawn_child(args!["generate", "-o": config_path.to_str().unwrap()])?;
@ -849,10 +872,14 @@ fn invalid_generated_config() -> Result<()> {
secs = 3600 secs = 3600
"; ";
tracing::info!(?config_path, "writing invalid config");
// Write the altered config file so that Zebra can pick it up. // Write the altered config file so that Zebra can pick it up.
fs::write(config_path.to_str().unwrap(), config_file.as_bytes()) fs::write(config_path.to_str().unwrap(), config_file.as_bytes())
.expect("Could not write the altered config file."); .expect("Could not write the altered config file.");
tracing::info!(?config_path, "testing invalid config parsing");
// Run Zebra in a temp dir so that it loads the config. // Run Zebra in a temp dir so that it loads the config.
let mut child = testdir.spawn_child(args!["start"])?; let mut child = testdir.spawn_child(args!["start"])?;
@ -883,6 +910,8 @@ fn invalid_generated_config() -> Result<()> {
fn stored_configs_work() -> Result<()> { fn stored_configs_work() -> Result<()> {
let old_configs_dir = configs_dir(); let old_configs_dir = configs_dir();
tracing::info!(?old_configs_dir, "testing older config parsing");
for config_file in old_configs_dir for config_file in old_configs_dir
.read_dir() .read_dir()
.expect("read_dir call failed") .expect("read_dir call failed")
@ -892,10 +921,10 @@ fn stored_configs_work() -> Result<()> {
let config_file_name = config_file_path let config_file_name = config_file_path
.file_name() .file_name()
.expect("config files must have a file name") .expect("config files must have a file name")
.to_string_lossy(); .to_str()
.expect("config file names are valid unicode");
if config_file_name.as_ref().starts_with('.') || config_file_name.as_ref().starts_with('#') if config_file_name.starts_with('.') || config_file_name.starts_with('#') {
{
// Skip editor files and other invalid config paths // Skip editor files and other invalid config paths
tracing::info!( tracing::info!(
?config_file_path, ?config_file_path,
@ -907,10 +936,7 @@ fn stored_configs_work() -> Result<()> {
// ignore files starting with getblocktemplate prefix // ignore files starting with getblocktemplate prefix
// if we were not built with the getblocktemplate-rpcs feature. // if we were not built with the getblocktemplate-rpcs feature.
#[cfg(not(feature = "getblocktemplate-rpcs"))] #[cfg(not(feature = "getblocktemplate-rpcs"))]
if config_file_name if config_file_name.starts_with(GET_BLOCK_TEMPLATE_CONFIG_PREFIX) {
.as_ref()
.starts_with(GET_BLOCK_TEMPLATE_CONFIG_PREFIX)
{
tracing::info!( tracing::info!(
?config_file_path, ?config_file_path,
"skipping getblocktemplate-rpcs config file path" "skipping getblocktemplate-rpcs config file path"
@ -921,12 +947,41 @@ fn stored_configs_work() -> Result<()> {
let run_dir = testdir()?; let run_dir = testdir()?;
let stored_config_path = config_file_full_path(config_file.path()); let stored_config_path = config_file_full_path(config_file.path());
tracing::info!(
?stored_config_path,
"testing old config can be parsed by current zebrad"
);
// run zebra with stored config // run zebra with stored config
let mut child = let mut child =
run_dir.spawn_child(args!["-c", stored_config_path.to_str().unwrap(), "start"])?; run_dir.spawn_child(args!["-c", stored_config_path.to_str().unwrap(), "start"])?;
// zebra was able to start with the stored config let success_regexes = [
child.expect_stdout_line_matches("Starting zebrad".to_string())?; // When logs are sent to the terminal, we see the config loading message and path.
format!(
"loaded zebrad config.*config_path.*=.*{}",
regex::escape(config_file_name)
),
// If they are sent to a file, we see a log file message on stdout,
// and a logo, welcome message, and progress bar on stderr.
"Sending logs to".to_string(),
// TODO: add expect_stdout_or_stderr_line_matches() and check for this instead:
//"Thank you for running a mainnet zebrad".to_string(),
];
tracing::info!(
?stored_config_path,
?success_regexes,
"waiting for zebrad to parse config and start logging"
);
let success_regexes = success_regexes
.iter()
.collect_regex_set()
.expect("regexes are valid");
// Zebra was able to start with the stored config.
child.expect_stdout_line_matches(success_regexes)?;
// finish // finish
child.kill(false)?; child.kill(false)?;

View File

@ -58,10 +58,9 @@ pub fn default_test_config() -> Result<ZebradConfig> {
env::var("ZEBRA_FORCE_USE_COLOR"), env::var("ZEBRA_FORCE_USE_COLOR"),
Err(env::VarError::NotPresent) Err(env::VarError::NotPresent)
); );
let tracing = tracing::Config {
force_use_color, let mut tracing = tracing::Config::default();
..tracing::Config::default() tracing.force_use_color = force_use_color;
};
let mut state = zebra_state::Config::ephemeral(); let mut state = zebra_state::Config::ephemeral();
state.debug_validity_check_interval = Some(DATABASE_FORMAT_CHECK_INTERVAL); state.debug_validity_check_interval = Some(DATABASE_FORMAT_CHECK_INTERVAL);

View File

@ -0,0 +1,73 @@
# Default configuration for zebrad.
#
# This file can be used as a skeleton for custom configs.
#
# Unspecified fields use default values. Optional fields are Some(field) if the
# field is present and None if it is absent.
#
# This file is generated as an example using zebrad's current defaults.
# You should set only the config options you want to keep, and delete the rest.
# Only a subset of fields are present in the skeleton, since optional values
# whose default is None are omitted.
#
# The config format (including a complete list of sections and fields) is
# documented here:
# https://doc.zebra.zfnd.org/zebrad/config/struct.ZebradConfig.html
#
# zebrad attempts to load configs in the following order:
#
# 1. The -c flag on the command line, e.g., `zebrad -c myconfig.toml start`;
# 2. The file `zebrad.toml` in the users's preference directory (platform-dependent);
# 3. The default config.
[consensus]
checkpoint_sync = true
debug_skip_parameter_preload = false
[mempool]
eviction_memory_time = "1h"
tx_cost_limit = 80000000
[metrics]
[network]
cache_dir = true
crawl_new_peer_interval = "1m 1s"
initial_mainnet_peers = [
"dnsseed.z.cash:8233",
"dnsseed.str4d.xyz:8233",
"mainnet.seeder.zfnd.org:8233",
"mainnet.is.yolo.money:8233",
]
initial_testnet_peers = [
"dnsseed.testnet.z.cash:18233",
"testnet.seeder.zfnd.org:18233",
"testnet.is.yolo.money:18233",
]
listen_addr = "0.0.0.0:8233"
max_connections_per_ip = 1
network = "Mainnet"
peerset_initial_target_size = 25
[rpc]
debug_force_finished_sync = false
parallel_cpu_threads = 1
[state]
cache_dir = "cache_dir"
delete_old_database = true
ephemeral = false
[sync]
checkpoint_verify_concurrency_limit = 1000
download_concurrency_limit = 50
full_verify_concurrency_limit = 20
parallel_cpu_threads = 0
[tracing]
buffer_limit = 128000
force_use_color = false
progress_bar = "summary"
use_color = true
use_journald = false