-
Notifications
You must be signed in to change notification settings - Fork 733
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[tracing-subscriber]: add chrono crate implementations of FormatTime (#…
…2690) Issue #2080 explains that it's not possible to soundly use [`tracing_subscriber::fmt::time::LocalTime`](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/time/struct.LocalTime.html) in a multithreaded context. It proposes adding alternative time formatters that use the [chrono crate](https://docs.rs/chrono/latest/chrono/) to workaround which is what this PR offers. A new source file 'chrono_crate.rs' is added to the 'tracing-subscriber' package implementing `mod chrono_crate` providing two new tag types `LocalTime` and `Utc` with associated `time::FormatTime` trait implementations that call `chrono::Local::now().to_rfc3339()` and `chrono::Utc::now().to_rfc3339()` respectively. Simple unit-tests of the new functionality accompany the additions. --------- Co-authored-by: David Barsky <me@davidbarsky.com> Co-authored-by: Shayne Fletcher <shaynefletcher@meta.com>
- Loading branch information
1 parent
88d3f9a
commit a023c42
Showing
3 changed files
with
191 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
use crate::fmt::format::Writer; | ||
use crate::fmt::time::FormatTime; | ||
|
||
use std::sync::Arc; | ||
|
||
/// Formats [local time]s and [UTC time]s with `FormatTime` implementations | ||
/// that use the [`chrono` crate]. | ||
/// | ||
/// [local time]: [`chrono::offset::Local`] | ||
/// [UTC time]: [`chrono::offset::Utc`] | ||
/// [`chrono` crate]: [`chrono`] | ||
/// Formats the current [local time] using a [formatter] from the [`chrono`] crate. | ||
/// | ||
/// [local time]: chrono::Local::now() | ||
/// [formatter]: chrono::format | ||
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] | ||
#[derive(Debug, Clone, Eq, PartialEq, Default)] | ||
pub struct ChronoLocal { | ||
format: Arc<ChronoFmtType>, | ||
} | ||
|
||
impl ChronoLocal { | ||
/// Format the time using the [`RFC 3339`] format | ||
/// (a subset of [`ISO 8601`]). | ||
/// | ||
/// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339 | ||
/// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601 | ||
pub fn rfc_3339() -> Self { | ||
Self { | ||
format: Arc::new(ChronoFmtType::Rfc3339), | ||
} | ||
} | ||
|
||
/// Format the time using the given format string. | ||
/// | ||
/// See [`chrono::format::strftime`] for details on the supported syntax. | ||
pub fn new(format_string: String) -> Self { | ||
Self { | ||
format: Arc::new(ChronoFmtType::Custom(format_string)), | ||
} | ||
} | ||
} | ||
|
||
impl FormatTime for ChronoLocal { | ||
fn format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result { | ||
let t = chrono::Local::now(); | ||
match self.format.as_ref() { | ||
ChronoFmtType::Rfc3339 => { | ||
use chrono::format::{Fixed, Item}; | ||
write!( | ||
w, | ||
"{}", | ||
t.format_with_items(core::iter::once(Item::Fixed(Fixed::RFC3339))) | ||
) | ||
} | ||
ChronoFmtType::Custom(fmt) => { | ||
write!(w, "{}", t.format(fmt)) | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Formats the current [UTC time] using a [formatter] from the [`chrono`] crate. | ||
/// | ||
/// [UTC time]: chrono::Utc::now() | ||
/// [formatter]: chrono::format | ||
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] | ||
#[derive(Debug, Clone, Eq, PartialEq, Default)] | ||
pub struct ChronoUtc { | ||
format: Arc<ChronoFmtType>, | ||
} | ||
|
||
impl ChronoUtc { | ||
/// Format the time using the [`RFC 3339`] format | ||
/// (a subset of [`ISO 8601`]). | ||
/// | ||
/// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339 | ||
/// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601 | ||
pub fn rfc_3339() -> Self { | ||
Self { | ||
format: Arc::new(ChronoFmtType::Rfc3339), | ||
} | ||
} | ||
|
||
/// Format the time using the given format string. | ||
/// | ||
/// See [`chrono::format::strftime`] for details on the supported syntax. | ||
pub fn new(format_string: String) -> Self { | ||
Self { | ||
format: Arc::new(ChronoFmtType::Custom(format_string)), | ||
} | ||
} | ||
} | ||
|
||
impl FormatTime for ChronoUtc { | ||
fn format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result { | ||
let t = chrono::Utc::now(); | ||
match self.format.as_ref() { | ||
ChronoFmtType::Rfc3339 => w.write_str(&t.to_rfc3339()), | ||
ChronoFmtType::Custom(fmt) => w.write_str(&format!("{}", t.format(fmt))), | ||
} | ||
} | ||
} | ||
|
||
/// The RFC 3339 format is used by default but a custom format string | ||
/// can be used. See [`chrono::format::strftime`]for details on | ||
/// the supported syntax. | ||
/// | ||
/// [`chrono::format::strftime`]: https://docs.rs/chrono/0.4.9/chrono/format/strftime/index.html | ||
#[derive(Debug, Clone, Eq, PartialEq)] | ||
enum ChronoFmtType { | ||
/// Format according to the RFC 3339 convention. | ||
Rfc3339, | ||
/// Format according to a custom format string. | ||
Custom(String), | ||
} | ||
|
||
impl Default for ChronoFmtType { | ||
fn default() -> Self { | ||
ChronoFmtType::Rfc3339 | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::fmt::format::Writer; | ||
use crate::fmt::time::FormatTime; | ||
|
||
use std::sync::Arc; | ||
|
||
use super::ChronoFmtType; | ||
use super::ChronoLocal; | ||
use super::ChronoUtc; | ||
|
||
#[test] | ||
fn test_chrono_format_time_utc_default() { | ||
let mut buf = String::new(); | ||
let mut dst: Writer<'_> = Writer::new(&mut buf); | ||
assert!(FormatTime::format_time(&ChronoUtc::default(), &mut dst).is_ok()); | ||
// e.g. `buf` contains "2023-08-18T19:05:08.662499+00:00" | ||
assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok()); | ||
} | ||
|
||
#[test] | ||
fn test_chrono_format_time_utc_custom() { | ||
let fmt = ChronoUtc { | ||
format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())), | ||
}; | ||
let mut buf = String::new(); | ||
let mut dst: Writer<'_> = Writer::new(&mut buf); | ||
assert!(FormatTime::format_time(&fmt, &mut dst).is_ok()); | ||
// e.g. `buf` contains "Wed Aug 23 15:53:23 2023" | ||
assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok()); | ||
} | ||
|
||
#[test] | ||
fn test_chrono_format_time_local_default() { | ||
let mut buf = String::new(); | ||
let mut dst: Writer<'_> = Writer::new(&mut buf); | ||
assert!(FormatTime::format_time(&ChronoLocal::default(), &mut dst).is_ok()); | ||
// e.g. `buf` contains "2023-08-18T14:59:08.662499-04:00". | ||
assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok()); | ||
} | ||
|
||
#[test] | ||
fn test_chrono_format_time_local_custom() { | ||
let fmt = ChronoLocal { | ||
format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())), | ||
}; | ||
let mut buf = String::new(); | ||
let mut dst: Writer<'_> = Writer::new(&mut buf); | ||
assert!(FormatTime::format_time(&fmt, &mut dst).is_ok()); | ||
// e.g. `buf` contains "Wed Aug 23 15:55:46 2023". | ||
assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters