diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 7ac50999..5ab3778d 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -17,6 +17,5 @@ # Applications - [Applications](applications.md) - [zebrad](./applications/zebrad.md) - - [Enviroment variables](./applications/zebrad/enviroment-variables.md) - [utils](./applications/utils.md) - [zebra-checkpoints](./applications/utils/zebra-checkpoints.md) diff --git a/book/src/applications/zebrad.md b/book/src/applications/zebrad.md index 8a6fad7a..a340d00e 100644 --- a/book/src/applications/zebrad.md +++ b/book/src/applications/zebrad.md @@ -1,4 +1,3 @@ # zebrad -- [Enviroment variables](./zebrad/enviroment-variables.md) diff --git a/book/src/applications/zebrad/enviroment-variables.md b/book/src/applications/zebrad/enviroment-variables.md deleted file mode 100644 index 9306cfb5..00000000 --- a/book/src/applications/zebrad/enviroment-variables.md +++ /dev/null @@ -1,29 +0,0 @@ -# Zebrad enviroment variables - -All zebrad subcommands support the following enviroment variables: -- `ZEBRAD_CACHE_DIR`: The directory to store zebra data just as state, blocks, wallet, etc. -- `ZEBRAD_LOG`: Manipulate the log level. Regex is supported. - -## Examples: - -Use a custom data directory: -``` -export ZEBRAD_CACHE_DIR="/my/zebra_data_dir" -zebrad start -``` - -Output all info messages: - -``` -export ZEBRAD_LOG="info" -zebrad start -``` - -Output block_verify with trace level and above every 1000 blocks: - -``` -export ZEBRAD_LOG="[block_verify{height=Some\(BlockHeight\(.*000\)\)}]=trace" -zebrad start -``` - -Note: Log options are processed in this order: verbose flag, command stdout, enviroment variable, config file. \ No newline at end of file diff --git a/zebra-state/src/lib.rs b/zebra-state/src/lib.rs index 7d6d18fe..a37309cd 100644 --- a/zebra-state/src/lib.rs +++ b/zebra-state/src/lib.rs @@ -37,32 +37,35 @@ pub mod on_disk; pub struct Config { /// The root directory for storing cached data. /// + /// Cached data includes any state that can be replicated from the network + /// (e.g., the chain state, the blocks, the UTXO set, etc.). It does *not* + /// include private data that cannot be replicated from the network, such as + /// wallet data. That data is not handled by `zebra-state`. + /// /// Each network has a separate state, which is stored in "mainnet/state" /// and "testnet/state" subdirectories. - pub cache_dir: Option, + /// + /// The default directory is platform dependent, based on + /// [`dirs::cache_dir()`](https://docs.rs/dirs/3.0.1/dirs/fn.cache_dir.html): + /// + /// |Platform | Value | Example | + /// | ------- | ----------------------------------------------- | ---------------------------------- | + /// | Linux | `$XDG_CACHE_HOME/zebra` or `$HOME/.cache/zebra` | /home/alice/.cache/zebra | + /// | macOS | `$HOME/Library/Caches/zebra` | /Users/Alice/Library/Caches/zebra | + /// | Windows | `{FOLDERID_LocalAppData}\zebra` | C:\Users\Alice\AppData\Local\zebra | + /// | Other | `std::env::current_dir()/cache` | | + pub cache_dir: PathBuf, } impl Config { /// Generate the appropriate `sled::Config` for `network`, based on the /// provided `zebra_state::Config`. - /// - /// # Details - /// - /// This function should panic if the user of `zebra-state` doesn't configure - /// a directory to store the state. pub(crate) fn sled_config(&self, network: Network) -> sled::Config { let net_dir = match network { Mainnet => "mainnet", Testnet => "testnet", }; - let path = self - .cache_dir - .as_ref() - .unwrap_or_else(|| { - todo!("create a nice user facing error explaining how to set the cache directory in zebrad.toml:\n[state]\ncache_dir = '/path/to/cache-or-tmp'") - }) - .join(net_dir) - .join("state"); + let path = self.cache_dir.join(net_dir).join("state"); sled::Config::default().path(path) } @@ -70,11 +73,9 @@ impl Config { impl Default for Config { fn default() -> Self { - let cache_dir = std::env::var("ZEBRAD_CACHE_DIR") - .map(PathBuf::from) - .ok() - .or_else(|| dirs::cache_dir().map(|dir| dir.join("zebra"))); - + let cache_dir = dirs::cache_dir() + .unwrap_or_else(|| std::env::current_dir().unwrap().join("cache")) + .join("zebra"); Self { cache_dir } } } @@ -224,17 +225,4 @@ mod tests { Testnet => assert_eq!(path.file_name(), Some(OsStr::new("testnet"))), } } - - /// Check what happens when the config is invalid. - #[test] - #[should_panic] - fn test_no_path() { - // We don't call `zebra_test::init` here, to silence the expected panic log - // TODO: - // - implement test log levels in #760 - // - call `zebra_test::init` - // - disable all log output from this test - let bad_config = Config { cache_dir: None }; - let _unreachable = bad_config.sled_config(Mainnet); - } } diff --git a/zebra-state/tests/basic.rs b/zebra-state/tests/basic.rs index ae9c2527..8d2e3d75 100644 --- a/zebra-state/tests/basic.rs +++ b/zebra-state/tests/basic.rs @@ -122,12 +122,8 @@ async fn check_transcripts(network: Network) -> Result<(), Report> { transcript.check(service).await?; let storage_guard = TempDir::new("")?; - let service = on_disk::init( - Config { - cache_dir: Some(storage_guard.path().to_owned()), - }, - network, - ); + let cache_dir = storage_guard.path().to_owned(); + let service = on_disk::init(Config { cache_dir }, network); let transcript = Transcript::from(transcript_data.iter().cloned()); /// SPANDOC: check the on disk service against the transcript transcript.check(service).await?; diff --git a/zebra-test/src/command.rs b/zebra-test/src/command.rs index 5bb9b545..c1ed4c04 100644 --- a/zebra-test/src/command.rs +++ b/zebra-test/src/command.rs @@ -3,7 +3,7 @@ use color_eyre::{ Help, SectionExt, }; use std::process::{Child, Command, ExitStatus, Output}; -use std::{env, fs}; +use std::{fs, io::Write}; use tempdir::TempDir; #[cfg(unix)] @@ -15,9 +15,19 @@ pub fn test_cmd(path: &str) -> Result<(Command, impl Drop)> { let mut cmd = Command::new(path); cmd.current_dir(dir.path()); - let state_dir = dir.path().join("state"); - fs::create_dir(&state_dir)?; - env::set_var("ZEBRAD_CACHE_DIR", state_dir); + let cache_dir = dir.path().join("state"); + fs::create_dir(&cache_dir)?; + + fs::File::create(dir.path().join("zebrad.toml"))?.write_all( + format!( + "[state]\ncache_dir = '{}'", + cache_dir + .into_os_string() + .into_string() + .map_err(|_| eyre!("tmp dir path cannot be encoded as UTF8"))? + ) + .as_bytes(), + )?; Ok((cmd, dir)) } diff --git a/zebrad/src/application.rs b/zebrad/src/application.rs index 8a6c37ed..51a9e416 100644 --- a/zebrad/src/application.rs +++ b/zebrad/src/application.rs @@ -92,7 +92,7 @@ impl Application for ZebradApp { color_eyre::install().unwrap(); if ZebradApp::command_is_server(&command) { - let tracing = self.tracing_component(command); + let tracing = self.tracing_component(); Ok(vec![Box::new(terminal), Box::new(tracing)]) } else { init_tracing_backup(); @@ -140,12 +140,13 @@ impl Application for ZebradApp { self.config = Some(config); if ZebradApp::command_is_server(&command) { - let level = self.level(command); - self.state - .components - .get_downcast_mut::() - .expect("Tracing component should be available") - .reload_filter(level); + if let Some(filter) = self.config.as_ref().unwrap().tracing.filter.as_ref() { + self.state + .components + .get_downcast_mut::() + .expect("Tracing component should be available") + .reload_filter(filter); + } // Work around some issues with dependency injection and configs let config = self @@ -177,49 +178,11 @@ impl Application for ZebradApp { } impl ZebradApp { - fn level(&self, command: &EntryPoint) -> String { - // `None` outputs zebrad usage information to stdout - let command_uses_stdout = match &command.command { - None => true, - Some(c) => c.uses_stdout(), - }; - - // Allow users to: - // - override all other configs and defaults using the command line - // - see command outputs without spurious log messages, by default - // - override the config file using an environmental variable - if command.verbose { - "debug".to_string() - } else if command_uses_stdout { - // Tracing sends output to stdout, so we disable info-level logs for - // some commands. - // - // TODO: send tracing output to stderr. This change requires an abscissa - // update, because `abscissa_core::component::Tracing` uses - // `tracing_subscriber::fmt::Formatter`, which has `Stdout` as a - // type parameter. We need `MakeWriter` or a similar type. - "warn".to_string() - } else if let Ok(level) = std::env::var("ZEBRAD_LOG") { - level - } else if let Some(ZebradConfig { - tracing: - crate::config::TracingSection { - filter: Some(filter), - endpoint_addr: _, - }, - .. - }) = &self.config - { - filter.clone() - } else { - "info".to_string() - } - } - - fn tracing_component(&self, command: &EntryPoint) -> Tracing { + fn tracing_component(&self) -> Tracing { // Construct a tracing subscriber with the supplied filter and enable reloading. let builder = tracing_subscriber::FmtSubscriber::builder() - .with_env_filter(self.level(command)) + // Set the filter to warn initially, then reset it in after_config. + .with_env_filter("warn") .with_filter_reloading(); let filter_handle = builder.reload_handle(); diff --git a/zebrad/src/commands.rs b/zebrad/src/commands.rs index 92632270..aeba33b8 100644 --- a/zebrad/src/commands.rs +++ b/zebrad/src/commands.rs @@ -50,28 +50,9 @@ pub enum ZebradCmd { } impl ZebradCmd { - /// Returns true if this command sends output to stdout. - /// - /// For example, `Generate` sends a default config file to stdout. - /// - /// Usage note: `abscissa_core::EntryPoint` stores an `Option`. - /// If the command is `None`, then abscissa writes zebrad usage information - /// to stdout. - pub(crate) fn uses_stdout(&self) -> bool { - match self { - // List all the commands, so new commands have to make a choice here - Generate(_) | Help(_) | Revhex(_) | Version(_) => true, - Seed(_) | Start(_) => false, - } - } - /// Returns true if this command is a server command. /// /// For example, `Start` acts as a Zcash node. - /// - /// Usage note: `abscissa_core::EntryPoint` stores an `Option`. - /// If the command is `None`, then abscissa prints zebrad's usage - /// information, then exits. pub(crate) fn is_server(&self) -> bool { match self { // List all the commands, so new commands have to make a choice here diff --git a/zebrad/src/config.rs b/zebrad/src/config.rs index 6e7420ea..a709c32f 100644 --- a/zebrad/src/config.rs +++ b/zebrad/src/config.rs @@ -37,6 +37,24 @@ pub struct ZebradConfig { #[serde(deny_unknown_fields, default)] pub struct TracingSection { /// The filter used for tracing events. + /// + /// The filter is used to create a `tracing-subscriber` + /// [`EnvFilter`](https://docs.rs/tracing-subscriber/0.2.10/tracing_subscriber/filter/struct.EnvFilter.html#directives), + /// and more details on the syntax can be found there or in the examples + /// below. + /// + /// # Examples + /// + /// `warn,zebrad=info,zebra_network=debug` sets a global `warn` level, an + /// `info` level for the `zebrad` crate, and a `debug` level for the + /// `zebra_network` crate. + /// + /// ```ascii,no_run + /// [block_verify{height=Some\(BlockHeight\(.*000\)\)}]=trace + /// ``` + /// sets `trace` level for all events occurring in the context of a + /// `block_verify` span whose `height` field ends in `000`, i.e., traces the + /// verification of every 1000th block. pub filter: Option, /// The endpoint address used for tracing.