diff --git a/Cargo.toml b/Cargo.toml index 2cb1587c..421d35ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ members = [ log = { version = "0.4", features = ["std"] } regex = { version = "1.0.3", optional = true } termcolor = { version = "1.0.2", optional = true } -humantime = { version = "1.1", optional = true } +humantime = { version = "1.3", optional = true } atty = { version = "0.2.5", optional = true } [[test]] diff --git a/src/fmt/humantime/extern_impl.rs b/src/fmt/humantime/extern_impl.rs index 596a2819..1d667a97 100644 --- a/src/fmt/humantime/extern_impl.rs +++ b/src/fmt/humantime/extern_impl.rs @@ -1,9 +1,12 @@ use std::fmt; use std::time::SystemTime; -use humantime::{format_rfc3339_nanos, format_rfc3339_seconds}; +use humantime::{ + format_rfc3339_micros, format_rfc3339_millis, + format_rfc3339_nanos, format_rfc3339_seconds, +}; -use ::fmt::Formatter; +use ::fmt::{Formatter, TimestampPrecision}; pub(in ::fmt) mod glob { pub use super::*; @@ -30,10 +33,50 @@ impl Formatter { /// /// [`Timestamp`]: struct.Timestamp.html pub fn timestamp(&self) -> Timestamp { - Timestamp(SystemTime::now()) + 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, + } } /// Get a [`PreciseTimestamp`] for the current date and time in UTC with nanos. + #[deprecated = "Use timestamp_nanos() instead"] pub fn precise_timestamp(&self) -> PreciseTimestamp { PreciseTimestamp(SystemTime::now()) } @@ -46,7 +89,10 @@ impl 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(SystemTime); +pub struct Timestamp { + time: SystemTime, + precision: TimestampPrecision, +} /// An [RFC3339] formatted timestamp with nanos. /// @@ -72,8 +118,15 @@ impl fmt::Debug for Timestamp { } impl fmt::Display for Timestamp { - fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { - format_rfc3339_seconds(self.0).fmt(f) + 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) } } @@ -81,4 +134,4 @@ impl fmt::Display for PreciseTimestamp { fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { format_rfc3339_nanos(self.0).fmt(f) } -} \ No newline at end of file +} diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 76f5e0c1..3cbbee6e 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -112,9 +112,25 @@ impl fmt::Debug for Formatter { } } +/// 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, +} + pub(crate) struct Builder { - pub default_format_timestamp: bool, - pub default_format_timestamp_nanos: bool, + pub default_format_timestamp: Option, pub default_format_module_path: bool, pub default_format_level: bool, pub default_format_indent: Option, @@ -126,8 +142,7 @@ pub(crate) struct Builder { impl Default for Builder { fn default() -> Self { Builder { - default_format_timestamp: true, - default_format_timestamp_nanos: false, + default_format_timestamp: Some(TimestampPrecision::Seconds), default_format_module_path: true, default_format_level: true, default_format_indent: Some(4), @@ -139,7 +154,7 @@ impl Default for Builder { 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. @@ -159,7 +174,6 @@ impl Builder { Box::new(move |buf, record| { let fmt = DefaultFormat { timestamp: built.default_format_timestamp, - timestamp_nanos: built.default_format_timestamp_nanos, module_path: built.default_format_module_path, level: built.default_format_level, written_header_value: false, @@ -179,13 +193,12 @@ type SubtleStyle = StyledValue<'static, &'static str>; type SubtleStyle = &'static str; /// The default format. -/// +/// /// This format needs to work with any combination of crate features. struct DefaultFormat<'a> { - timestamp: bool, + timestamp: Option, module_path: bool, level: bool, - timestamp_nanos: bool, written_header_value: bool, indent: Option, buf: &'a mut Formatter, @@ -251,22 +264,19 @@ impl<'a> DefaultFormat<'a> { fn write_timestamp(&mut self) -> io::Result<()> { #[cfg(feature = "humantime")] { - if !self.timestamp { - return Ok(()) - } - - if self.timestamp_nanos { - let ts_nanos = self.buf.precise_timestamp(); - self.write_header_value(ts_nanos) - } else { - let ts = self.buf.timestamp(); - self.write_header_value(ts) - } + use fmt::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"))] { - let _ = self.timestamp; - let _ = self.timestamp_nanos; Ok(()) } } @@ -294,7 +304,7 @@ impl<'a> DefaultFormat<'a> { fn write_args(&mut self, record: &Record) -> io::Result<()> { match self.indent { - + // Fast path for no indentation None => writeln!(self.buf, "{}", record.args()), @@ -376,8 +386,7 @@ mod tests { let mut f = Formatter::new(&writer); let written = write(DefaultFormat { - timestamp: false, - timestamp_nanos: false, + timestamp: None, module_path: true, level: true, written_header_value: false, @@ -397,8 +406,7 @@ mod tests { let mut f = Formatter::new(&writer); let written = write(DefaultFormat { - timestamp: false, - timestamp_nanos: false, + timestamp: None, module_path: false, level: false, written_header_value: false, @@ -418,8 +426,7 @@ mod tests { let mut f = Formatter::new(&writer); let written = write(DefaultFormat { - timestamp: false, - timestamp_nanos: false, + timestamp: None, module_path: true, level: true, written_header_value: false, @@ -439,8 +446,7 @@ mod tests { let mut f = Formatter::new(&writer); let written = write(DefaultFormat { - timestamp: false, - timestamp_nanos: false, + timestamp: None, module_path: true, level: true, written_header_value: false, @@ -460,8 +466,7 @@ mod tests { let mut f = Formatter::new(&writer); let written = write(DefaultFormat { - timestamp: false, - timestamp_nanos: false, + timestamp: None, module_path: false, level: false, written_header_value: false, diff --git a/src/lib.rs b/src/lib.rs index fd9bc5d2..d3667b87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -272,6 +272,8 @@ use self::filter::Filter; use self::fmt::Formatter; use self::fmt::writer::{self, Writer}; +pub use self::fmt::TimestampPrecision as Timestamp; + /// The default name for the environment variable to read filters from. pub const DEFAULT_FILTER_ENV: &'static str = "RUST_LOG"; @@ -518,14 +520,24 @@ impl Builder { } /// Whether or not to write the timestamp in the default format. + #[deprecated = "Use format_timestamp() instead"] pub fn default_format_timestamp(&mut self, write: bool) -> &mut Self { - self.format.default_format_timestamp = write; + if write { + self.format_timestamp(Some(Timestamp::Seconds)); + } else { + self.format_timestamp(None); + } self } - /// Whether or not to write the timestamp with nanos. + /// Whether or not to write the timestamp with nanosecond precision. + #[deprecated = "Use format_timestamp() instead"] pub fn default_format_timestamp_nanos(&mut self, write: bool) -> &mut Self { - self.format.default_format_timestamp_nanos = write; + // The old logic included two booleans: One for timestamp on/off and one + // for nanosecond precision. Mimic it here for compatibility. + if write && self.format.default_format_timestamp.is_some() { + self.format.default_format_timestamp = Some(fmt::TimestampPrecision::Nanos); + } self } @@ -536,6 +548,12 @@ impl Builder { self } + /// Configures if timestamp should be included and in what precision. + pub fn format_timestamp(&mut self, timestamp: Option) -> &mut Self { + self.format.default_format_timestamp = timestamp; + self + } + /// Adds a directive to the filter for a specific module. /// /// # Examples