Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/dfx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ serde_cbor = "0.10"
serde_json = "1.0.40"
serde_repr = "0.1.5"
signal-hook = "0.1.13"
slog = "2.5.2"
slog-term = "2.5.0"
slog = { version = "2.5.2", features = ["max_level_trace"] }
slog-async = "2.4.0"
slog-term = "2.5.0"
sysinfo = "0.9.6"
tar = "0.4.26"
tempfile = "3.1.0"
Expand Down
71 changes: 71 additions & 0 deletions src/dfx/src/lib/logger.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
use crate::config::dfx_version_str;
use slog::{Drain, Level, Logger};
use slog_async;
use slog_term;
use std::fs::File;
use std::path::PathBuf;

/// The logging mode to use.
pub enum LoggingMode {
/// The default mode for logging; output without any decoration, to STDERR.
Stderr,

/// Tee logging to a file (in addition to STDERR). This mimics the verbose flag.
/// So it would be similar to `dfx ... |& tee /some/file.txt
Tee(PathBuf),

/// Output Debug logs and up to a file, regardless of verbosity, keep the STDERR output
/// the same (with verbosity).
File(PathBuf),
}

/// A Slog formatter that writes to a term decorator, without any formatting.
pub struct PlainFormat<D>
where
Expand Down Expand Up @@ -33,3 +54,53 @@ impl<D: slog_term::Decorator> slog::Drain for PlainFormat<D> {
})
}
}

/// Create a log drain.
fn create_drain(mode: LoggingMode) -> Logger {
match mode {
LoggingMode::Stderr => Logger::root(
PlainFormat::new(slog_term::PlainSyncDecorator::new(std::io::stderr())).fuse(),
slog::o!(),
),
LoggingMode::File(out) => {
let file = File::create(out).expect("Couldn't open log file");
let decorator = slog_term::PlainDecorator::new(file);
let drain = slog_term::FullFormat::new(decorator).build().fuse();
Logger::root(slog_async::Async::new(drain).build().fuse(), slog::o!())
}
// A Tee mode is basically 2 drains duplicated.
LoggingMode::Tee(out) => Logger::root(
slog::Duplicate::new(
create_drain(LoggingMode::Stderr),
create_drain(LoggingMode::File(out)),
)
.fuse(),
slog::o!(),
),
}
}

/// Create a root logger.
/// The verbose_level can be negative, in which case it's a quiet mode which removes warnings,
/// then errors entirely.
pub fn create_root_logger(verbose_level: i64, mode: LoggingMode) -> Logger {
let log_level = match verbose_level {
-3 => Level::Critical,
-2 => Level::Error,
-1 => Level::Warning,
0 => Level::Info,
1 => Level::Debug,
x => {
if x > 0 {
Level::Trace
} else {
return Logger::root(slog::Discard, slog::o!());
}
}
};

let drain = slog::LevelFilter::new(create_drain(mode), log_level).fuse();
let drain = slog_async::Async::new(drain).build().fuse();

Logger::root(drain, slog::o!("version" => dfx_version_str()))
}
66 changes: 33 additions & 33 deletions src/dfx/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ use crate::commands::CliCommand;
use crate::config::{dfx_version, dfx_version_str};
use crate::lib::environment::{Environment, EnvironmentImpl};
use crate::lib::error::*;
use clap::{App, AppSettings, Arg};
use crate::lib::logger::{create_root_logger, LoggingMode};
use clap::{App, AppSettings, Arg, ArgMatches};
use ic_http_agent::AgentError;
use slog;
use slog::Drain;
use slog_async;
use slog_term;
use std::path::PathBuf;

mod commands;
mod config;
Expand All @@ -31,6 +30,19 @@ fn cli(_: &impl Environment) -> App<'_, '_> {
.short("q")
.multiple(true),
)
.arg(
Arg::with_name("logmode")
.long("log")
.takes_value(true)
.possible_values(&["stderr", "tee", "file"])
.default_value("stderr"),
)
.arg(
Arg::with_name("logfile")
.long("log-file")
.long("logfile")
.takes_value(true),
)
.subcommands(
commands::builtin()
.into_iter()
Expand Down Expand Up @@ -111,31 +123,24 @@ fn maybe_redirect_dfx(env: &impl Environment) -> Option<()> {
None
}

/// Setup a logger with the proper configuration.
/// The verbose_level can be negative, in which case it's a quiet mode which removes warnings,
/// then errors entirely.
fn setup_logging(verbose_level: i64) -> slog::Logger {
let log_level = match verbose_level {
-3 => slog::Level::Critical,
-2 => slog::Level::Error,
-1 => slog::Level::Warning,
0 => slog::Level::Info,
1 => slog::Level::Debug,
x => {
if x > 0 {
slog::Level::Trace
} else {
return slog::Logger::root(slog::Discard, slog::o!());
}
}
/// Setup a logger with the proper configuration, based on arguments.
/// Returns a topple of whether or not to have a progress bar, and a logger.
fn setup_logging(matches: &ArgMatches<'_>) -> (bool, slog::Logger) {
// Create a logger with our argument matches.
let level = matches.occurrences_of("verbose") as i64 - matches.occurrences_of("quiet") as i64;

let mode = match matches.value_of("logmode") {
Some("tee") => LoggingMode::Tee(PathBuf::from(
matches.value_of("logfile").unwrap_or("log.txt"),
)),
Some("file") => LoggingMode::File(PathBuf::from(
matches.value_of("logfile").unwrap_or("log.txt"),
)),
_ => LoggingMode::Stderr,
};

let plain = slog_term::PlainSyncDecorator::new(std::io::stderr());
let drain = lib::logger::PlainFormat::new(plain).fuse();
let drain = slog::LevelFilter::new(drain, log_level).fuse();
let drain = slog_async::Async::new(drain).build().fuse();

slog::Logger::root(drain, slog::o!("version" => dfx_version_str()))
// Only show the progress bar if the level is INFO or more.
(level >= 0, create_root_logger(level, mode))
}

fn main() {
Expand All @@ -147,12 +152,7 @@ fn main() {

let matches = cli(&env).get_matches();

// Create a logger with our argument matches.
let level =
matches.occurrences_of("verbose") as i64 - matches.occurrences_of("quiet") as i64;
let log = setup_logging(level);
// Only show the progress bar if the level is INFO or more.
let progress_bar = level >= 0;
let (progress_bar, log) = setup_logging(&matches);

// Need to recreate the environment because we use it to get matches.
// TODO(hansl): resolve this double-create problem.
Expand Down