Skip to content

Commit

Permalink
standalone: allow overriding log level from CLI (#598)
Browse files Browse the repository at this point in the history
Currently, `propolis-standalone` hard-codes the maximum `slog` log level
to `Info`. Since `propolis-standalone` is intended for development use,
it may sometimes be useful to enable more verbose logging. Therefore,
this branch adds a `--log-level` CLI argument to allow overriding the
maximum log level filter. The `PROPOLIS_LOG` environment variable may
also be used to configure the log level.
  • Loading branch information
hawkw authored Jan 10, 2024
1 parent eb43ab3 commit 398dc5b
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 5 deletions.
2 changes: 1 addition & 1 deletion bin/propolis-standalone/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ doctest = false
anyhow.workspace = true
atty.workspace = true
bhyve_api.workspace = true
clap = { workspace = true, features = ["derive"] }
clap = { workspace = true, features = ["derive", "env"] }
ctrlc.workspace = true
fatfs.workspace = true
futures.workspace = true
Expand Down
47 changes: 43 additions & 4 deletions bin/propolis-standalone/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std::collections::VecDeque;
use std::fmt;
use std::fs::File;
use std::io::{Error, ErrorKind, Result};
use std::path::Path;
Expand Down Expand Up @@ -705,7 +706,7 @@ fn open_bootrom(path: &str) -> Result<(File, usize)> {
}
}

fn build_log() -> slog::Logger {
fn build_log(level: slog::Level) -> slog::Logger {
let main_drain = if atty::is(atty::Stream::Stdout) {
let decorator = slog_term::TermDecorator::new().build();
let drain = slog_term::CompactFormat::new(decorator).build().fuse();
Expand All @@ -724,7 +725,7 @@ fn build_log() -> slog::Logger {

let (dtrace_drain, probe_reg) = slog_dtrace::Dtrace::new();

let filtered_main = slog::LevelFilter::new(main_drain, slog::Level::Info);
let filtered_main = slog::LevelFilter::new(main_drain, level);

let log = slog::Logger::root(
slog::Duplicate::new(filtered_main.fuse(), dtrace_drain.fuse()).fuse(),
Expand Down Expand Up @@ -1028,15 +1029,25 @@ struct Args {
/// Restore previously captured snapshot.
#[clap(short, long, action)]
restore: bool,

/// Maximum log level filter.
#[clap(
long,
env = "PROPOLIS_LOG",
default_value_t = LogFilter(slog::Level::Info),
ignore_case(true),
)]
log_level: LogFilter,
}

fn main() -> anyhow::Result<ExitCode> {
let Args { target, snapshot, restore } = Args::parse();
let Args { target, snapshot, restore, log_level: LogFilter(log_level) } =
Args::parse();

// Ensure proper setup of USDT probes
register_probes().context("Failed to setup USDT probes")?;

let log = build_log();
let log = build_log(log_level);

// Check that vmm and viona device version match what we expect
api_version_checks(&log).context("API version checks")?;
Expand Down Expand Up @@ -1093,3 +1104,31 @@ fn main() -> anyhow::Result<ExitCode> {
// wait for instance to be destroyed
Ok(inst.wait_destroyed())
}

/// Wrapper around `slog::Level` to implement `clap::ValueEnum`, so that this
/// type can be parsed from the command line.
#[derive(Clone, Debug)]
struct LogFilter(slog::Level);

impl clap::ValueEnum for LogFilter {
fn value_variants<'a>() -> &'a [Self] {
&[
LogFilter(slog::Level::Critical),
LogFilter(slog::Level::Error),
LogFilter(slog::Level::Warning),
LogFilter(slog::Level::Info),
LogFilter(slog::Level::Debug),
LogFilter(slog::Level::Trace),
]
}

fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
Some(clap::builder::PossibleValue::new(self.0.as_str()))
}
}

impl fmt::Display for LogFilter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

0 comments on commit 398dc5b

Please sign in to comment.