blob: 60d57fd0bc75cf89c092060deae880786e1a637a [file] [log] [blame]
use crate::fmt::{format::Writer, time::FormatTime, writer::WriteAdaptor};
use std::fmt;
use time::{format_description::well_known, formatting::Formattable, OffsetDateTime, UtcOffset};
/// Formats the current [local time] using a [formatter] from the [`time` crate].
///
/// To format the current [UTC time] instead, use the [`UtcTime`] type.
///
/// <div class="example-wrap" style="display:inline-block">
/// <pre class="compile_fail" style="white-space:normal;font:inherit;">
/// <strong>Warning</strong>: The <a href = "https://docs.rs/time/0.3/time/"><code>time</code>
/// crate</a> must be compiled with <code>--cfg unsound_local_offset</code> in order to use
/// local timestamps. When this cfg is not enabled, local timestamps cannot be recorded, and
/// events will be logged without timestamps.
///
/// Alternatively, [`OffsetTime`] can log with a local offset if it is initialized early.
///
/// See the <a href="https://docs.rs/time/0.3.4/time/#feature-flags"><code>time</code>
/// documentation</a> for more details.
/// </pre></div>
///
/// [local time]: time::OffsetDateTime::now_local
/// [UTC time]: time::OffsetDateTime::now_utc
/// [formatter]: time::formatting::Formattable
/// [`time` crate]: time
#[derive(Clone, Debug)]
#[cfg_attr(
docsrs,
doc(cfg(all(unsound_local_offset, feature = "time", feature = "local-time")))
)]
#[cfg(feature = "local-time")]
pub struct LocalTime<F> {
format: F,
}
/// Formats the current [UTC time] using a [formatter] from the [`time` crate].
///
/// To format the current [local time] instead, use the [`LocalTime`] type.
///
/// [local time]: time::OffsetDateTime::now_local
/// [UTC time]: time::OffsetDateTime::now_utc
/// [formatter]: time::formatting::Formattable
/// [`time` crate]: time
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
#[derive(Clone, Debug)]
pub struct UtcTime<F> {
format: F,
}
/// Formats the current time using a fixed offset and a [formatter] from the [`time` crate].
///
/// This is typically used as an alternative to [`LocalTime`]. `LocalTime` determines the offset
/// every time it formats a message, which may be unsound or fail. With `OffsetTime`, the offset is
/// determined once. This makes it possible to do so while the program is still single-threaded and
/// handle any errors. However, this also means the offset cannot change while the program is
/// running (the offset will not change across DST changes).
///
/// [formatter]: time::formatting::Formattable
/// [`time` crate]: time
#[derive(Clone, Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
pub struct OffsetTime<F> {
offset: time::UtcOffset,
format: F,
}
// === impl LocalTime ===
#[cfg(feature = "local-time")]
impl LocalTime<well_known::Rfc3339> {
/// Returns a formatter that formats the current [local time] in the
/// [RFC 3339] format (a subset of the [ISO 8601] timestamp format).
///
/// # Examples
///
/// ```
/// use tracing_subscriber::fmt::{self, time};
///
/// let collector = tracing_subscriber::fmt()
/// .with_timer(time::LocalTime::rfc_3339());
/// # drop(collector);
/// ```
///
/// [local time]: time::OffsetDateTime::now_local
/// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
/// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
pub fn rfc_3339() -> Self {
Self::new(well_known::Rfc3339)
}
}
#[cfg(feature = "local-time")]
impl<F: Formattable> LocalTime<F> {
/// Returns a formatter that formats the current [local time] using the
/// [`time` crate] with the provided provided format. The format may be any
/// type that implements the [`Formattable`] trait.
///
///
/// <div class="example-wrap" style="display:inline-block">
/// <pre class="compile_fail" style="white-space:normal;font:inherit;">
/// <strong>Warning</strong>: The <a href = "https://docs.rs/time/0.3/time/">
/// <code>time</code> crate</a> must be compiled with <code>--cfg
/// unsound_local_offset</code> in order to use local timestamps. When this
/// cfg is not enabled, local timestamps cannot be recorded, and
/// events will be logged without timestamps.
///
/// See the <a href="https://docs.rs/time/0.3.4/time/#feature-flags">
/// <code>time</code> documentation</a> for more details.
/// </pre></div>
///
/// Typically, the format will be a format description string, or one of the
/// `time` crate's [well-known formats].
///
/// If the format description is statically known, then the
/// [`format_description!`] macro should be used. This is identical to the
/// [`time::format_description::parse`] method, but runs at compile-time,
/// throwing an error if the format description is invalid. If the desired format
/// is not known statically (e.g., a user is providing a format string), then the
/// [`time::format_description::parse`] method should be used. Note that this
/// method is fallible.
///
/// See the [`time` book] for details on the format description syntax.
///
/// # Examples
///
/// Using the [`format_description!`] macro:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::LocalTime};
/// use time::macros::format_description;
///
/// let timer = LocalTime::new(format_description!("[hour]:[minute]:[second]"));
/// let collector = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(collector);
/// ```
///
/// Using [`time::format_description::parse`]:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::LocalTime};
///
/// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
/// .expect("format string should be valid!");
/// let timer = LocalTime::new(time_format);
/// let collector = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(collector);
/// ```
///
/// Using the [`format_description!`] macro requires enabling the `time`
/// crate's "macros" feature flag.
///
/// Using a [well-known format][well-known formats] (this is equivalent to
/// [`LocalTime::rfc_3339`]):
///
/// ```
/// use tracing_subscriber::fmt::{self, time::LocalTime};
///
/// let timer = LocalTime::new(time::format_description::well_known::Rfc3339);
/// let collector = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(collector);
/// ```
///
/// [local time]: time::OffsetDateTime::now_local()
/// [`time` crate]: time
/// [`Formattable`]: time::formatting::Formattable
/// [well-known formats]: time::format_description::well_known
/// [`format_description!`]: time::macros::format_description!
/// [`time::format_description::parse`]: time::format_description::parse()
/// [`time` book]: https://time-rs.github.io/book/api/format-description.html
pub fn new(format: F) -> Self {
Self { format }
}
}
#[cfg(feature = "local-time")]
impl<F> FormatTime for LocalTime<F>
where
F: Formattable,
{
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
let now = OffsetDateTime::now_local().map_err(|_| fmt::Error)?;
format_datetime(now, w, &self.format)
}
}
#[cfg(feature = "local-time")]
impl<F> Default for LocalTime<F>
where
F: Formattable + Default,
{
fn default() -> Self {
Self::new(F::default())
}
}
// === impl UtcTime ===
impl UtcTime<well_known::Rfc3339> {
/// Returns a formatter that formats the current [UTC time] in the
/// [RFC 3339] format, which is a subset of the [ISO 8601] timestamp format.
///
/// # Examples
///
/// ```
/// use tracing_subscriber::fmt::{self, time};
///
/// let collector = tracing_subscriber::fmt()
/// .with_timer(time::UtcTime::rfc_3339());
/// # drop(collector);
/// ```
///
/// [local time]: time::OffsetDateTime::now_utc
/// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
/// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
pub fn rfc_3339() -> Self {
Self::new(well_known::Rfc3339)
}
}
impl<F: Formattable> UtcTime<F> {
/// Returns a formatter that formats the current [UTC time] using the
/// [`time` crate], with the provided provided format. The format may be any
/// type that implements the [`Formattable`] trait.
///
/// Typically, the format will be a format description string, or one of the
/// `time` crate's [well-known formats].
///
/// If the format description is statically known, then the
/// [`format_description!`] macro should be used. This is identical to the
/// [`time::format_description::parse`] method, but runs at compile-time,
/// failing an error if the format description is invalid. If the desired format
/// is not known statically (e.g., a user is providing a format string), then the
/// [`time::format_description::parse`] method should be used. Note that this
/// method is fallible.
///
/// See the [`time` book] for details on the format description syntax.
///
/// # Examples
///
/// Using the [`format_description!`] macro:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::UtcTime};
/// use time::macros::format_description;
///
/// let timer = UtcTime::new(format_description!("[hour]:[minute]:[second]"));
/// let collector = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(collector);
/// ```
///
/// Using the [`format_description!`] macro requires enabling the `time`
/// crate's "macros" feature flag.
///
/// Using [`time::format_description::parse`]:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::UtcTime};
///
/// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
/// .expect("format string should be valid!");
/// let timer = UtcTime::new(time_format);
/// let collector = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(collector);
/// ```
///
/// Using a [well-known format][well-known formats] (this is equivalent to
/// [`UtcTime::rfc_3339`]):
///
/// ```
/// use tracing_subscriber::fmt::{self, time::UtcTime};
///
/// let timer = UtcTime::new(time::format_description::well_known::Rfc3339);
/// let collector = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(collector);
/// ```
///
/// [UTC time]: time::OffsetDateTime::now_utc()
/// [`time` crate]: time
/// [`Formattable`]: time::formatting::Formattable
/// [well-known formats]: time::format_description::well_known
/// [`format_description!`]: time::macros::format_description!
/// [`time::format_description::parse`]: time::format_description::parse
/// [`time` book]: https://time-rs.github.io/book/api/format-description.html
pub fn new(format: F) -> Self {
Self { format }
}
}
impl<F> FormatTime for UtcTime<F>
where
F: Formattable,
{
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
format_datetime(OffsetDateTime::now_utc(), w, &self.format)
}
}
impl<F> Default for UtcTime<F>
where
F: Formattable + Default,
{
fn default() -> Self {
Self::new(F::default())
}
}
// === impl OffsetTime ===
#[cfg(feature = "local-time")]
impl OffsetTime<well_known::Rfc3339> {
/// Returns a formatter that formats the current time using the [local time offset] in the [RFC
/// 3339] format (a subset of the [ISO 8601] timestamp format).
///
/// Returns an error if the local time offset cannot be determined. This typically occurs in
/// multithreaded programs. To avoid this problem, initialize `OffsetTime` before forking
/// threads. When using Tokio, this means initializing `OffsetTime` before the Tokio runtime.
///
/// # Examples
///
/// ```
/// use tracing_subscriber::fmt::{self, time};
///
/// let collector = tracing_subscriber::fmt()
/// .with_timer(time::OffsetTime::local_rfc_3339().expect("could not get local offset!"));
/// # drop(collector);
/// ```
///
/// Using `OffsetTime` with Tokio:
///
/// ```
/// use tracing_subscriber::fmt::time::OffsetTime;
///
/// #[tokio::main]
/// async fn run() {
/// tracing::info!("runtime initialized");
///
/// // At this point the Tokio runtime is initialized, and we can use both Tokio and Tracing
/// // normally.
/// }
///
/// fn main() {
/// // Because we need to get the local offset before Tokio spawns any threads, our `main`
/// // function cannot use `tokio::main`.
/// tracing_subscriber::fmt()
/// .with_timer(OffsetTime::local_rfc_3339().expect("could not get local time offset"))
/// .init();
///
/// // Even though `run` is written as an `async fn`, because we used `tokio::main` on it
/// // we can call it as a synchronous function.
/// run();
/// }
/// ```
///
/// [local time offset]: time::UtcOffset::current_local_offset
/// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
/// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
pub fn local_rfc_3339() -> Result<Self, time::error::IndeterminateOffset> {
Ok(Self::new(
UtcOffset::current_local_offset()?,
well_known::Rfc3339,
))
}
}
impl<F: time::formatting::Formattable> OffsetTime<F> {
/// Returns a formatter that formats the current time using the [`time` crate] with the provided
/// provided format and [timezone offset]. The format may be any type that implements the
/// [`Formattable`] trait.
///
///
/// Typically, the offset will be the [local offset], and format will be a format description
/// string, or one of the `time` crate's [well-known formats].
///
/// If the format description is statically known, then the
/// [`format_description!`] macro should be used. This is identical to the
/// [`time::format_description::parse`] method, but runs at compile-time,
/// throwing an error if the format description is invalid. If the desired format
/// is not known statically (e.g., a user is providing a format string), then the
/// [`time::format_description::parse`] method should be used. Note that this
/// method is fallible.
///
/// See the [`time` book] for details on the format description syntax.
///
/// # Examples
///
/// Using the [`format_description!`] macro:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::OffsetTime};
/// use time::macros::format_description;
/// use time::UtcOffset;
///
/// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
/// let timer = OffsetTime::new(offset, format_description!("[hour]:[minute]:[second]"));
/// let collector = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(collector);
/// ```
///
/// Using [`time::format_description::parse`]:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::OffsetTime};
/// use time::UtcOffset;
///
/// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
/// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
/// .expect("format string should be valid!");
/// let timer = OffsetTime::new(offset, time_format);
/// let collector = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(collector);
/// ```
///
/// Using the [`format_description!`] macro requires enabling the `time`
/// crate's "macros" feature flag.
///
/// Using a [well-known format][well-known formats] (this is equivalent to
/// [`OffsetTime::local_rfc_3339`]):
///
/// ```
/// use tracing_subscriber::fmt::{self, time::OffsetTime};
/// use time::UtcOffset;
///
/// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
/// let timer = OffsetTime::new(offset, time::format_description::well_known::Rfc3339);
/// let collector = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(collector);
/// ```
///
/// [`time` crate]: time
/// [timezone offset]: time::UtcOffset
/// [`Formattable`]: time::formatting::Formattable
/// [local offset]: time::UtcOffset::current_local_offset()
/// [well-known formats]: time::format_description::well_known
/// [`format_description!`]: time::macros::format_description
/// [`time::format_description::parse`]: time::format_description::parse
/// [`time` book]: https://time-rs.github.io/book/api/format-description.html
pub fn new(offset: time::UtcOffset, format: F) -> Self {
Self { offset, format }
}
}
impl<F> FormatTime for OffsetTime<F>
where
F: time::formatting::Formattable,
{
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
let now = OffsetDateTime::now_utc().to_offset(self.offset);
format_datetime(now, w, &self.format)
}
}
fn format_datetime(
now: OffsetDateTime,
into: &mut Writer<'_>,
fmt: &impl Formattable,
) -> fmt::Result {
let mut into = WriteAdaptor::new(into);
now.format_into(&mut into, fmt)
.map_err(|_| fmt::Error)
.map(|_| ())
}