diff --git a/Cargo.lock b/Cargo.lock index b5da57563..262aff6d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -697,6 +697,16 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + [[package]] name = "env_logger" version = "0.10.2" @@ -1069,6 +1079,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "hostname" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "windows", +] + [[package]] name = "http" version = "0.2.11" @@ -2561,12 +2582,14 @@ dependencies = [ "daemonize", "directories", "encoding_rs", + "env_filter", "env_logger", "filetime", "flate2", "fs-err", "futures", "gzp", + "hostname 0.4.0", "http 1.1.0", "http-body-util", "hyper 1.1.0", @@ -2959,7 +2982,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7434e95bcccce1215d30f4bf84fe8c00e8de1b9be4fb736d747ca53d36e7f96f" dependencies = [ "error-chain", - "hostname", + "hostname 0.3.1", "libc", "log", "time", @@ -3719,6 +3742,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.0", +] + [[package]] name = "windows-core" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index e47de37e7..c26d75193 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,6 +109,8 @@ zip = { version = "0.6", default-features = false } zstd = "0.13" # dist-server only +env_filter = { version = "0.1.3", optional = true } +hostname = { version = "0.4.0", optional = true } nix = { version = "0.28.0", optional = true, features = [ "mount", "user", @@ -192,7 +194,9 @@ dist-client = [ # Enables the sccache-dist binary dist-server = [ "jwt", + "env_filter", "flate2", + "hostname", "libmount", "nix", "openssl", diff --git a/docs/Distributed.md b/docs/Distributed.md index 9316d0905..4a3f9d453 100644 --- a/docs/Distributed.md +++ b/docs/Distributed.md @@ -427,6 +427,30 @@ token = "preshared token" type = "DANGEROUSLY_INSECURE" ``` +# Logging + +## stderr + +You can set the `SCCACHE_LOG` environment variable to a comma-separated list of directives that specifies the levels and modules desired. + +For example: +``` +SCCACHE_LOG=info,sccache_dist=trace,sccache=trace,sccache_heartbeat=off +``` + + +For more details, consult the [`env_logger`](https://docs.rs/env_logger/latest/env_logger/#enabling-logging) documentation. + +## Syslog + +Use the `--syslog` argument to enable logging to a syslog daemon. +The presence of the `SCCACHE_LOG` environment variable takes precedence over this argument. +The same syntax is accepted: + +``` +--syslog info,sccache_dist=debug,sccache=debug,sccache_heartbeat=off +``` + # Building the Distributed Server Binaries diff --git a/src/bin/sccache-dist/cmdline/parse.rs b/src/bin/sccache-dist/cmdline/parse.rs index 54a990fb5..ef20ceadc 100644 --- a/src/bin/sccache-dist/cmdline/parse.rs +++ b/src/bin/sccache-dist/cmdline/parse.rs @@ -13,15 +13,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{env, ffi::OsString, fmt, net::SocketAddr, path::PathBuf, str::FromStr}; +use std::{ + ffi::OsString, + fmt, + io::BufWriter, + net::{SocketAddr, TcpStream, UdpSocket}, + path::PathBuf, + process, +}; use anyhow::{anyhow, bail}; -use clap::{Arg, ArgGroup, Command as ClapCommand, ValueEnum}; +use clap::{Arg, ArgGroup, Command as ClapCommand}; use sccache::{config, dist::ServerId}; -use syslog::Facility; +use syslog::{unix, BasicLogger, Formatter3164, LoggerBackend}; +use syslog::{Facility, Logger}; use crate::cmdline::{AuthSubcommand, Command}; - #[derive(Debug, Clone)] struct TokenLength(usize); @@ -53,44 +60,6 @@ impl fmt::Display for TokenLength { } } -#[derive(Clone, Copy, ValueEnum)] -enum LogLevel { - Error, - Warn, - Info, - Debug, - Trace, -} - -impl FromStr for LogLevel { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let variant = match s { - "error" => Self::Error, - "warn" => Self::Warn, - "info" => Self::Info, - "debug" => Self::Debug, - "trace" => Self::Trace, - _ => bail!("Unknown log level: {:?}", s), - }; - - Ok(variant) - } -} - -impl From for log::LevelFilter { - fn from(log_level: LogLevel) -> Self { - match log_level { - LogLevel::Error => Self::Error, - LogLevel::Warn => Self::Warn, - LogLevel::Info => Self::Info, - LogLevel::Debug => Self::Debug, - LogLevel::Trace => Self::Trace, - } - } -} - fn flag_infer_long(name: &'static str) -> Arg { Arg::new(name).long(name) } @@ -98,8 +67,7 @@ fn flag_infer_long(name: &'static str) -> Arg { fn get_clap_command() -> ClapCommand { let syslog = flag_infer_long("syslog") .help("Log to the syslog with LEVEL") - .value_name("LEVEL") - .value_parser(clap::value_parser!(LogLevel)); + .value_name("LEVEL"); let config_with_help_message = |help: &'static str| { flag_infer_long("config") .help(help) @@ -155,9 +123,38 @@ fn get_clap_command() -> ClapCommand { ])) } -fn check_init_syslog(name: &str, log_level: LogLevel) { - let level = log::LevelFilter::from(log_level); - drop(syslog::init(Facility::LOG_DAEMON, level, Some(name))); +fn check_init_syslog(name: &str, log_filter: &str) { + let facility = Facility::LOG_DAEMON; + let process = name.to_string(); + let pid = process::id(); + let mut formatter = Formatter3164 { + facility, + hostname: None, + process, + pid, + }; + + let backend = if let Ok(logger) = unix(formatter.clone()) { + logger.backend + } else { + formatter.hostname = Some(hostname::get().unwrap().to_string_lossy().to_string()); + if let Ok(tcp_stream) = TcpStream::connect(("127.0.0.1", 601)) { + LoggerBackend::Tcp(BufWriter::new(tcp_stream)) + } else { + let udp_addr = "127.0.0.1:514".parse().unwrap(); + let udp_stream = UdpSocket::bind(("127.0.0.1", 0)).unwrap(); + LoggerBackend::Udp(udp_stream, udp_addr) + } + }; + + let mut builder = env_filter::Builder::new(); + builder.parse(log_filter); + let filter = builder.build(); + log::set_max_level(filter.filter()); + + let logger = + env_filter::FilteredLog::new(BasicLogger::new(Logger { formatter, backend }), filter); + drop(log::set_boxed_logger(Box::new(logger))); } /// Parse commandline `args` into a `Result` to execute. @@ -217,9 +214,9 @@ pub fn try_parse_from( Some(("scheduler", matches)) => { if matches.contains_id("syslog") { let log_level = matches - .get_one::("syslog") + .get_one::("syslog") .expect("`syslog` is required"); - check_init_syslog("sccache-scheduler", *log_level); + check_init_syslog("sccache-scheduler", log_level.as_str()); } let config_path = matches @@ -234,9 +231,9 @@ pub fn try_parse_from( Some(("server", matches)) => { if matches.contains_id("syslog") { let log_level = matches - .get_one::("syslog") + .get_one::("syslog") .expect("`syslog` is required"); - check_init_syslog("sccache-buildserver", *log_level); + check_init_syslog("sccache-buildserver", log_level.as_str()); } let config_path = matches