diff --git a/Cargo.lock b/Cargo.lock index 647e92af..5fd263ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,12 +11,33 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + [[package]] name = "cc" version = "1.0.77" @@ -29,15 +50,90 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + [[package]] name = "ci" version = "0.0.0" +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cxx" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "env_logger" version = "0.10.0" dependencies = [ - "humantime", + "chrono", "is-terminal", "log", "regex", @@ -75,10 +171,28 @@ dependencies = [ ] [[package]] -name = "humantime" -version = "2.1.0" +name = "iana-time-zone" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] [[package]] name = "io-lifetimes" @@ -102,12 +216,30 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + [[package]] name = "linux-raw-sys" version = "0.1.3" @@ -129,6 +261,49 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + [[package]] name = "regex" version = "1.7.0" @@ -160,6 +335,23 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + +[[package]] +name = "syn" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -169,6 +361,89 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 64b9ee9b..3af067d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,17 +37,17 @@ pre-release-replacements = [ ] [features] -default = ["auto-color", "humantime", "regex"] +default = ["auto-color", "chrono", "regex"] color = ["dep:termcolor"] auto-color = ["dep:is-terminal", "color"] -humantime = ["dep:humantime"] +chrono = ["dep:chrono"] regex = ["dep:regex"] [dependencies] log = { version = "0.4.8", features = ["std"] } regex = { version = "1.0.3", optional = true, default-features=false, features=["std", "perf"] } termcolor = { version = "1.1.1", optional = true } -humantime = { version = "2.0.0", optional = true } +chrono = { version = "0.4.23", optional = true } is-terminal = { version = "0.4.0", optional = true } [[test]] diff --git a/ci/src/main.rs b/ci/src/main.rs index 961a3141..eab34763 100644 --- a/ci/src/main.rs +++ b/ci/src/main.rs @@ -2,7 +2,7 @@ mod permute; mod task; fn main() { - let features = ["color", "humantime", "auto-color", "regex"]; + let features = ["color", "chrono", "auto-color", "regex"]; // Run a default build if !task::test(Default::default()) { diff --git a/examples/custom_format.rs b/examples/custom_format.rs index cc16b336..c9b4150b 100644 --- a/examples/custom_format.rs +++ b/examples/custom_format.rs @@ -17,7 +17,7 @@ $ export MY_LOG_STYLE=never If you want to control the logging output completely, see the `custom_logger` example. */ -#[cfg(all(feature = "color", feature = "humantime"))] +#[cfg(all(feature = "color", feature = "chrono"))] fn main() { use env_logger::{fmt::Color, Builder, Env}; @@ -50,5 +50,5 @@ fn main() { log::info!("a log from `MyLogger`"); } -#[cfg(not(all(feature = "color", feature = "humantime")))] +#[cfg(not(all(feature = "color", feature = "chrono")))] fn main() {} diff --git a/src/fmt/chrono/extern_impl.rs b/src/fmt/chrono/extern_impl.rs new file mode 100644 index 00000000..2e3667be --- /dev/null +++ b/src/fmt/chrono/extern_impl.rs @@ -0,0 +1,133 @@ +use std::fmt; + +use chrono::{DateTime, SecondsFormat, Utc}; + +use crate::fmt::{Formatter, TimestampFormat, TimestampPrecision}; + +pub(in crate::fmt) mod glob { + pub use super::*; +} + +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: Utc::now(), + precision: Default::default(), + format: Default::default(), + } + } + + /// Get a [`Timestamp`] for the current date and time in UTC with a specified style and precision. + /// + /// # Examples + /// + /// Include the current timestamp, in a 12-hour format to second precision, with the log record: + /// + /// ``` + /// use std::io::Write; + /// use env_logger::fmt; + /// + /// let mut builder = env_logger::Builder::new(); + /// + /// builder.format(|buf, record| { + /// let ts = buf.timestamp_custom(fmt::TimestampPrecision::Seconds, fmt::TimestampFormat::Human12Hour); + /// + /// writeln!(buf, "{}: {}: {}", ts, record.level(), record.args()) + /// }); + /// ``` + /// + /// [`Timestamp`]: struct.Timestamp.html + pub fn timestamp_custom( + &self, + precision: TimestampPrecision, + format: TimestampFormat, + ) -> Timestamp { + Timestamp { + time: Utc::now(), + precision, + format, + } + } +} + +/// An formatted timestamp. +/// +/// The timestamp implements [`Display`] and can be written to a [`Formatter`]. This defaults to formatting with [RFC3339] with second precision. +/// +/// [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: DateTime, + precision: TimestampPrecision, + format: TimestampFormat, +} + +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 { + match self.format { + TimestampFormat::RFC3339 => self + .time + .to_rfc3339_opts( + match self.precision { + TimestampPrecision::Seconds => SecondsFormat::Secs, + TimestampPrecision::Millis => SecondsFormat::Millis, + TimestampPrecision::Micros => SecondsFormat::Micros, + TimestampPrecision::Nanos => SecondsFormat::Nanos, + }, + true, + ) + .fmt(f), + TimestampFormat::Human12Hour => { + if self.precision != TimestampPrecision::Seconds { + panic!("Sorry, currently with the new human timestamp formats, we only support second precision."); + } + + self.time.format("%v %r").fmt(f) + } + TimestampFormat::Human24Hour => { + if self.precision != TimestampPrecision::Seconds { + panic!("Sorry, currently with the new human timestamp formats, we only support second precision."); + } + + self.time.format("%v %X").fmt(f) + } + } + } +} diff --git a/src/fmt/chrono/mod.rs b/src/fmt/chrono/mod.rs new file mode 100644 index 00000000..0776fef6 --- /dev/null +++ b/src/fmt/chrono/mod.rs @@ -0,0 +1,11 @@ +/* +This internal module contains the timestamp implementation. + +Its public API is available when the `chrono` crate is available. +*/ + +#[cfg_attr(feature = "chrono", path = "extern_impl.rs")] +#[cfg_attr(not(feature = "chrono"), path = "shim_impl.rs")] +mod imp; + +pub(in crate::fmt) use self::imp::*; diff --git a/src/fmt/chrono/shim_impl.rs b/src/fmt/chrono/shim_impl.rs new file mode 100644 index 00000000..567e6c4a --- /dev/null +++ b/src/fmt/chrono/shim_impl.rs @@ -0,0 +1,5 @@ +/* +Timestamps aren't available when we don't have a `chrono` dependency. +*/ + +pub(in crate::fmt) mod glob {} diff --git a/src/fmt/humantime/extern_impl.rs b/src/fmt/humantime/extern_impl.rs deleted file mode 100644 index bdf165c4..00000000 --- a/src/fmt/humantime/extern_impl.rs +++ /dev/null @@ -1,118 +0,0 @@ -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}; - -pub(in crate::fmt) mod glob { - pub use super::*; -} - -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) - } -} diff --git a/src/fmt/humantime/mod.rs b/src/fmt/humantime/mod.rs deleted file mode 100644 index ac23ae24..00000000 --- a/src/fmt/humantime/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -/* -This internal module contains the timestamp implementation. - -Its public API is available when the `humantime` crate is available. -*/ - -#[cfg_attr(feature = "humantime", path = "extern_impl.rs")] -#[cfg_attr(not(feature = "humantime"), path = "shim_impl.rs")] -mod imp; - -pub(in crate::fmt) use self::imp::*; diff --git a/src/fmt/humantime/shim_impl.rs b/src/fmt/humantime/shim_impl.rs deleted file mode 100644 index 906bf9e4..00000000 --- a/src/fmt/humantime/shim_impl.rs +++ /dev/null @@ -1,5 +0,0 @@ -/* -Timestamps aren't available when we don't have a `humantime` dependency. -*/ - -pub(in crate::fmt) mod glob {} diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 86c093f0..1ac866ed 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -37,10 +37,10 @@ use std::{fmt, io, mem}; use log::Record; -mod humantime; +mod chrono; pub(crate) mod writer; -pub use self::humantime::glob::*; +pub use self::chrono::glob::*; pub use self::writer::glob::*; use self::writer::{Buffer, Writer}; @@ -54,7 +54,7 @@ pub(crate) mod glob { /// 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)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum TimestampPrecision { /// Full second precision (0 decimal digits) Seconds, @@ -73,6 +73,52 @@ impl Default for TimestampPrecision { } } +/// Standard used for formatting of timestamps. +/// +/// RFC339 is the default and is a widely used internet standard, however it is not particularly human-readable. +/// Currently, we also support human-readable formats with either a 12 or 24 hour clock. Note that this currently only works with `TimestampPrecision::Seconds`. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum TimestampFormat { + /// Full RFC3339 conformance + RFC3339, + /// A human-readable standard of the form "YYYY:MM:DD HH:MM:SS AM/PM". + Human12Hour, + /// A human-readable standard of the form "YYYY:MM:DD HH:MM:SS" + Human24Hour, +} + +/// The default timestamp style is the RFC3339 standard. +impl Default for TimestampFormat { + fn default() -> Self { + TimestampFormat::RFC3339 + } +} + +/// A styled timestamp, with precision and formatting. +/// +/// Defaults to `TimestampFormat::RFC3339` and `TimestampPrecision::Seconds`. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +pub struct TimestampStyle { + /// The format used for this style. + /// + /// Determines how the timestamp is displayed. E.G. HH:MM:SS or YY:MM:DD + pub format: TimestampFormat, + + /// The precision used for this style. + /// + /// Determines how accurate the displayed timestamp is. + pub precision: TimestampPrecision, +} + +impl TimestampStyle { + /// Create a new `TimestampStyle` from a format and precision. + /// + /// Rust's constructors can also be used instead of this method. + pub fn new(format: TimestampFormat, precision: TimestampPrecision) -> Self { + Self { format, precision } + } +} + /// A formatter to write logs into. /// /// `Formatter` implements the standard [`Write`] trait for writing log records. @@ -139,7 +185,7 @@ impl fmt::Debug for Formatter { pub(crate) type FormatFn = Box io::Result<()> + Sync + Send>; pub(crate) struct Builder { - pub format_timestamp: Option, + pub format_timestamp: Option, pub format_module_path: bool, pub format_target: bool, pub format_level: bool, @@ -211,7 +257,7 @@ type SubtleStyle = &'static str; /// /// This format needs to work with any combination of crate features. struct DefaultFormat<'a> { - timestamp: Option, + timestamp: Option, module_path: bool, target: bool, level: bool, @@ -282,20 +328,19 @@ impl<'a> DefaultFormat<'a> { } fn write_timestamp(&mut self) -> io::Result<()> { - #[cfg(feature = "humantime")] + #[cfg(feature = "chrono")] { - 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(), + // Make sure that timestamp is defined, otherwise omit writing the timestamp. + let ts = if let Some(timestamp) = self.timestamp { + self.buf + .timestamp_custom(timestamp.precision, timestamp.format) + } else { + return Ok(()); }; self.write_header_value(ts) } - #[cfg(not(feature = "humantime"))] + #[cfg(not(feature = "chrono"))] { // Trick the compiler to think we have used self.timestamp // Workaround for "field is never used: `timestamp`" compiler nag. diff --git a/src/lib.rs b/src/lib.rs index 59fa2a3e..e5f2551d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -286,6 +286,7 @@ use std::{borrow::Cow, cell::RefCell, env, io}; +use fmt::TimestampFormat; use log::{LevelFilter, Log, Metadata, Record, SetLoggerError}; pub mod filter; @@ -617,30 +618,58 @@ 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.format_timestamp = timestamp; + /// Configures if timestamp should be included and in what precision and style. + pub fn format_timestamp(&mut self, style: Option) -> &mut Self { + self.format.format_timestamp = style; self } /// Configures the timestamp to use second precision. pub fn format_timestamp_secs(&mut self) -> &mut Self { - self.format_timestamp(Some(fmt::TimestampPrecision::Seconds)) + self.format_timestamp(Some(fmt::TimestampStyle::new( + TimestampFormat::default(), + TimestampPrecision::Seconds, + ))) } /// Configures the timestamp to use millisecond precision. pub fn format_timestamp_millis(&mut self) -> &mut Self { - self.format_timestamp(Some(fmt::TimestampPrecision::Millis)) + self.format_timestamp(Some(fmt::TimestampStyle::new( + TimestampFormat::default(), + TimestampPrecision::Millis, + ))) } /// Configures the timestamp to use microsecond precision. pub fn format_timestamp_micros(&mut self) -> &mut Self { - self.format_timestamp(Some(fmt::TimestampPrecision::Micros)) + self.format_timestamp(Some(fmt::TimestampStyle::new( + TimestampFormat::default(), + TimestampPrecision::Micros, + ))) } /// Configures the timestamp to use nanosecond precision. pub fn format_timestamp_nanos(&mut self) -> &mut Self { - self.format_timestamp(Some(fmt::TimestampPrecision::Nanos)) + self.format_timestamp(Some(fmt::TimestampStyle::new( + TimestampFormat::default(), + TimestampPrecision::Nanos, + ))) + } + + /// Configures the timestamp to use second precision, formatted as a 12-hour clock. + pub fn format_timestamp_12hour(&mut self) -> &mut Self { + self.format_timestamp(Some(fmt::TimestampStyle::new( + TimestampFormat::Human12Hour, + TimestampPrecision::Seconds, + ))) + } + + /// Configures the timestamp to use second precision, formatted as a 24-hour clock. + pub fn format_timestamp_24hour(&mut self) -> &mut Self { + self.format_timestamp(Some(fmt::TimestampStyle::new( + TimestampFormat::Human24Hour, + TimestampPrecision::Seconds, + ))) } /// Configures the end of line suffix.