Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
879
third-party/vendor/env_logger/src/filter/mod.rs
vendored
Normal file
879
third-party/vendor/env_logger/src/filter/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,879 @@
|
|||
//! Filtering for log records.
|
||||
//!
|
||||
//! This module contains the log filtering used by `env_logger` to match records.
|
||||
//! You can use the `Filter` type in your own logger implementation to use the same
|
||||
//! filter parsing and matching as `env_logger`. For more details about the format
|
||||
//! for directive strings see [Enabling Logging].
|
||||
//!
|
||||
//! ## Using `env_logger` in your own logger
|
||||
//!
|
||||
//! You can use `env_logger`'s filtering functionality with your own logger.
|
||||
//! Call [`Builder::parse`] to parse directives from a string when constructing
|
||||
//! your logger. Call [`Filter::matches`] to check whether a record should be
|
||||
//! logged based on the parsed filters when log records are received.
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate log;
|
||||
//! extern crate env_logger;
|
||||
//! use env_logger::filter::Filter;
|
||||
//! use log::{Log, Metadata, Record};
|
||||
//!
|
||||
//! struct MyLogger {
|
||||
//! filter: Filter
|
||||
//! }
|
||||
//!
|
||||
//! impl MyLogger {
|
||||
//! fn new() -> MyLogger {
|
||||
//! use env_logger::filter::Builder;
|
||||
//! let mut builder = Builder::new();
|
||||
//!
|
||||
//! // Parse a directives string from an environment variable
|
||||
//! if let Ok(ref filter) = std::env::var("MY_LOG_LEVEL") {
|
||||
//! builder.parse(filter);
|
||||
//! }
|
||||
//!
|
||||
//! MyLogger {
|
||||
//! filter: builder.build()
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl Log for MyLogger {
|
||||
//! fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
//! self.filter.enabled(metadata)
|
||||
//! }
|
||||
//!
|
||||
//! fn log(&self, record: &Record) {
|
||||
//! // Check if the record is matched by the filter
|
||||
//! if self.filter.matches(record) {
|
||||
//! println!("{:?}", record);
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn flush(&self) {}
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [Enabling Logging]: ../index.html#enabling-logging
|
||||
//! [`Builder::parse`]: struct.Builder.html#method.parse
|
||||
//! [`Filter::matches`]: struct.Filter.html#method.matches
|
||||
|
||||
use log::{Level, LevelFilter, Metadata, Record};
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
|
||||
#[cfg(feature = "regex")]
|
||||
#[path = "regex.rs"]
|
||||
mod inner;
|
||||
|
||||
#[cfg(not(feature = "regex"))]
|
||||
#[path = "string.rs"]
|
||||
mod inner;
|
||||
|
||||
/// A builder for a log filter.
|
||||
///
|
||||
/// It can be used to parse a set of directives from a string before building
|
||||
/// a [`Filter`] instance.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate log;
|
||||
/// # use std::env;
|
||||
/// use env_logger::filter::Builder;
|
||||
///
|
||||
/// let mut builder = Builder::new();
|
||||
///
|
||||
/// // Parse a logging filter from an environment variable.
|
||||
/// if let Ok(rust_log) = env::var("RUST_LOG") {
|
||||
/// builder.parse(&rust_log);
|
||||
/// }
|
||||
///
|
||||
/// let filter = builder.build();
|
||||
/// ```
|
||||
///
|
||||
/// [`Filter`]: struct.Filter.html
|
||||
pub struct Builder {
|
||||
directives: Vec<Directive>,
|
||||
filter: Option<inner::Filter>,
|
||||
built: bool,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Initializes the filter builder with defaults.
|
||||
pub fn new() -> Builder {
|
||||
Builder {
|
||||
directives: Vec::new(),
|
||||
filter: None,
|
||||
built: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the filter builder from an environment.
|
||||
pub fn from_env(env: &str) -> Builder {
|
||||
let mut builder = Builder::new();
|
||||
|
||||
if let Ok(s) = env::var(env) {
|
||||
builder.parse(&s);
|
||||
}
|
||||
|
||||
builder
|
||||
}
|
||||
|
||||
/// Insert the directive replacing any directive with the same name.
|
||||
fn insert_directive(&mut self, mut directive: Directive) {
|
||||
if let Some(pos) = self
|
||||
.directives
|
||||
.iter()
|
||||
.position(|d| d.name == directive.name)
|
||||
{
|
||||
mem::swap(&mut self.directives[pos], &mut directive);
|
||||
} else {
|
||||
self.directives.push(directive);
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a directive to the filter for a specific module.
|
||||
pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
|
||||
self.filter(Some(module), level)
|
||||
}
|
||||
|
||||
/// Adds a directive to the filter for all modules.
|
||||
pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
|
||||
self.filter(None, level)
|
||||
}
|
||||
|
||||
/// Adds a directive to the filter.
|
||||
///
|
||||
/// The given module (if any) will log at most the specified level provided.
|
||||
/// If no module is provided then the filter will apply to all log messages.
|
||||
pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
|
||||
self.insert_directive(Directive {
|
||||
name: module.map(|s| s.to_string()),
|
||||
level,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Parses the directives string.
|
||||
///
|
||||
/// See the [Enabling Logging] section for more details.
|
||||
///
|
||||
/// [Enabling Logging]: ../index.html#enabling-logging
|
||||
pub fn parse(&mut self, filters: &str) -> &mut Self {
|
||||
let (directives, filter) = parse_spec(filters);
|
||||
|
||||
self.filter = filter;
|
||||
|
||||
for directive in directives {
|
||||
self.insert_directive(directive);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Build a log filter.
|
||||
pub fn build(&mut self) -> Filter {
|
||||
assert!(!self.built, "attempt to re-use consumed builder");
|
||||
self.built = true;
|
||||
|
||||
let mut directives = Vec::new();
|
||||
if self.directives.is_empty() {
|
||||
// Adds the default filter if none exist
|
||||
directives.push(Directive {
|
||||
name: None,
|
||||
level: LevelFilter::Error,
|
||||
});
|
||||
} else {
|
||||
// Consume directives.
|
||||
directives = mem::take(&mut self.directives);
|
||||
// Sort the directives by length of their name, this allows a
|
||||
// little more efficient lookup at runtime.
|
||||
directives.sort_by(|a, b| {
|
||||
let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
|
||||
let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
|
||||
alen.cmp(&blen)
|
||||
});
|
||||
}
|
||||
|
||||
Filter {
|
||||
directives: mem::take(&mut directives),
|
||||
filter: mem::take(&mut self.filter),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
Builder::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Builder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.built {
|
||||
f.debug_struct("Filter").field("built", &true).finish()
|
||||
} else {
|
||||
f.debug_struct("Filter")
|
||||
.field("filter", &self.filter)
|
||||
.field("directives", &self.directives)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Directive {
|
||||
name: Option<String>,
|
||||
level: LevelFilter,
|
||||
}
|
||||
|
||||
/// A log filter.
|
||||
///
|
||||
/// This struct can be used to determine whether or not a log record
|
||||
/// should be written to the output.
|
||||
/// Use the [`Builder`] type to parse and construct a `Filter`.
|
||||
///
|
||||
/// [`Builder`]: struct.Builder.html
|
||||
pub struct Filter {
|
||||
directives: Vec<Directive>,
|
||||
filter: Option<inner::Filter>,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
/// Returns the maximum `LevelFilter` that this filter instance is
|
||||
/// configured to output.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use log::LevelFilter;
|
||||
/// use env_logger::filter::Builder;
|
||||
///
|
||||
/// let mut builder = Builder::new();
|
||||
/// builder.filter(Some("module1"), LevelFilter::Info);
|
||||
/// builder.filter(Some("module2"), LevelFilter::Error);
|
||||
///
|
||||
/// let filter = builder.build();
|
||||
/// assert_eq!(filter.filter(), LevelFilter::Info);
|
||||
/// ```
|
||||
pub fn filter(&self) -> LevelFilter {
|
||||
self.directives
|
||||
.iter()
|
||||
.map(|d| d.level)
|
||||
.max()
|
||||
.unwrap_or(LevelFilter::Off)
|
||||
}
|
||||
|
||||
/// Checks if this record matches the configured filter.
|
||||
pub fn matches(&self, record: &Record) -> bool {
|
||||
if !self.enabled(record.metadata()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(filter) = self.filter.as_ref() {
|
||||
if !filter.is_match(&record.args().to_string()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Determines if a log message with the specified metadata would be logged.
|
||||
pub fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
let level = metadata.level();
|
||||
let target = metadata.target();
|
||||
|
||||
enabled(&self.directives, level, target)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Filter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Filter")
|
||||
.field("filter", &self.filter)
|
||||
.field("directives", &self.directives)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo")
|
||||
/// and return a vector with log directives.
|
||||
fn parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>) {
|
||||
let mut dirs = Vec::new();
|
||||
|
||||
let mut parts = spec.split('/');
|
||||
let mods = parts.next();
|
||||
let filter = parts.next();
|
||||
if parts.next().is_some() {
|
||||
eprintln!(
|
||||
"warning: invalid logging spec '{}', \
|
||||
ignoring it (too many '/'s)",
|
||||
spec
|
||||
);
|
||||
return (dirs, None);
|
||||
}
|
||||
if let Some(m) = mods {
|
||||
for s in m.split(',').map(|ss| ss.trim()) {
|
||||
if s.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut parts = s.split('=');
|
||||
let (log_level, name) =
|
||||
match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
|
||||
(Some(part0), None, None) => {
|
||||
// if the single argument is a log-level string or number,
|
||||
// treat that as a global fallback
|
||||
match part0.parse() {
|
||||
Ok(num) => (num, None),
|
||||
Err(_) => (LevelFilter::max(), Some(part0)),
|
||||
}
|
||||
}
|
||||
(Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)),
|
||||
(Some(part0), Some(part1), None) => match part1.parse() {
|
||||
Ok(num) => (num, Some(part0)),
|
||||
_ => {
|
||||
eprintln!(
|
||||
"warning: invalid logging spec '{}', \
|
||||
ignoring it",
|
||||
part1
|
||||
);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
eprintln!(
|
||||
"warning: invalid logging spec '{}', \
|
||||
ignoring it",
|
||||
s
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
dirs.push(Directive {
|
||||
name: name.map(|s| s.to_string()),
|
||||
level: log_level,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let filter = filter.and_then(|filter| match inner::Filter::new(filter) {
|
||||
Ok(re) => Some(re),
|
||||
Err(e) => {
|
||||
eprintln!("warning: invalid regex filter - {}", e);
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
(dirs, filter)
|
||||
}
|
||||
|
||||
// Check whether a level and target are enabled by the set of directives.
|
||||
fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
|
||||
// Search for the longest match, the vector is assumed to be pre-sorted.
|
||||
for directive in directives.iter().rev() {
|
||||
match directive.name {
|
||||
Some(ref name) if !target.starts_with(&**name) => {}
|
||||
Some(..) | None => return level <= directive.level,
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use log::{Level, LevelFilter};
|
||||
|
||||
use super::{enabled, parse_spec, Builder, Directive, Filter};
|
||||
|
||||
fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
|
||||
let mut logger = Builder::new().build();
|
||||
logger.directives = dirs;
|
||||
logger
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_info() {
|
||||
let logger = Builder::new().filter(None, LevelFilter::Info).build();
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate1"));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_beginning_longest_match() {
|
||||
let logger = Builder::new()
|
||||
.filter(Some("crate2"), LevelFilter::Info)
|
||||
.filter(Some("crate2::mod"), LevelFilter::Debug)
|
||||
.filter(Some("crate1::mod1"), LevelFilter::Warn)
|
||||
.build();
|
||||
assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
|
||||
}
|
||||
|
||||
// Some of our tests are only correct or complete when they cover the full
|
||||
// universe of variants for log::Level. In the unlikely event that a new
|
||||
// variant is added in the future, this test will detect the scenario and
|
||||
// alert us to the need to review and update the tests. In such a
|
||||
// situation, this test will fail to compile, and the error message will
|
||||
// look something like this:
|
||||
//
|
||||
// error[E0004]: non-exhaustive patterns: `NewVariant` not covered
|
||||
// --> src/filter/mod.rs:413:15
|
||||
// |
|
||||
// 413 | match level_universe {
|
||||
// | ^^^^^^^^^^^^^^ pattern `NewVariant` not covered
|
||||
#[test]
|
||||
fn ensure_tests_cover_level_universe() {
|
||||
let level_universe: Level = Level::Trace; // use of trace variant is arbitrary
|
||||
match level_universe {
|
||||
Level::Error | Level::Warn | Level::Info | Level::Debug | Level::Trace => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default() {
|
||||
let logger = Builder::new().parse("info,crate1::mod1=warn").build();
|
||||
assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_off_lc() {
|
||||
let logger = Builder::new().parse("off").build();
|
||||
assert!(!enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_off_uc() {
|
||||
let logger = Builder::new().parse("OFF").build();
|
||||
assert!(!enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_error_lc() {
|
||||
let logger = Builder::new().parse("error").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_error_uc() {
|
||||
let logger = Builder::new().parse("ERROR").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_warn_lc() {
|
||||
let logger = Builder::new().parse("warn").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_warn_uc() {
|
||||
let logger = Builder::new().parse("WARN").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_info_lc() {
|
||||
let logger = Builder::new().parse("info").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_info_uc() {
|
||||
let logger = Builder::new().parse("INFO").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_debug_lc() {
|
||||
let logger = Builder::new().parse("debug").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_debug_uc() {
|
||||
let logger = Builder::new().parse("DEBUG").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_trace_lc() {
|
||||
let logger = Builder::new().parse("trace").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default_bare_level_trace_uc() {
|
||||
let logger = Builder::new().parse("TRACE").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
|
||||
// In practice, the desired log level is typically specified by a token
|
||||
// that is either all lowercase (e.g., 'trace') or all uppercase (.e.g,
|
||||
// 'TRACE'), but this tests serves as a reminder that
|
||||
// log::Level::from_str() ignores all case variants.
|
||||
#[test]
|
||||
fn parse_default_bare_level_debug_mixed() {
|
||||
{
|
||||
let logger = Builder::new().parse("Debug").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
{
|
||||
let logger = Builder::new().parse("debuG").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
{
|
||||
let logger = Builder::new().parse("deBug").build();
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
{
|
||||
let logger = Builder::new().parse("DeBuG").build(); // LaTeX flavor!
|
||||
assert!(enabled(&logger.directives, Level::Error, ""));
|
||||
assert!(enabled(&logger.directives, Level::Warn, ""));
|
||||
assert!(enabled(&logger.directives, Level::Info, ""));
|
||||
assert!(enabled(&logger.directives, Level::Debug, ""));
|
||||
assert!(!enabled(&logger.directives, Level::Trace, ""));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_full_path() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: Some("crate2".to_string()),
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Warn,
|
||||
},
|
||||
]);
|
||||
assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
|
||||
assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate2"));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_match() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: Some("crate2".to_string()),
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Warn,
|
||||
},
|
||||
]);
|
||||
assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_beginning() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: Some("crate2".to_string()),
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Warn,
|
||||
},
|
||||
]);
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_beginning_longest_match() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: Some("crate2".to_string()),
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate2::mod".to_string()),
|
||||
level: LevelFilter::Debug,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Warn,
|
||||
},
|
||||
]);
|
||||
assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_default() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: None,
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Warn,
|
||||
},
|
||||
]);
|
||||
assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_level() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: None,
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Off,
|
||||
},
|
||||
]);
|
||||
assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_valid() {
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
|
||||
assert_eq!(dirs.len(), 3);
|
||||
assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Error);
|
||||
|
||||
assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
|
||||
assert_eq!(dirs[1].level, LevelFilter::max());
|
||||
|
||||
assert_eq!(dirs[2].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[2].level, LevelFilter::Debug);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_invalid_crate() {
|
||||
// test parse_spec with multiple = in specification
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Debug);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_invalid_level() {
|
||||
// test parse_spec with 'noNumber' as log level
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Debug);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_string_level() {
|
||||
// test parse_spec with 'warn' as log level
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Warn);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_empty_level() {
|
||||
// test parse_spec with '' as log level
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::max());
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_empty_level_isolated() {
|
||||
// test parse_spec with "" as log level (and the entire spec str)
|
||||
let (dirs, filter) = parse_spec(""); // should be ignored
|
||||
assert_eq!(dirs.len(), 0);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_blank_level_isolated() {
|
||||
// test parse_spec with a white-space-only string specified as the log
|
||||
// level (and the entire spec str)
|
||||
let (dirs, filter) = parse_spec(" "); // should be ignored
|
||||
assert_eq!(dirs.len(), 0);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_blank_level_isolated_comma_only() {
|
||||
// The spec should contain zero or more comma-separated string slices,
|
||||
// so a comma-only string should be interpreted as two empty strings
|
||||
// (which should both be treated as invalid, so ignored).
|
||||
let (dirs, filter) = parse_spec(","); // should be ignored
|
||||
assert_eq!(dirs.len(), 0);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_blank_level_isolated_comma_blank() {
|
||||
// The spec should contain zero or more comma-separated string slices,
|
||||
// so this bogus spec should be interpreted as containing one empty
|
||||
// string and one blank string. Both should both be treated as
|
||||
// invalid, so ignored.
|
||||
let (dirs, filter) = parse_spec(", "); // should be ignored
|
||||
assert_eq!(dirs.len(), 0);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_blank_level_isolated_blank_comma() {
|
||||
// The spec should contain zero or more comma-separated string slices,
|
||||
// so this bogus spec should be interpreted as containing one blank
|
||||
// string and one empty string. Both should both be treated as
|
||||
// invalid, so ignored.
|
||||
let (dirs, filter) = parse_spec(" ,"); // should be ignored
|
||||
assert_eq!(dirs.len(), 0);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_global() {
|
||||
// test parse_spec with no crate
|
||||
let (dirs, filter) = parse_spec("warn,crate2=debug");
|
||||
assert_eq!(dirs.len(), 2);
|
||||
assert_eq!(dirs[0].name, None);
|
||||
assert_eq!(dirs[0].level, LevelFilter::Warn);
|
||||
assert_eq!(dirs[1].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[1].level, LevelFilter::Debug);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_global_bare_warn_lc() {
|
||||
// test parse_spec with no crate, in isolation, all lowercase
|
||||
let (dirs, filter) = parse_spec("warn");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, None);
|
||||
assert_eq!(dirs[0].level, LevelFilter::Warn);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_global_bare_warn_uc() {
|
||||
// test parse_spec with no crate, in isolation, all uppercase
|
||||
let (dirs, filter) = parse_spec("WARN");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, None);
|
||||
assert_eq!(dirs[0].level, LevelFilter::Warn);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_global_bare_warn_mixed() {
|
||||
// test parse_spec with no crate, in isolation, mixed case
|
||||
let (dirs, filter) = parse_spec("wArN");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, None);
|
||||
assert_eq!(dirs[0].level, LevelFilter::Warn);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_valid_filter() {
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc");
|
||||
assert_eq!(dirs.len(), 3);
|
||||
assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Error);
|
||||
|
||||
assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
|
||||
assert_eq!(dirs[1].level, LevelFilter::max());
|
||||
|
||||
assert_eq!(dirs[2].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[2].level, LevelFilter::Debug);
|
||||
assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_invalid_crate_filter() {
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Debug);
|
||||
assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_empty_with_filter() {
|
||||
let (dirs, filter) = parse_spec("crate1/a*c");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate1".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::max());
|
||||
assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
|
||||
}
|
||||
}
|
||||
29
third-party/vendor/env_logger/src/filter/regex.rs
vendored
Normal file
29
third-party/vendor/env_logger/src/filter/regex.rs
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
extern crate regex;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use self::regex::Regex;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Filter {
|
||||
inner: Regex,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn new(spec: &str) -> Result<Filter, String> {
|
||||
match Regex::new(spec) {
|
||||
Ok(r) => Ok(Filter { inner: r }),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_match(&self, s: &str) -> bool {
|
||||
self.inner.is_match(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Filter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
24
third-party/vendor/env_logger/src/filter/string.rs
vendored
Normal file
24
third-party/vendor/env_logger/src/filter/string.rs
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Filter {
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn new(spec: &str) -> Result<Filter, String> {
|
||||
Ok(Filter {
|
||||
inner: spec.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_match(&self, s: &str) -> bool {
|
||||
s.contains(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Filter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
114
third-party/vendor/env_logger/src/fmt/humantime.rs
vendored
Normal file
114
third-party/vendor/env_logger/src/fmt/humantime.rs
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
use std::fmt;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use humantime::{
|
||||
format_rfc3339_micros, format_rfc3339_millis, format_rfc3339_nanos, format_rfc3339_seconds,
|
||||
};
|
||||
|
||||
use crate::fmt::{Formatter, TimestampPrecision};
|
||||
|
||||
impl Formatter {
|
||||
/// Get a [`Timestamp`] for the current date and time in UTC.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Include the current timestamp with the log record:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let ts = buf.timestamp();
|
||||
///
|
||||
/// writeln!(buf, "{}: {}: {}", ts, record.level(), record.args())
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// [`Timestamp`]: struct.Timestamp.html
|
||||
pub fn timestamp(&self) -> Timestamp {
|
||||
Timestamp {
|
||||
time: SystemTime::now(),
|
||||
precision: TimestampPrecision::Seconds,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a [`Timestamp`] for the current date and time in UTC with full
|
||||
/// second precision.
|
||||
pub fn timestamp_seconds(&self) -> Timestamp {
|
||||
Timestamp {
|
||||
time: SystemTime::now(),
|
||||
precision: TimestampPrecision::Seconds,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a [`Timestamp`] for the current date and time in UTC with
|
||||
/// millisecond precision.
|
||||
pub fn timestamp_millis(&self) -> Timestamp {
|
||||
Timestamp {
|
||||
time: SystemTime::now(),
|
||||
precision: TimestampPrecision::Millis,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a [`Timestamp`] for the current date and time in UTC with
|
||||
/// microsecond precision.
|
||||
pub fn timestamp_micros(&self) -> Timestamp {
|
||||
Timestamp {
|
||||
time: SystemTime::now(),
|
||||
precision: TimestampPrecision::Micros,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a [`Timestamp`] for the current date and time in UTC with
|
||||
/// nanosecond precision.
|
||||
pub fn timestamp_nanos(&self) -> Timestamp {
|
||||
Timestamp {
|
||||
time: SystemTime::now(),
|
||||
precision: TimestampPrecision::Nanos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [RFC3339] formatted timestamp.
|
||||
///
|
||||
/// The timestamp implements [`Display`] and can be written to a [`Formatter`].
|
||||
///
|
||||
/// [RFC3339]: https://www.ietf.org/rfc/rfc3339.txt
|
||||
/// [`Display`]: https://doc.rust-lang.org/stable/std/fmt/trait.Display.html
|
||||
/// [`Formatter`]: struct.Formatter.html
|
||||
pub struct Timestamp {
|
||||
time: SystemTime,
|
||||
precision: TimestampPrecision,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Timestamp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// A `Debug` wrapper for `Timestamp` that uses the `Display` implementation.
|
||||
struct TimestampValue<'a>(&'a Timestamp);
|
||||
|
||||
impl<'a> fmt::Debug for TimestampValue<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
f.debug_tuple("Timestamp")
|
||||
.field(&TimestampValue(self))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Timestamp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let formatter = match self.precision {
|
||||
TimestampPrecision::Seconds => format_rfc3339_seconds,
|
||||
TimestampPrecision::Millis => format_rfc3339_millis,
|
||||
TimestampPrecision::Micros => format_rfc3339_micros,
|
||||
TimestampPrecision::Nanos => format_rfc3339_nanos,
|
||||
};
|
||||
|
||||
formatter(self.time).fmt(f)
|
||||
}
|
||||
}
|
||||
717
third-party/vendor/env_logger/src/fmt/mod.rs
vendored
Normal file
717
third-party/vendor/env_logger/src/fmt/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,717 @@
|
|||
//! Formatting for log records.
|
||||
//!
|
||||
//! This module contains a [`Formatter`] that can be used to format log records
|
||||
//! into without needing temporary allocations. Usually you won't need to worry
|
||||
//! about the contents of this module and can use the `Formatter` like an ordinary
|
||||
//! [`Write`].
|
||||
//!
|
||||
//! # Formatting log records
|
||||
//!
|
||||
//! The format used to print log records can be customised using the [`Builder::format`]
|
||||
//! method.
|
||||
//! Custom formats can apply different color and weight to printed values using
|
||||
//! [`Style`] builders.
|
||||
//!
|
||||
//! ```
|
||||
//! use std::io::Write;
|
||||
//!
|
||||
//! let mut builder = env_logger::Builder::new();
|
||||
//!
|
||||
//! builder.format(|buf, record| {
|
||||
//! writeln!(buf, "{}: {}",
|
||||
//! record.level(),
|
||||
//! record.args())
|
||||
//! });
|
||||
//! ```
|
||||
//!
|
||||
//! [`Formatter`]: struct.Formatter.html
|
||||
//! [`Style`]: struct.Style.html
|
||||
//! [`Builder::format`]: ../struct.Builder.html#method.format
|
||||
//! [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::Display;
|
||||
use std::io::prelude::*;
|
||||
use std::rc::Rc;
|
||||
use std::{fmt, io, mem};
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
use log::Level;
|
||||
use log::Record;
|
||||
|
||||
#[cfg(feature = "humantime")]
|
||||
mod humantime;
|
||||
pub(crate) mod writer;
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
mod style;
|
||||
#[cfg(feature = "color")]
|
||||
pub use style::{Color, Style, StyledValue};
|
||||
|
||||
#[cfg(feature = "humantime")]
|
||||
pub use self::humantime::Timestamp;
|
||||
pub use self::writer::glob::*;
|
||||
|
||||
use self::writer::{Buffer, Writer};
|
||||
|
||||
pub(crate) mod glob {
|
||||
pub use super::{Target, TimestampPrecision, WriteStyle};
|
||||
}
|
||||
|
||||
/// Formatting precision of timestamps.
|
||||
///
|
||||
/// Seconds give precision of full seconds, milliseconds give thousands of a
|
||||
/// second (3 decimal digits), microseconds are millionth of a second (6 decimal
|
||||
/// digits) and nanoseconds are billionth of a second (9 decimal digits).
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum TimestampPrecision {
|
||||
/// Full second precision (0 decimal digits)
|
||||
Seconds,
|
||||
/// Millisecond precision (3 decimal digits)
|
||||
Millis,
|
||||
/// Microsecond precision (6 decimal digits)
|
||||
Micros,
|
||||
/// Nanosecond precision (9 decimal digits)
|
||||
Nanos,
|
||||
}
|
||||
|
||||
/// The default timestamp precision is seconds.
|
||||
impl Default for TimestampPrecision {
|
||||
fn default() -> Self {
|
||||
TimestampPrecision::Seconds
|
||||
}
|
||||
}
|
||||
|
||||
/// A formatter to write logs into.
|
||||
///
|
||||
/// `Formatter` implements the standard [`Write`] trait for writing log records.
|
||||
/// It also supports terminal colors, through the [`style`] method.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Use the [`writeln`] macro to format a log record.
|
||||
/// An instance of a `Formatter` is passed to an `env_logger` format as `buf`:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()));
|
||||
/// ```
|
||||
///
|
||||
/// [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
|
||||
/// [`writeln`]: https://doc.rust-lang.org/stable/std/macro.writeln.html
|
||||
/// [`style`]: #method.style
|
||||
pub struct Formatter {
|
||||
buf: Rc<RefCell<Buffer>>,
|
||||
write_style: WriteStyle,
|
||||
}
|
||||
|
||||
impl Formatter {
|
||||
pub(crate) fn new(writer: &Writer) -> Self {
|
||||
Formatter {
|
||||
buf: Rc::new(RefCell::new(writer.buffer())),
|
||||
write_style: writer.write_style(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn write_style(&self) -> WriteStyle {
|
||||
self.write_style
|
||||
}
|
||||
|
||||
pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> {
|
||||
writer.print(&self.buf.borrow())
|
||||
}
|
||||
|
||||
pub(crate) fn clear(&mut self) {
|
||||
self.buf.borrow_mut().clear()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
impl Formatter {
|
||||
/// Begin a new [`Style`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a bold, red colored style and use it to print the log level:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut level_style = buf.style();
|
||||
///
|
||||
/// level_style.set_color(Color::Red).set_bold(true);
|
||||
///
|
||||
/// writeln!(buf, "{}: {}",
|
||||
/// level_style.value(record.level()),
|
||||
/// record.args())
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// [`Style`]: struct.Style.html
|
||||
pub fn style(&self) -> Style {
|
||||
Style {
|
||||
buf: self.buf.clone(),
|
||||
spec: termcolor::ColorSpec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the default [`Style`] for the given level.
|
||||
///
|
||||
/// The style can be used to print other values besides the level.
|
||||
pub fn default_level_style(&self, level: Level) -> Style {
|
||||
let mut level_style = self.style();
|
||||
match level {
|
||||
Level::Trace => level_style.set_color(Color::Cyan),
|
||||
Level::Debug => level_style.set_color(Color::Blue),
|
||||
Level::Info => level_style.set_color(Color::Green),
|
||||
Level::Warn => level_style.set_color(Color::Yellow),
|
||||
Level::Error => level_style.set_color(Color::Red).set_bold(true),
|
||||
};
|
||||
level_style
|
||||
}
|
||||
|
||||
/// Get a printable [`Style`] for the given level.
|
||||
///
|
||||
/// The style can only be used to print the level.
|
||||
pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> {
|
||||
self.default_level_style(level).into_value(level)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Formatter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.buf.borrow_mut().write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.buf.borrow_mut().flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Formatter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Formatter").finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type FormatFn = Box<dyn Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send>;
|
||||
|
||||
pub(crate) struct Builder {
|
||||
pub format_timestamp: Option<TimestampPrecision>,
|
||||
pub format_module_path: bool,
|
||||
pub format_target: bool,
|
||||
pub format_level: bool,
|
||||
pub format_indent: Option<usize>,
|
||||
pub custom_format: Option<FormatFn>,
|
||||
pub format_suffix: &'static str,
|
||||
built: bool,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Convert the format into a callable function.
|
||||
///
|
||||
/// If the `custom_format` is `Some`, then any `default_format` switches are ignored.
|
||||
/// If the `custom_format` is `None`, then a default format is returned.
|
||||
/// Any `default_format` switches set to `false` won't be written by the format.
|
||||
pub fn build(&mut self) -> FormatFn {
|
||||
assert!(!self.built, "attempt to re-use consumed builder");
|
||||
|
||||
let built = mem::replace(
|
||||
self,
|
||||
Builder {
|
||||
built: true,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(fmt) = built.custom_format {
|
||||
fmt
|
||||
} else {
|
||||
Box::new(move |buf, record| {
|
||||
let fmt = DefaultFormat {
|
||||
timestamp: built.format_timestamp,
|
||||
module_path: built.format_module_path,
|
||||
target: built.format_target,
|
||||
level: built.format_level,
|
||||
written_header_value: false,
|
||||
indent: built.format_indent,
|
||||
suffix: built.format_suffix,
|
||||
buf,
|
||||
};
|
||||
|
||||
fmt.write(record)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
Builder {
|
||||
format_timestamp: Some(Default::default()),
|
||||
format_module_path: false,
|
||||
format_target: true,
|
||||
format_level: true,
|
||||
format_indent: Some(4),
|
||||
custom_format: None,
|
||||
format_suffix: "\n",
|
||||
built: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
type SubtleStyle = StyledValue<'static, &'static str>;
|
||||
#[cfg(not(feature = "color"))]
|
||||
type SubtleStyle = &'static str;
|
||||
|
||||
/// The default format.
|
||||
///
|
||||
/// This format needs to work with any combination of crate features.
|
||||
struct DefaultFormat<'a> {
|
||||
timestamp: Option<TimestampPrecision>,
|
||||
module_path: bool,
|
||||
target: bool,
|
||||
level: bool,
|
||||
written_header_value: bool,
|
||||
indent: Option<usize>,
|
||||
buf: &'a mut Formatter,
|
||||
suffix: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> DefaultFormat<'a> {
|
||||
fn write(mut self, record: &Record) -> io::Result<()> {
|
||||
self.write_timestamp()?;
|
||||
self.write_level(record)?;
|
||||
self.write_module_path(record)?;
|
||||
self.write_target(record)?;
|
||||
self.finish_header()?;
|
||||
|
||||
self.write_args(record)
|
||||
}
|
||||
|
||||
fn subtle_style(&self, text: &'static str) -> SubtleStyle {
|
||||
#[cfg(feature = "color")]
|
||||
{
|
||||
self.buf
|
||||
.style()
|
||||
.set_color(Color::Black)
|
||||
.set_intense(true)
|
||||
.clone()
|
||||
.into_value(text)
|
||||
}
|
||||
#[cfg(not(feature = "color"))]
|
||||
{
|
||||
text
|
||||
}
|
||||
}
|
||||
|
||||
fn write_header_value<T>(&mut self, value: T) -> io::Result<()>
|
||||
where
|
||||
T: Display,
|
||||
{
|
||||
if !self.written_header_value {
|
||||
self.written_header_value = true;
|
||||
|
||||
let open_brace = self.subtle_style("[");
|
||||
write!(self.buf, "{}{}", open_brace, value)
|
||||
} else {
|
||||
write!(self.buf, " {}", value)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_level(&mut self, record: &Record) -> io::Result<()> {
|
||||
if !self.level {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let level = {
|
||||
#[cfg(feature = "color")]
|
||||
{
|
||||
self.buf.default_styled_level(record.level())
|
||||
}
|
||||
#[cfg(not(feature = "color"))]
|
||||
{
|
||||
record.level()
|
||||
}
|
||||
};
|
||||
|
||||
self.write_header_value(format_args!("{:<5}", level))
|
||||
}
|
||||
|
||||
fn write_timestamp(&mut self) -> io::Result<()> {
|
||||
#[cfg(feature = "humantime")]
|
||||
{
|
||||
use self::TimestampPrecision::*;
|
||||
let ts = match self.timestamp {
|
||||
None => return Ok(()),
|
||||
Some(Seconds) => self.buf.timestamp_seconds(),
|
||||
Some(Millis) => self.buf.timestamp_millis(),
|
||||
Some(Micros) => self.buf.timestamp_micros(),
|
||||
Some(Nanos) => self.buf.timestamp_nanos(),
|
||||
};
|
||||
|
||||
self.write_header_value(ts)
|
||||
}
|
||||
#[cfg(not(feature = "humantime"))]
|
||||
{
|
||||
// Trick the compiler to think we have used self.timestamp
|
||||
// Workaround for "field is never used: `timestamp`" compiler nag.
|
||||
let _ = self.timestamp;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_module_path(&mut self, record: &Record) -> io::Result<()> {
|
||||
if !self.module_path {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(module_path) = record.module_path() {
|
||||
self.write_header_value(module_path)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_target(&mut self, record: &Record) -> io::Result<()> {
|
||||
if !self.target {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match record.target() {
|
||||
"" => Ok(()),
|
||||
target => self.write_header_value(target),
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_header(&mut self) -> io::Result<()> {
|
||||
if self.written_header_value {
|
||||
let close_brace = self.subtle_style("]");
|
||||
write!(self.buf, "{} ", close_brace)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_args(&mut self, record: &Record) -> io::Result<()> {
|
||||
match self.indent {
|
||||
// Fast path for no indentation
|
||||
None => write!(self.buf, "{}{}", record.args(), self.suffix),
|
||||
|
||||
Some(indent_count) => {
|
||||
// Create a wrapper around the buffer only if we have to actually indent the message
|
||||
|
||||
struct IndentWrapper<'a, 'b: 'a> {
|
||||
fmt: &'a mut DefaultFormat<'b>,
|
||||
indent_count: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Write for IndentWrapper<'a, 'b> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let mut first = true;
|
||||
for chunk in buf.split(|&x| x == b'\n') {
|
||||
if !first {
|
||||
write!(
|
||||
self.fmt.buf,
|
||||
"{}{:width$}",
|
||||
self.fmt.suffix,
|
||||
"",
|
||||
width = self.indent_count
|
||||
)?;
|
||||
}
|
||||
self.fmt.buf.write_all(chunk)?;
|
||||
first = false;
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.fmt.buf.flush()
|
||||
}
|
||||
}
|
||||
|
||||
// The explicit scope here is just to make older versions of Rust happy
|
||||
{
|
||||
let mut wrapper = IndentWrapper {
|
||||
fmt: self,
|
||||
indent_count,
|
||||
};
|
||||
write!(wrapper, "{}", record.args())?;
|
||||
}
|
||||
|
||||
write!(self.buf, "{}", self.suffix)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use log::{Level, Record};
|
||||
|
||||
fn write_record(record: Record, fmt: DefaultFormat) -> String {
|
||||
let buf = fmt.buf.buf.clone();
|
||||
|
||||
fmt.write(&record).expect("failed to write record");
|
||||
|
||||
let buf = buf.borrow();
|
||||
String::from_utf8(buf.as_bytes().to_vec()).expect("failed to read record")
|
||||
}
|
||||
|
||||
fn write_target(target: &str, fmt: DefaultFormat) -> String {
|
||||
write_record(
|
||||
Record::builder()
|
||||
.args(format_args!("log\nmessage"))
|
||||
.level(Level::Info)
|
||||
.file(Some("test.rs"))
|
||||
.line(Some(144))
|
||||
.module_path(Some("test::path"))
|
||||
.target(target)
|
||||
.build(),
|
||||
fmt,
|
||||
)
|
||||
}
|
||||
|
||||
fn write(fmt: DefaultFormat) -> String {
|
||||
write_target("", fmt)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_with_header() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: true,
|
||||
target: false,
|
||||
level: true,
|
||||
written_header_value: false,
|
||||
indent: None,
|
||||
suffix: "\n",
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("[INFO test::path] log\nmessage\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_no_header() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: false,
|
||||
target: false,
|
||||
level: false,
|
||||
written_header_value: false,
|
||||
indent: None,
|
||||
suffix: "\n",
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("log\nmessage\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_indent_spaces() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: true,
|
||||
target: false,
|
||||
level: true,
|
||||
written_header_value: false,
|
||||
indent: Some(4),
|
||||
suffix: "\n",
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("[INFO test::path] log\n message\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_indent_zero_spaces() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: true,
|
||||
target: false,
|
||||
level: true,
|
||||
written_header_value: false,
|
||||
indent: Some(0),
|
||||
suffix: "\n",
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("[INFO test::path] log\nmessage\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_indent_spaces_no_header() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: false,
|
||||
target: false,
|
||||
level: false,
|
||||
written_header_value: false,
|
||||
indent: Some(4),
|
||||
suffix: "\n",
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("log\n message\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_suffix() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: false,
|
||||
target: false,
|
||||
level: false,
|
||||
written_header_value: false,
|
||||
indent: None,
|
||||
suffix: "\n\n",
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("log\nmessage\n\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_suffix_with_indent() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: false,
|
||||
target: false,
|
||||
level: false,
|
||||
written_header_value: false,
|
||||
indent: Some(4),
|
||||
suffix: "\n\n",
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("log\n\n message\n\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_target() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write_target(
|
||||
"target",
|
||||
DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: true,
|
||||
target: true,
|
||||
level: true,
|
||||
written_header_value: false,
|
||||
indent: None,
|
||||
suffix: "\n",
|
||||
buf: &mut f,
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!("[INFO test::path target] log\nmessage\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_empty_target() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: true,
|
||||
target: true,
|
||||
level: true,
|
||||
written_header_value: false,
|
||||
indent: None,
|
||||
suffix: "\n",
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("[INFO test::path] log\nmessage\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_no_target() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write_target(
|
||||
"target",
|
||||
DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: true,
|
||||
target: false,
|
||||
level: true,
|
||||
written_header_value: false,
|
||||
indent: None,
|
||||
suffix: "\n",
|
||||
buf: &mut f,
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!("[INFO test::path] log\nmessage\n", written);
|
||||
}
|
||||
}
|
||||
351
third-party/vendor/env_logger/src/fmt/style.rs
vendored
Normal file
351
third-party/vendor/env_logger/src/fmt/style.rs
vendored
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::Buffer;
|
||||
|
||||
/// A set of styles to apply to the terminal output.
|
||||
///
|
||||
/// Call [`Formatter::style`] to get a `Style` and use the builder methods to
|
||||
/// set styling properties, like [color] and [weight].
|
||||
/// To print a value using the style, wrap it in a call to [`value`] when the log
|
||||
/// record is formatted.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a bold, red colored style and use it to print the log level:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut level_style = buf.style();
|
||||
///
|
||||
/// level_style.set_color(Color::Red).set_bold(true);
|
||||
///
|
||||
/// writeln!(buf, "{}: {}",
|
||||
/// level_style.value(record.level()),
|
||||
/// record.args())
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// Styles can be re-used to output multiple values:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut bold = buf.style();
|
||||
///
|
||||
/// bold.set_bold(true);
|
||||
///
|
||||
/// writeln!(buf, "{}: {} {}",
|
||||
/// bold.value(record.level()),
|
||||
/// bold.value("some bold text"),
|
||||
/// record.args())
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// [`Formatter::style`]: struct.Formatter.html#method.style
|
||||
/// [color]: #method.set_color
|
||||
/// [weight]: #method.set_bold
|
||||
/// [`value`]: #method.value
|
||||
#[derive(Clone)]
|
||||
pub struct Style {
|
||||
pub(in crate::fmt) buf: Rc<RefCell<Buffer>>,
|
||||
pub(in crate::fmt) spec: termcolor::ColorSpec,
|
||||
}
|
||||
|
||||
impl Style {
|
||||
/// Set the text color.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a style with red text:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut style = buf.style();
|
||||
///
|
||||
/// style.set_color(Color::Red);
|
||||
///
|
||||
/// writeln!(buf, "{}", style.value(record.args()))
|
||||
/// });
|
||||
/// ```
|
||||
pub fn set_color(&mut self, color: Color) -> &mut Style {
|
||||
self.spec.set_fg(Some(color.into_termcolor()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the text weight.
|
||||
///
|
||||
/// If `yes` is true then text will be written in bold.
|
||||
/// If `yes` is false then text will be written in the default weight.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a style with bold text:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut style = buf.style();
|
||||
///
|
||||
/// style.set_bold(true);
|
||||
///
|
||||
/// writeln!(buf, "{}", style.value(record.args()))
|
||||
/// });
|
||||
/// ```
|
||||
pub fn set_bold(&mut self, yes: bool) -> &mut Style {
|
||||
self.spec.set_bold(yes);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the text intensity.
|
||||
///
|
||||
/// If `yes` is true then text will be written in a brighter color.
|
||||
/// If `yes` is false then text will be written in the default color.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a style with intense text:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut style = buf.style();
|
||||
///
|
||||
/// style.set_intense(true);
|
||||
///
|
||||
/// writeln!(buf, "{}", style.value(record.args()))
|
||||
/// });
|
||||
/// ```
|
||||
pub fn set_intense(&mut self, yes: bool) -> &mut Style {
|
||||
self.spec.set_intense(yes);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether the text is dimmed.
|
||||
///
|
||||
/// If `yes` is true then text will be written in a dimmer color.
|
||||
/// If `yes` is false then text will be written in the default color.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a style with dimmed text:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut style = buf.style();
|
||||
///
|
||||
/// style.set_dimmed(true);
|
||||
///
|
||||
/// writeln!(buf, "{}", style.value(record.args()))
|
||||
/// });
|
||||
/// ```
|
||||
pub fn set_dimmed(&mut self, yes: bool) -> &mut Style {
|
||||
self.spec.set_dimmed(yes);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the background color.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a style with a yellow background:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut style = buf.style();
|
||||
///
|
||||
/// style.set_bg(Color::Yellow);
|
||||
///
|
||||
/// writeln!(buf, "{}", style.value(record.args()))
|
||||
/// });
|
||||
/// ```
|
||||
pub fn set_bg(&mut self, color: Color) -> &mut Style {
|
||||
self.spec.set_bg(Some(color.into_termcolor()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Wrap a value in the style.
|
||||
///
|
||||
/// The same `Style` can be used to print multiple different values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a bold, red colored style and use it to print the log level:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut style = buf.style();
|
||||
///
|
||||
/// style.set_color(Color::Red).set_bold(true);
|
||||
///
|
||||
/// writeln!(buf, "{}: {}",
|
||||
/// style.value(record.level()),
|
||||
/// record.args())
|
||||
/// });
|
||||
/// ```
|
||||
pub fn value<T>(&self, value: T) -> StyledValue<T> {
|
||||
StyledValue {
|
||||
style: Cow::Borrowed(self),
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a value in the style by taking ownership of it.
|
||||
pub(crate) fn into_value<T>(self, value: T) -> StyledValue<'static, T> {
|
||||
StyledValue {
|
||||
style: Cow::Owned(self),
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Style {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Style").field("spec", &self.spec).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A value that can be printed using the given styles.
|
||||
///
|
||||
/// It is the result of calling [`Style::value`].
|
||||
///
|
||||
/// [`Style::value`]: struct.Style.html#method.value
|
||||
pub struct StyledValue<'a, T> {
|
||||
style: Cow<'a, Style>,
|
||||
value: T,
|
||||
}
|
||||
|
||||
impl<'a, T> StyledValue<'a, T> {
|
||||
fn write_fmt<F>(&self, f: F) -> fmt::Result
|
||||
where
|
||||
F: FnOnce() -> fmt::Result,
|
||||
{
|
||||
self.style
|
||||
.buf
|
||||
.borrow_mut()
|
||||
.set_color(&self.style.spec)
|
||||
.map_err(|_| fmt::Error)?;
|
||||
|
||||
// Always try to reset the terminal style, even if writing failed
|
||||
let write = f();
|
||||
let reset = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error);
|
||||
|
||||
write.and(reset)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_styled_value_fmt {
|
||||
($($fmt_trait:path),*) => {
|
||||
$(
|
||||
impl<'a, T: $fmt_trait> $fmt_trait for StyledValue<'a, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result {
|
||||
self.write_fmt(|| T::fmt(&self.value, f))
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_styled_value_fmt!(
|
||||
fmt::Debug,
|
||||
fmt::Display,
|
||||
fmt::Pointer,
|
||||
fmt::Octal,
|
||||
fmt::Binary,
|
||||
fmt::UpperHex,
|
||||
fmt::LowerHex,
|
||||
fmt::UpperExp,
|
||||
fmt::LowerExp
|
||||
);
|
||||
|
||||
// The `Color` type is copied from https://github.com/BurntSushi/termcolor
|
||||
|
||||
/// The set of available colors for the terminal foreground/background.
|
||||
///
|
||||
/// The `Ansi256` and `Rgb` colors will only output the correct codes when
|
||||
/// paired with the `Ansi` `WriteColor` implementation.
|
||||
///
|
||||
/// The `Ansi256` and `Rgb` color types are not supported when writing colors
|
||||
/// on Windows using the console. If they are used on Windows, then they are
|
||||
/// silently ignored and no colors will be emitted.
|
||||
///
|
||||
/// This set may expand over time.
|
||||
///
|
||||
/// This type has a `FromStr` impl that can parse colors from their human
|
||||
/// readable form. The format is as follows:
|
||||
///
|
||||
/// 1. Any of the explicitly listed colors in English. They are matched
|
||||
/// case insensitively.
|
||||
/// 2. A single 8-bit integer, in either decimal or hexadecimal format.
|
||||
/// 3. A triple of 8-bit integers separated by a comma, where each integer is
|
||||
/// in decimal or hexadecimal format.
|
||||
///
|
||||
/// Hexadecimal numbers are written with a `0x` prefix.
|
||||
#[allow(missing_docs)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Color {
|
||||
Black,
|
||||
Blue,
|
||||
Green,
|
||||
Red,
|
||||
Cyan,
|
||||
Magenta,
|
||||
Yellow,
|
||||
White,
|
||||
Ansi256(u8),
|
||||
Rgb(u8, u8, u8),
|
||||
}
|
||||
|
||||
impl Color {
|
||||
fn into_termcolor(self) -> termcolor::Color {
|
||||
match self {
|
||||
Color::Black => termcolor::Color::Black,
|
||||
Color::Blue => termcolor::Color::Blue,
|
||||
Color::Green => termcolor::Color::Green,
|
||||
Color::Red => termcolor::Color::Red,
|
||||
Color::Cyan => termcolor::Color::Cyan,
|
||||
Color::Magenta => termcolor::Color::Magenta,
|
||||
Color::Yellow => termcolor::Color::Yellow,
|
||||
Color::White => termcolor::Color::White,
|
||||
Color::Ansi256(value) => termcolor::Color::Ansi256(value),
|
||||
Color::Rgb(r, g, b) => termcolor::Color::Rgb(r, g, b),
|
||||
}
|
||||
}
|
||||
}
|
||||
33
third-party/vendor/env_logger/src/fmt/writer/atty.rs
vendored
Normal file
33
third-party/vendor/env_logger/src/fmt/writer/atty.rs
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
This internal module contains the terminal detection implementation.
|
||||
|
||||
If the `auto-color` feature is enabled then we detect whether we're attached to a particular TTY.
|
||||
Otherwise, assume we're not attached to anything. This effectively prevents styles from being
|
||||
printed.
|
||||
*/
|
||||
|
||||
#[cfg(feature = "auto-color")]
|
||||
mod imp {
|
||||
use is_terminal::IsTerminal;
|
||||
|
||||
pub(in crate::fmt) fn is_stdout() -> bool {
|
||||
std::io::stdout().is_terminal()
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn is_stderr() -> bool {
|
||||
std::io::stderr().is_terminal()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "auto-color"))]
|
||||
mod imp {
|
||||
pub(in crate::fmt) fn is_stdout() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn is_stderr() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt) use self::imp::*;
|
||||
15
third-party/vendor/env_logger/src/fmt/writer/buffer/mod.rs
vendored
Normal file
15
third-party/vendor/env_logger/src/fmt/writer/buffer/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
This internal module contains the style and terminal writing implementation.
|
||||
|
||||
Its public API is available when the `termcolor` crate is available.
|
||||
The terminal printing is shimmed when the `termcolor` crate is not available.
|
||||
*/
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
mod termcolor;
|
||||
#[cfg(feature = "color")]
|
||||
pub(in crate::fmt) use self::termcolor::*;
|
||||
#[cfg(not(feature = "color"))]
|
||||
mod plain;
|
||||
#[cfg(not(feature = "color"))]
|
||||
pub(in crate::fmt) use plain::*;
|
||||
68
third-party/vendor/env_logger/src/fmt/writer/buffer/plain.rs
vendored
Normal file
68
third-party/vendor/env_logger/src/fmt/writer/buffer/plain.rs
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
use std::{io, sync::Mutex};
|
||||
|
||||
use crate::fmt::{WritableTarget, WriteStyle};
|
||||
|
||||
pub(in crate::fmt::writer) struct BufferWriter {
|
||||
target: WritableTarget,
|
||||
}
|
||||
|
||||
impl BufferWriter {
|
||||
pub(in crate::fmt::writer) fn stderr(is_test: bool, _write_style: WriteStyle) -> Self {
|
||||
BufferWriter {
|
||||
target: if is_test {
|
||||
WritableTarget::PrintStderr
|
||||
} else {
|
||||
WritableTarget::WriteStderr
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn stdout(is_test: bool, _write_style: WriteStyle) -> Self {
|
||||
BufferWriter {
|
||||
target: if is_test {
|
||||
WritableTarget::PrintStdout
|
||||
} else {
|
||||
WritableTarget::WriteStdout
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn pipe(pipe: Box<Mutex<dyn io::Write + Send + 'static>>) -> Self {
|
||||
BufferWriter {
|
||||
target: WritableTarget::Pipe(pipe),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn write_style(&self) -> WriteStyle {
|
||||
WriteStyle::Never
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
|
||||
Buffer(Vec::new())
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
|
||||
self.target.print(buf)
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt) struct Buffer(Vec<u8>);
|
||||
|
||||
impl Buffer {
|
||||
pub(in crate::fmt) fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.extend(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
108
third-party/vendor/env_logger/src/fmt/writer/buffer/termcolor.rs
vendored
Normal file
108
third-party/vendor/env_logger/src/fmt/writer/buffer/termcolor.rs
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
use std::io::{self, Write};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use termcolor::{self, ColorSpec, WriteColor};
|
||||
|
||||
use crate::fmt::{WritableTarget, WriteStyle};
|
||||
|
||||
pub(in crate::fmt::writer) struct BufferWriter {
|
||||
inner: termcolor::BufferWriter,
|
||||
uncolored_target: Option<WritableTarget>,
|
||||
write_style: WriteStyle,
|
||||
}
|
||||
|
||||
impl BufferWriter {
|
||||
pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self {
|
||||
BufferWriter {
|
||||
inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()),
|
||||
uncolored_target: if is_test {
|
||||
Some(WritableTarget::PrintStderr)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
write_style,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self {
|
||||
BufferWriter {
|
||||
inner: termcolor::BufferWriter::stdout(write_style.into_color_choice()),
|
||||
uncolored_target: if is_test {
|
||||
Some(WritableTarget::PrintStdout)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
write_style,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn pipe(pipe: Box<Mutex<dyn io::Write + Send + 'static>>) -> Self {
|
||||
let write_style = WriteStyle::Never;
|
||||
BufferWriter {
|
||||
// The inner Buffer is never printed from, but it is still needed to handle coloring and other formatting
|
||||
inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()),
|
||||
uncolored_target: Some(WritableTarget::Pipe(pipe)),
|
||||
write_style,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn write_style(&self) -> WriteStyle {
|
||||
self.write_style
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
|
||||
Buffer {
|
||||
inner: self.inner.buffer(),
|
||||
has_uncolored_target: self.uncolored_target.is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
|
||||
if let Some(target) = &self.uncolored_target {
|
||||
target.print(buf)
|
||||
} else {
|
||||
self.inner.print(&buf.inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt) struct Buffer {
|
||||
inner: termcolor::Buffer,
|
||||
has_uncolored_target: bool,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub(in crate::fmt) fn clear(&mut self) {
|
||||
self.inner.clear()
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.write(buf)
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.flush()
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn as_bytes(&self) -> &[u8] {
|
||||
self.inner.as_slice()
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
|
||||
// Ignore styles for test captured logs because they can't be printed
|
||||
if !self.has_uncolored_target {
|
||||
self.inner.set_color(spec)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn reset(&mut self) -> io::Result<()> {
|
||||
// Ignore styles for test captured logs because they can't be printed
|
||||
if !self.has_uncolored_target {
|
||||
self.inner.reset()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
288
third-party/vendor/env_logger/src/fmt/writer/mod.rs
vendored
Normal file
288
third-party/vendor/env_logger/src/fmt/writer/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
mod atty;
|
||||
mod buffer;
|
||||
|
||||
use self::atty::{is_stderr, is_stdout};
|
||||
use self::buffer::BufferWriter;
|
||||
use std::{fmt, io, mem, sync::Mutex};
|
||||
|
||||
pub(super) mod glob {
|
||||
pub use super::*;
|
||||
}
|
||||
|
||||
pub(super) use self::buffer::Buffer;
|
||||
|
||||
/// Log target, either `stdout`, `stderr` or a custom pipe.
|
||||
#[non_exhaustive]
|
||||
pub enum Target {
|
||||
/// Logs will be sent to standard output.
|
||||
Stdout,
|
||||
/// Logs will be sent to standard error.
|
||||
Stderr,
|
||||
/// Logs will be sent to a custom pipe.
|
||||
Pipe(Box<dyn io::Write + Send + 'static>),
|
||||
}
|
||||
|
||||
impl Default for Target {
|
||||
fn default() -> Self {
|
||||
Target::Stderr
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Target {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::Stdout => "stdout",
|
||||
Self::Stderr => "stderr",
|
||||
Self::Pipe(_) => "pipe",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Log target, either `stdout`, `stderr` or a custom pipe.
|
||||
///
|
||||
/// Same as `Target`, except the pipe is wrapped in a mutex for interior mutability.
|
||||
pub(super) enum WritableTarget {
|
||||
/// Logs will be written to standard output.
|
||||
#[allow(dead_code)]
|
||||
WriteStdout,
|
||||
/// Logs will be printed to standard output.
|
||||
PrintStdout,
|
||||
/// Logs will be written to standard error.
|
||||
#[allow(dead_code)]
|
||||
WriteStderr,
|
||||
/// Logs will be printed to standard error.
|
||||
PrintStderr,
|
||||
/// Logs will be sent to a custom pipe.
|
||||
Pipe(Box<Mutex<dyn io::Write + Send + 'static>>),
|
||||
}
|
||||
|
||||
impl WritableTarget {
|
||||
fn print(&self, buf: &Buffer) -> io::Result<()> {
|
||||
use std::io::Write as _;
|
||||
|
||||
let buf = buf.as_bytes();
|
||||
match self {
|
||||
WritableTarget::WriteStdout => {
|
||||
let stream = std::io::stdout();
|
||||
let mut stream = stream.lock();
|
||||
stream.write_all(buf)?;
|
||||
stream.flush()?;
|
||||
}
|
||||
WritableTarget::PrintStdout => print!("{}", String::from_utf8_lossy(buf)),
|
||||
WritableTarget::WriteStderr => {
|
||||
let stream = std::io::stderr();
|
||||
let mut stream = stream.lock();
|
||||
stream.write_all(buf)?;
|
||||
stream.flush()?;
|
||||
}
|
||||
WritableTarget::PrintStderr => eprint!("{}", String::from_utf8_lossy(buf)),
|
||||
// Safety: If the target type is `Pipe`, `target_pipe` will always be non-empty.
|
||||
WritableTarget::Pipe(pipe) => {
|
||||
let mut stream = pipe.lock().unwrap();
|
||||
stream.write_all(buf)?;
|
||||
stream.flush()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for WritableTarget {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::WriteStdout => "stdout",
|
||||
Self::PrintStdout => "stdout",
|
||||
Self::WriteStderr => "stderr",
|
||||
Self::PrintStderr => "stderr",
|
||||
Self::Pipe(_) => "pipe",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
/// Whether or not to print styles to the target.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum WriteStyle {
|
||||
/// Try to print styles, but don't force the issue.
|
||||
Auto,
|
||||
/// Try very hard to print styles.
|
||||
Always,
|
||||
/// Never print styles.
|
||||
Never,
|
||||
}
|
||||
|
||||
impl Default for WriteStyle {
|
||||
fn default() -> Self {
|
||||
WriteStyle::Auto
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
impl WriteStyle {
|
||||
fn into_color_choice(self) -> ::termcolor::ColorChoice {
|
||||
match self {
|
||||
WriteStyle::Always => ::termcolor::ColorChoice::Always,
|
||||
WriteStyle::Auto => ::termcolor::ColorChoice::Auto,
|
||||
WriteStyle::Never => ::termcolor::ColorChoice::Never,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A terminal target with color awareness.
|
||||
pub(crate) struct Writer {
|
||||
inner: BufferWriter,
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
pub fn write_style(&self) -> WriteStyle {
|
||||
self.inner.write_style()
|
||||
}
|
||||
|
||||
pub(super) fn buffer(&self) -> Buffer {
|
||||
self.inner.buffer()
|
||||
}
|
||||
|
||||
pub(super) fn print(&self, buf: &Buffer) -> io::Result<()> {
|
||||
self.inner.print(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Writer {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Writer").finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for a terminal writer.
|
||||
///
|
||||
/// The target and style choice can be configured before building.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Builder {
|
||||
target: Target,
|
||||
write_style: WriteStyle,
|
||||
is_test: bool,
|
||||
built: bool,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Initialize the writer builder with defaults.
|
||||
pub(crate) fn new() -> Self {
|
||||
Builder {
|
||||
target: Default::default(),
|
||||
write_style: Default::default(),
|
||||
is_test: false,
|
||||
built: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the target to write to.
|
||||
pub(crate) fn target(&mut self, target: Target) -> &mut Self {
|
||||
self.target = target;
|
||||
self
|
||||
}
|
||||
|
||||
/// Parses a style choice string.
|
||||
///
|
||||
/// See the [Disabling colors] section for more details.
|
||||
///
|
||||
/// [Disabling colors]: ../index.html#disabling-colors
|
||||
pub(crate) fn parse_write_style(&mut self, write_style: &str) -> &mut Self {
|
||||
self.write_style(parse_write_style(write_style))
|
||||
}
|
||||
|
||||
/// Whether or not to print style characters when writing.
|
||||
pub(crate) fn write_style(&mut self, write_style: WriteStyle) -> &mut Self {
|
||||
self.write_style = write_style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether or not to capture logs for `cargo test`.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub(crate) fn is_test(&mut self, is_test: bool) -> &mut Self {
|
||||
self.is_test = is_test;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build a terminal writer.
|
||||
pub(crate) fn build(&mut self) -> Writer {
|
||||
assert!(!self.built, "attempt to re-use consumed builder");
|
||||
self.built = true;
|
||||
|
||||
let color_choice = match self.write_style {
|
||||
WriteStyle::Auto => {
|
||||
if match &self.target {
|
||||
Target::Stderr => is_stderr(),
|
||||
Target::Stdout => is_stdout(),
|
||||
Target::Pipe(_) => false,
|
||||
} {
|
||||
WriteStyle::Auto
|
||||
} else {
|
||||
WriteStyle::Never
|
||||
}
|
||||
}
|
||||
color_choice => color_choice,
|
||||
};
|
||||
let color_choice = if self.is_test {
|
||||
WriteStyle::Never
|
||||
} else {
|
||||
color_choice
|
||||
};
|
||||
|
||||
let writer = match mem::take(&mut self.target) {
|
||||
Target::Stderr => BufferWriter::stderr(self.is_test, color_choice),
|
||||
Target::Stdout => BufferWriter::stdout(self.is_test, color_choice),
|
||||
Target::Pipe(pipe) => BufferWriter::pipe(Box::new(Mutex::new(pipe))),
|
||||
};
|
||||
|
||||
Writer { inner: writer }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
Builder::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_write_style(spec: &str) -> WriteStyle {
|
||||
match spec {
|
||||
"auto" => WriteStyle::Auto,
|
||||
"always" => WriteStyle::Always,
|
||||
"never" => WriteStyle::Never,
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_write_style_valid() {
|
||||
let inputs = vec![
|
||||
("auto", WriteStyle::Auto),
|
||||
("always", WriteStyle::Always),
|
||||
("never", WriteStyle::Never),
|
||||
];
|
||||
|
||||
for (input, expected) in inputs {
|
||||
assert_eq!(expected, parse_write_style(input));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_write_style_invalid() {
|
||||
let inputs = vec!["", "true", "false", "NEVER!!"];
|
||||
|
||||
for input in inputs {
|
||||
assert_eq!(WriteStyle::Auto, parse_write_style(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
284
third-party/vendor/env_logger/src/lib.rs
vendored
Normal file
284
third-party/vendor/env_logger/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A simple logger that can be configured via environment variables, for use
|
||||
//! with the logging facade exposed by the [`log` crate][log-crate-url].
|
||||
//!
|
||||
//! Despite having "env" in its name, **`env_logger`** can also be configured by
|
||||
//! other means besides environment variables. See [the examples][gh-repo-examples]
|
||||
//! in the source repository for more approaches.
|
||||
//!
|
||||
//! By default, `env_logger` writes logs to `stderr`, but can be configured to
|
||||
//! instead write them to `stdout`.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! ```
|
||||
//! use log::{debug, error, log_enabled, info, Level};
|
||||
//!
|
||||
//! env_logger::init();
|
||||
//!
|
||||
//! debug!("this is a debug {}", "message");
|
||||
//! error!("this is printed by default");
|
||||
//!
|
||||
//! if log_enabled!(Level::Info) {
|
||||
//! let x = 3 * 4; // expensive computation
|
||||
//! info!("the answer was: {}", x);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Assumes the binary is `main`:
|
||||
//!
|
||||
//! ```{.bash}
|
||||
//! $ RUST_LOG=error ./main
|
||||
//! [2017-11-09T02:12:24Z ERROR main] this is printed by default
|
||||
//! ```
|
||||
//!
|
||||
//! ```{.bash}
|
||||
//! $ RUST_LOG=info ./main
|
||||
//! [2017-11-09T02:12:24Z ERROR main] this is printed by default
|
||||
//! [2017-11-09T02:12:24Z INFO main] the answer was: 12
|
||||
//! ```
|
||||
//!
|
||||
//! ```{.bash}
|
||||
//! $ RUST_LOG=debug ./main
|
||||
//! [2017-11-09T02:12:24Z DEBUG main] this is a debug message
|
||||
//! [2017-11-09T02:12:24Z ERROR main] this is printed by default
|
||||
//! [2017-11-09T02:12:24Z INFO main] the answer was: 12
|
||||
//! ```
|
||||
//!
|
||||
//! You can also set the log level on a per module basis:
|
||||
//!
|
||||
//! ```{.bash}
|
||||
//! $ RUST_LOG=main=info ./main
|
||||
//! [2017-11-09T02:12:24Z ERROR main] this is printed by default
|
||||
//! [2017-11-09T02:12:24Z INFO main] the answer was: 12
|
||||
//! ```
|
||||
//!
|
||||
//! And enable all logging:
|
||||
//!
|
||||
//! ```{.bash}
|
||||
//! $ RUST_LOG=main ./main
|
||||
//! [2017-11-09T02:12:24Z DEBUG main] this is a debug message
|
||||
//! [2017-11-09T02:12:24Z ERROR main] this is printed by default
|
||||
//! [2017-11-09T02:12:24Z INFO main] the answer was: 12
|
||||
//! ```
|
||||
//!
|
||||
//! If the binary name contains hyphens, you will need to replace
|
||||
//! them with underscores:
|
||||
//!
|
||||
//! ```{.bash}
|
||||
//! $ RUST_LOG=my_app ./my-app
|
||||
//! [2017-11-09T02:12:24Z DEBUG my_app] this is a debug message
|
||||
//! [2017-11-09T02:12:24Z ERROR my_app] this is printed by default
|
||||
//! [2017-11-09T02:12:24Z INFO my_app] the answer was: 12
|
||||
//! ```
|
||||
//!
|
||||
//! This is because Rust modules and crates cannot contain hyphens
|
||||
//! in their name, although `cargo` continues to accept them.
|
||||
//!
|
||||
//! See the documentation for the [`log` crate][log-crate-url] for more
|
||||
//! information about its API.
|
||||
//!
|
||||
//! ## Enabling logging
|
||||
//!
|
||||
//! **By default all logging is disabled except for the `error` level**
|
||||
//!
|
||||
//! The **`RUST_LOG`** environment variable controls logging with the syntax:
|
||||
//! ```text
|
||||
//! RUST_LOG=[target][=][level][,...]
|
||||
//! ```
|
||||
//! Or in other words, its a comma-separated list of directives.
|
||||
//! Directives can filter by **target**, by **level**, or both (using `=`).
|
||||
//!
|
||||
//! For example,
|
||||
//! ```text
|
||||
//! RUST_LOG=data=debug,hardware=debug
|
||||
//! ```
|
||||
//!
|
||||
//! **target** is typically the path of the module the message
|
||||
//! in question originated from, though it can be overridden.
|
||||
//! The path is rooted in the name of the crate it was compiled for, so if
|
||||
//! your program is in a file called, for example, `hello.rs`, the path would
|
||||
//! simply be `hello`.
|
||||
//!
|
||||
//! Furthermore, the log can be filtered using prefix-search based on the
|
||||
//! specified log target.
|
||||
//!
|
||||
//! For example, `RUST_LOG=example` would match the following targets:
|
||||
//! - `example`
|
||||
//! - `example::test`
|
||||
//! - `example::test::module::submodule`
|
||||
//! - `examples::and_more_examples`
|
||||
//!
|
||||
//! When providing the crate name or a module path, explicitly specifying the
|
||||
//! log level is optional. If omitted, all logging for the item will be
|
||||
//! enabled.
|
||||
//!
|
||||
//! **level** is the maximum [`log::Level`][level-enum] to be shown and includes:
|
||||
//! - `error`
|
||||
//! - `warn`
|
||||
//! - `info`
|
||||
//! - `debug`
|
||||
//! - `trace`
|
||||
//! - `off` (pseudo level to disable all logging for the target)
|
||||
//!
|
||||
//! Logging level names are case-insensitive; e.g.,
|
||||
//! `debug`, `DEBUG`, and `dEbuG` all represent the same logging level. For
|
||||
//! consistency, our convention is to use the lower case names. Where our docs
|
||||
//! do use other forms, they do so in the context of specific examples, so you
|
||||
//! won't be surprised if you see similar usage in the wild.
|
||||
//!
|
||||
//! Some examples of valid values of `RUST_LOG` are:
|
||||
//!
|
||||
//! - `RUST_LOG=hello` turns on all logging for the `hello` module
|
||||
//! - `RUST_LOG=trace` turns on all logging for the application, regardless of its name
|
||||
//! - `RUST_LOG=TRACE` turns on all logging for the application, regardless of its name (same as previous)
|
||||
//! - `RUST_LOG=info` turns on all info logging
|
||||
//! - `RUST_LOG=INFO` turns on all info logging (same as previous)
|
||||
//! - `RUST_LOG=hello=debug` turns on debug logging for `hello`
|
||||
//! - `RUST_LOG=hello=DEBUG` turns on debug logging for `hello` (same as previous)
|
||||
//! - `RUST_LOG=hello,std::option` turns on `hello`, and std's option logging
|
||||
//! - `RUST_LOG=error,hello=warn` turn on global error logging and also warn for `hello`
|
||||
//! - `RUST_LOG=error,hello=off` turn on global error logging, but turn off logging for `hello`
|
||||
//! - `RUST_LOG=off` turns off all logging for the application
|
||||
//! - `RUST_LOG=OFF` turns off all logging for the application (same as previous)
|
||||
//!
|
||||
//! ## Filtering results
|
||||
//!
|
||||
//! A `RUST_LOG` directive may include a regex filter. The syntax is to append `/`
|
||||
//! followed by a regex. Each message is checked against the regex, and is only
|
||||
//! logged if it matches. Note that the matching is done after formatting the
|
||||
//! log string but before adding any logging meta-data. There is a single filter
|
||||
//! for all modules.
|
||||
//!
|
||||
//! Some examples:
|
||||
//!
|
||||
//! * `hello/foo` turns on all logging for the 'hello' module where the log
|
||||
//! message includes 'foo'.
|
||||
//! * `info/f.o` turns on all info logging where the log message includes 'foo',
|
||||
//! 'f1o', 'fao', etc.
|
||||
//! * `hello=debug/foo*foo` turns on debug logging for 'hello' where the log
|
||||
//! message includes 'foofoo' or 'fofoo' or 'fooooooofoo', etc.
|
||||
//! * `error,hello=warn/[0-9]scopes` turn on global error logging and also
|
||||
//! warn for hello. In both cases the log message must include a single digit
|
||||
//! number followed by 'scopes'.
|
||||
//!
|
||||
//! ## Capturing logs in tests
|
||||
//!
|
||||
//! Records logged during `cargo test` will not be captured by the test harness by default.
|
||||
//! The [`Builder::is_test`] method can be used in unit tests to ensure logs will be captured:
|
||||
//!
|
||||
//! ```
|
||||
//! #[cfg(test)]
|
||||
//! mod tests {
|
||||
//! use log::info;
|
||||
//!
|
||||
//! fn init() {
|
||||
//! let _ = env_logger::builder().is_test(true).try_init();
|
||||
//! }
|
||||
//!
|
||||
//! #[test]
|
||||
//! fn it_works() {
|
||||
//! init();
|
||||
//!
|
||||
//! info!("This record will be captured by `cargo test`");
|
||||
//!
|
||||
//! assert_eq!(2, 1 + 1);
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Enabling test capturing comes at the expense of color and other style support
|
||||
//! and may have performance implications.
|
||||
//!
|
||||
//! ## Disabling colors
|
||||
//!
|
||||
//! Colors and other styles can be configured with the `RUST_LOG_STYLE`
|
||||
//! environment variable. It accepts the following values:
|
||||
//!
|
||||
//! * `auto` (default) will attempt to print style characters, but don't force the issue.
|
||||
//! If the console isn't available on Windows, or if TERM=dumb, for example, then don't print colors.
|
||||
//! * `always` will always print style characters even if they aren't supported by the terminal.
|
||||
//! This includes emitting ANSI colors on Windows if the console API is unavailable.
|
||||
//! * `never` will never print style characters.
|
||||
//!
|
||||
//! ## Tweaking the default format
|
||||
//!
|
||||
//! Parts of the default format can be excluded from the log output using the [`Builder`].
|
||||
//! The following example excludes the timestamp from the log output:
|
||||
//!
|
||||
//! ```
|
||||
//! env_logger::builder()
|
||||
//! .format_timestamp(None)
|
||||
//! .init();
|
||||
//! ```
|
||||
//!
|
||||
//! ### Stability of the default format
|
||||
//!
|
||||
//! The default format won't optimise for long-term stability, and explicitly makes no
|
||||
//! guarantees about the stability of its output across major, minor or patch version
|
||||
//! bumps during `0.x`.
|
||||
//!
|
||||
//! If you want to capture or interpret the output of `env_logger` programmatically
|
||||
//! then you should use a custom format.
|
||||
//!
|
||||
//! ### Using a custom format
|
||||
//!
|
||||
//! Custom formats can be provided as closures to the [`Builder`].
|
||||
//! These closures take a [`Formatter`][crate::fmt::Formatter] and `log::Record` as arguments:
|
||||
//!
|
||||
//! ```
|
||||
//! use std::io::Write;
|
||||
//!
|
||||
//! env_logger::builder()
|
||||
//! .format(|buf, record| {
|
||||
//! writeln!(buf, "{}: {}", record.level(), record.args())
|
||||
//! })
|
||||
//! .init();
|
||||
//! ```
|
||||
//!
|
||||
//! See the [`fmt`] module for more details about custom formats.
|
||||
//!
|
||||
//! ## Specifying defaults for environment variables
|
||||
//!
|
||||
//! `env_logger` can read configuration from environment variables.
|
||||
//! If these variables aren't present, the default value to use can be tweaked with the [`Env`] type.
|
||||
//! The following example defaults to log `warn` and above if the `RUST_LOG` environment variable
|
||||
//! isn't set:
|
||||
//!
|
||||
//! ```
|
||||
//! use env_logger::Env;
|
||||
//!
|
||||
//! env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init();
|
||||
//! ```
|
||||
//!
|
||||
//! [gh-repo-examples]: https://github.com/rust-cli/env_logger/tree/main/examples
|
||||
//! [level-enum]: https://docs.rs/log/latest/log/enum.Level.html
|
||||
//! [log-crate-url]: https://docs.rs/log
|
||||
//! [`Builder`]: struct.Builder.html
|
||||
//! [`Builder::is_test`]: struct.Builder.html#method.is_test
|
||||
//! [`Env`]: struct.Env.html
|
||||
//! [`fmt`]: fmt/index.html
|
||||
|
||||
#![doc(
|
||||
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
|
||||
html_favicon_url = "https://www.rust-lang.org/static/images/favicon.ico"
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
// When compiled for the rustc compiler itself we want to make sure that this is
|
||||
// an unstable crate
|
||||
#![cfg_attr(rustbuild, feature(staged_api, rustc_private))]
|
||||
#![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))]
|
||||
#![deny(missing_debug_implementations, missing_docs)]
|
||||
|
||||
mod logger;
|
||||
|
||||
pub mod filter;
|
||||
pub mod fmt;
|
||||
|
||||
pub use self::fmt::glob::*;
|
||||
pub use self::logger::*;
|
||||
1016
third-party/vendor/env_logger/src/logger.rs
vendored
Normal file
1016
third-party/vendor/env_logger/src/logger.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue