Commit 98105da7 authored by Nick Mathewson's avatar Nick Mathewson 🤹
Browse files

Merge branch 'move-log' into 'main'

Combine logging config and implementation in arti lib crate

See merge request !421
parents ff05ed61 f7c9910a
Loading
Loading
Loading
Loading
+7 −9
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ dependencies = [
 "cfg-if 1.0.0",
 "clap",
 "config",
 "derive_builder",
 "futures",
 "libc",
 "notify",
@@ -89,6 +90,7 @@ dependencies = [
 "rlimit",
 "serde",
 "tokio",
 "tor-config",
 "tor-error",
 "tor-rtcompat",
 "tor-socksproto",
@@ -105,6 +107,7 @@ name = "arti-bench"
version = "0.1.0"
dependencies = [
 "anyhow",
 "arti",
 "arti-client",
 "arti-config",
 "clap",
@@ -193,6 +196,7 @@ name = "arti-testing"
version = "0.1.0"
dependencies = [
 "anyhow",
 "arti",
 "arti-client",
 "arti-config",
 "async-trait",
@@ -869,18 +873,14 @@ dependencies = [

[[package]]
name = "derive_builder"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd8db2d51ce241afb74f9e5cf9482de69c4b77657c2ddaf78993eda8ce2ce8c"
version = "0.11.1"
dependencies = [
 "derive_builder_macro",
]

[[package]]
name = "derive_builder_core"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf03b84d76c5bb011d983c17e2675ab49f5498f121bf1ceae5b5855b4a4dddd"
version = "0.11.1"
dependencies = [
 "darling",
 "proc-macro2",
@@ -890,9 +890,7 @@ dependencies = [

[[package]]
name = "derive_builder_macro"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cae44c47bca4fd1eaeda52b3d0eecb664ddcbc0d4e7b238bae7ec2c931646c0"
version = "0.11.1"
dependencies = [
 "derive_builder_core",
 "syn",
+1 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ tracing = "0.1.18"
tracing-subscriber = { version = "0.3.0", features = ["env-filter"] }
tokio = { version = "1.7", features = ["full"] }
tor-rtcompat = { path="../tor-rtcompat", version = "0.1.0", features = ["tokio", "native-tls"] }
arti = { path="../arti", version = "0.1.0"}
arti-config = { path="../arti-config", version = "0.1.0"}
arti-client = { package="arti-client", path = "../arti-client", version = "0.1.0"}
tokio-socks = "0.5"
+1 −1
Original line number Diff line number Diff line
@@ -36,8 +36,8 @@
#![allow(clippy::unwrap_used)]

use anyhow::{anyhow, Result};
use arti::cfg::ArtiConfig;
use arti_client::{IsolationToken, TorAddr, TorClient, TorClientConfig};
use arti_config::ArtiConfig;
use clap::{App, Arg};
use futures::StreamExt;
use rand::distributions::Standard;
+1 −3
Original line number Diff line number Diff line
@@ -52,9 +52,7 @@ mod options;

pub use cmdline::CmdLine;
pub use options::{
    ApplicationConfig, ApplicationConfigBuilder, ArtiConfig, ArtiConfigBuilder, LogRotation,
    LogfileConfig, LogfileConfigBuilder, LoggingConfig, LoggingConfigBuilder, ProxyConfig,
    ProxyConfigBuilder,
    ApplicationConfig, ApplicationConfigBuilder, ProxyConfig, ProxyConfigBuilder, ARTI_DEFAULTS,
};
use tor_config::CfgPath;

+4 −383
Original line number Diff line number Diff line
//! Handling for arti's configuration formats.

use arti_client::config::{
    SystemConfig, SystemConfigBuilder, TorClientConfig, TorClientConfigBuilder,
};
use derive_builder::Builder;
use serde::Deserialize;
use std::convert::TryFrom;
use tor_config::{CfgPath, ConfigBuildError};
use tor_config::ConfigBuildError;

/// Default options to use for our configuration.
pub(crate) const ARTI_DEFAULTS: &str = concat!(include_str!("./arti_defaults.toml"),);
//
// TODO should this be in `arti::cfg` ?
pub const ARTI_DEFAULTS: &str = concat!(include_str!("./arti_defaults.toml"),);

/// Structure to hold our application configuration options
#[derive(Deserialize, Debug, Default, Clone, Builder, Eq, PartialEq)]
@@ -36,130 +34,6 @@ impl ApplicationConfig {
    }
}

/// Structure to hold our logging configuration options
#[derive(Deserialize, Debug, Clone, Builder, Eq, PartialEq)]
#[serde(deny_unknown_fields)]
#[non_exhaustive] // TODO(nickm) remove public elements when I revise this.
#[builder(build_fn(error = "ConfigBuildError"))]
#[builder(derive(Deserialize))]
pub struct LoggingConfig {
    /// Filtering directives that determine tracing levels as described at
    /// <https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/targets/struct.Targets.html#impl-FromStr>
    ///
    /// You can override this setting with the -l, --log-level command line parameter.
    ///
    /// Example: "info,tor_proto::channel=trace"
    #[serde(default = "default_console_filter")]
    #[builder(default = "default_console_filter()", setter(into, strip_option))]
    console: Option<String>,

    /// Filtering directives for the journald logger.
    ///
    /// Only takes effect if Arti is built with the `journald` filter.
    #[serde(default)]
    #[builder(default, setter(into, strip_option))]
    journald: Option<String>,

    /// Configuration for one or more logfiles.
    #[serde(default)]
    #[builder(default)]
    file: Vec<LogfileConfig>,
}

/// Return a default tracing filter value for `logging.console`.
#[allow(clippy::unnecessary_wraps)]
fn default_console_filter() -> Option<String> {
    Some("debug".to_owned())
}

impl Default for LoggingConfig {
    fn default() -> Self {
        Self::builder().build().expect("Default builder failed")
    }
}

impl LoggingConfig {
    /// Return a new LoggingConfigBuilder
    pub fn builder() -> LoggingConfigBuilder {
        LoggingConfigBuilder::default()
    }

    /// Return the configured journald filter, if one is present
    pub fn journald_filter(&self) -> Option<&str> {
        match self.journald {
            Some(ref s) if !s.is_empty() => Some(s.as_str()),
            _ => None,
        }
    }

    /// Return the configured stdout filter, if one is present
    pub fn console_filter(&self) -> Option<&str> {
        match self.console {
            Some(ref s) if !s.is_empty() => Some(s.as_str()),
            _ => None,
        }
    }

    /// Return a list of the configured log files
    pub fn logfiles(&self) -> &[LogfileConfig] {
        &self.file
    }
}

/// Configuration information for an (optionally rotating) logfile.
#[derive(Deserialize, Debug, Builder, Clone, Eq, PartialEq)]
pub struct LogfileConfig {
    /// How often to rotate the file?
    #[serde(default)]
    #[builder(default)]
    rotate: LogRotation,
    /// Where to write the files?
    path: CfgPath,
    /// Filter to apply before writing
    filter: String,
}

/// How often to rotate a log file
#[derive(Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
#[non_exhaustive]
#[serde(rename_all = "lowercase")]
pub enum LogRotation {
    /// Rotate logs daily
    Daily,
    /// Rotate logs hourly
    Hourly,
    /// Never rotate the log
    Never,
}

impl Default for LogRotation {
    fn default() -> Self {
        Self::Never
    }
}

impl LogfileConfig {
    /// Return a new [`LogfileConfigBuilder`]
    pub fn builder() -> LogfileConfigBuilder {
        LogfileConfigBuilder::default()
    }

    /// Return the configured rotation interval.
    pub fn rotate(&self) -> LogRotation {
        self.rotate
    }

    /// Return the configured path to the log file.
    pub fn path(&self) -> &CfgPath {
        &self.path
    }

    /// Return the configured filter.
    pub fn filter(&self) -> &str {
        &self.filter
    }
}

/// Configuration for one or more proxy listeners.
#[derive(Deserialize, Debug, Clone, Builder, Eq, PartialEq)]
#[serde(deny_unknown_fields)]
@@ -207,256 +81,3 @@ impl ProxyConfig {
        self.dns_port
    }
}

/// Structure to hold Arti's configuration options, whether from a
/// configuration file or the command line.
//
/// These options are declared in a public crate outside of `arti` so that other
/// applications can parse and use them, if desired.  If you're only embedding
/// arti via `arti-client`, and you don't want to use Arti's configuration
/// format, use [`arti_client::TorClientConfig`] instead.
///
/// By default, Arti will run using the default Tor network, store state and
/// cache information to a per-user set of directories shared by all
/// that user's applications, and run a SOCKS client on a local port.
///
/// NOTE: These are NOT the final options or their final layout. Expect NO
/// stability here.
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct ArtiConfig {
    /// Configuration for application behavior.
    application: ApplicationConfig,

    /// Configuration for proxy listeners
    proxy: ProxyConfig,

    /// Logging configuration
    logging: LoggingConfig,

    /// Information on system resources used by Arti.
    system: SystemConfig,

    /// Configuration of the actual Tor client
    tor: TorClientConfig,
}

impl TryFrom<config::Config> for ArtiConfig {
    type Error = config::ConfigError;
    fn try_from(cfg: config::Config) -> Result<ArtiConfig, Self::Error> {
        let builder: ArtiConfigBuilder = cfg.try_deserialize()?;
        builder
            .build()
            .map_err(|e| config::ConfigError::Foreign(Box::new(e)))
    }
}

// This handwritten impl ought not to exist, but it is needed until #374 is done.
impl From<ArtiConfigBuilder> for TorClientConfigBuilder {
    fn from(cfg: ArtiConfigBuilder) -> TorClientConfigBuilder {
        cfg.tor
    }
}

impl ArtiConfig {
    /// Construct a [`TorClientConfig`] based on this configuration.
    pub fn tor_client_config(&self) -> Result<TorClientConfig, ConfigBuildError> {
        Ok(self.tor.clone())
    }

    /// Return a new ArtiConfigBuilder.
    pub fn builder() -> ArtiConfigBuilder {
        ArtiConfigBuilder::default()
    }

    /// Return the [`ApplicationConfig`] for this configuration.
    pub fn application(&self) -> &ApplicationConfig {
        &self.application
    }

    /// Return the [`LoggingConfig`] for this configuration.
    pub fn logging(&self) -> &LoggingConfig {
        &self.logging
    }

    /// Return the [`ProxyConfig`] for this configuration.
    pub fn proxy(&self) -> &ProxyConfig {
        &self.proxy
    }
}

/// Builder object used to construct an ArtiConfig.
///
/// Most code won't need this, and should use [`TorClientConfigBuilder`] instead.
///
/// Unlike other builder types in Arti, this builder works by exposing an
/// inner builder for each section in the [`TorClientConfig`].
#[derive(Default, Clone, Deserialize)]
// This ought to be replaced by a derive-builder generated struct (probably as part of #374),
// but currently derive-builder can't do this.
pub struct ArtiConfigBuilder {
    /// Builder for the actual Tor client.
    #[serde(flatten)]
    tor: TorClientConfigBuilder,

    /// Builder for the application section
    #[serde(default)]
    application: ApplicationConfigBuilder,
    /// Builder for the proxy section.
    #[serde(default)]
    proxy: ProxyConfigBuilder,
    /// Builder for the logging section.
    #[serde(default)]
    logging: LoggingConfigBuilder,
    /// Builder for system resource configuration.
    #[serde(default)]
    system: SystemConfigBuilder,
}

impl ArtiConfigBuilder {
    /// Try to construct a new [`ArtiConfig`] from this builder.
    pub fn build(&self) -> Result<ArtiConfig, ConfigBuildError> {
        let application = self
            .application
            .build()
            .map_err(|e| e.within("application"))?;
        let proxy = self.proxy.build().map_err(|e| e.within("proxy"))?;
        let logging = self.logging.build().map_err(|e| e.within("logging"))?;
        let system = self.system.build().map_err(|e| e.within("system"))?;
        let tor = TorClientConfigBuilder::from(self.clone());
        let tor = tor.build()?;
        Ok(ArtiConfig {
            application,
            proxy,
            logging,
            system,
            tor,
        })
    }

    /// Return a mutable reference to an [`ApplicationConfigBuilder`] to use in
    /// configuring the Arti process.
    pub fn application(&mut self) -> &mut ApplicationConfigBuilder {
        &mut self.application
    }

    /// Return a mutable reference to a [`ProxyConfig`] to use in
    /// configuring the Arti process.
    pub fn proxy(&mut self) -> &mut ProxyConfigBuilder {
        &mut self.proxy
    }

    /// Return a mutable reference to a
    /// [`LoggingConfigBuilder`]
    /// to use in configuring the Arti process.
    pub fn logging(&mut self) -> &mut LoggingConfigBuilder {
        &mut self.logging
    }

    /// Return a mutable reference to a `TorClientConfigBuilder`.
    /// to use in configuring the underlying Tor network.
    ///
    /// Most programs shouldn't need to alter this configuration: it's only for
    /// cases when you need to use a nonstandard set of Tor directory authorities
    /// and fallback caches.
    pub fn tor(&mut self) -> &mut TorClientConfigBuilder {
        &mut self.tor
    }

    /// Return a mutable reference to a [`SystemConfigBuilder`].
    ///
    /// This section controls the system parameters used by Arti.
    pub fn system(&mut self) -> &mut SystemConfigBuilder {
        &mut self.system
    }
}

#[cfg(test)]
mod test {
    #![allow(clippy::unwrap_used)]

    use arti_client::config::dir;
    use std::convert::TryInto;
    use std::time::Duration;

    use super::*;

    #[test]
    fn default_config() {
        // TODO: this is duplicate code.
        let cfg = config::Config::builder()
            .add_source(config::File::from_str(
                ARTI_DEFAULTS,
                config::FileFormat::Toml,
            ))
            .build()
            .unwrap();

        let parsed: ArtiConfig = cfg.try_into().unwrap();
        let default = ArtiConfig::default();
        assert_eq!(&parsed, &default);

        // Make sure that the client configuration this gives us is the default one.
        let client_config = parsed.tor_client_config().unwrap();
        let dflt_client_config = TorClientConfig::default();
        assert_eq!(&client_config, &dflt_client_config);
    }

    #[test]
    fn builder() {
        use arti_client::config::dir::DownloadSchedule;
        use tor_config::CfgPath;
        let sec = std::time::Duration::from_secs(1);

        let auth = dir::Authority::builder()
            .name("Fred")
            .v3ident([22; 20].into())
            .build()
            .unwrap();
        let fallback = dir::FallbackDir::builder()
            .rsa_identity([23; 20].into())
            .ed_identity([99; 32].into())
            .orports(vec!["127.0.0.7:7".parse().unwrap()])
            .build()
            .unwrap();

        let mut bld = ArtiConfig::builder();
        bld.proxy().socks_port(Some(9999));
        bld.logging().console("warn");
        bld.tor()
            .tor_network()
            .authorities(vec![auth])
            .fallback_caches(vec![fallback]);
        bld.tor()
            .storage()
            .cache_dir(CfgPath::new("/var/tmp/foo".to_owned()))
            .state_dir(CfgPath::new("/var/tmp/bar".to_owned()));
        bld.tor()
            .download_schedule()
            .retry_certs(DownloadSchedule::new(10, sec, 3))
            .retry_microdescs(DownloadSchedule::new(30, 10 * sec, 9));
        bld.tor()
            .override_net_params()
            .insert("wombats-per-quokka".to_owned(), 7);
        bld.tor()
            .path_rules()
            .ipv4_subnet_family_prefix(20)
            .ipv6_subnet_family_prefix(48);
        bld.tor()
            .preemptive_circuits()
            .disable_at_threshold(12)
            .initial_predicted_ports(vec![80, 443])
            .prediction_lifetime(Duration::from_secs(3600))
            .min_exit_circs_for_port(2);
        bld.tor()
            .circuit_timing()
            .max_dirtiness(90 * sec)
            .request_timeout(10 * sec)
            .request_max_retries(22)
            .request_loyalty(3600 * sec);
        bld.tor().address_filter().allow_local_addrs(true);

        let val = bld.build().unwrap();

        assert_ne!(val, ArtiConfig::default());
    }
}
Loading