• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::fmt::{format::Writer, time::FormatTime, writer::WriteAdaptor};
2 use std::fmt;
3 use time::{format_description::well_known, formatting::Formattable, OffsetDateTime, UtcOffset};
4 
5 /// Formats the current [local time] using a [formatter] from the [`time` crate].
6 ///
7 /// To format the current [UTC time] instead, use the [`UtcTime`] type.
8 ///
9 /// <div class="example-wrap" style="display:inline-block">
10 /// <pre class="compile_fail" style="white-space:normal;font:inherit;">
11 ///     <strong>Warning</strong>: The <a href = "https://docs.rs/time/0.3/time/"><code>time</code>
12 ///     crate</a> must be compiled with <code>--cfg unsound_local_offset</code> in order to use
13 ///     local timestamps. When this cfg is not enabled, local timestamps cannot be recorded, and
14 ///     events will be logged without timestamps.
15 ///
16 ///    Alternatively, [`OffsetTime`] can log with a local offset if it is initialized early.
17 ///
18 ///    See the <a href="https://docs.rs/time/0.3.4/time/#feature-flags"><code>time</code>
19 ///    documentation</a> for more details.
20 /// </pre></div>
21 ///
22 /// [local time]: time::OffsetDateTime::now_local
23 /// [UTC time]:     time::OffsetDateTime::now_utc
24 /// [formatter]:    time::formatting::Formattable
25 /// [`time` crate]: time
26 #[derive(Clone, Debug)]
27 #[cfg_attr(
28     docsrs,
29     doc(cfg(all(unsound_local_offset, feature = "time", feature = "local-time")))
30 )]
31 #[cfg(feature = "local-time")]
32 pub struct LocalTime<F> {
33     format: F,
34 }
35 
36 /// Formats the current [UTC time] using a [formatter] from the [`time` crate].
37 ///
38 /// To format the current [local time] instead, use the [`LocalTime`] type.
39 ///
40 /// [local time]: time::OffsetDateTime::now_local
41 /// [UTC time]:     time::OffsetDateTime::now_utc
42 /// [formatter]:    time::formatting::Formattable
43 /// [`time` crate]: time
44 #[cfg_attr(docsrs, doc(cfg(feature = "time")))]
45 #[derive(Clone, Debug)]
46 pub struct UtcTime<F> {
47     format: F,
48 }
49 
50 /// Formats the current time using a fixed offset and a [formatter] from the [`time` crate].
51 ///
52 /// This is typically used as an alternative to [`LocalTime`]. `LocalTime` determines the offset
53 /// every time it formats a message, which may be unsound or fail. With `OffsetTime`, the offset is
54 /// determined once. This makes it possible to do so while the program is still single-threaded and
55 /// handle any errors. However, this also means the offset cannot change while the program is
56 /// running (the offset will not change across DST changes).
57 ///
58 /// [formatter]: time::formatting::Formattable
59 /// [`time` crate]: time
60 #[derive(Clone, Debug)]
61 #[cfg_attr(docsrs, doc(cfg(feature = "time")))]
62 pub struct OffsetTime<F> {
63     offset: time::UtcOffset,
64     format: F,
65 }
66 
67 // === impl LocalTime ===
68 
69 #[cfg(feature = "local-time")]
70 impl LocalTime<well_known::Rfc3339> {
71     /// Returns a formatter that formats the current [local time] in the
72     /// [RFC 3339] format (a subset of the [ISO 8601] timestamp format).
73     ///
74     /// # Examples
75     ///
76     /// ```
77     /// use tracing_subscriber::fmt::{self, time};
78     ///
79     /// let collector = tracing_subscriber::fmt()
80     ///     .with_timer(time::LocalTime::rfc_3339());
81     /// # drop(collector);
82     /// ```
83     ///
84     /// [local time]: time::OffsetDateTime::now_local
85     /// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
86     /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
rfc_3339() -> Self87     pub fn rfc_3339() -> Self {
88         Self::new(well_known::Rfc3339)
89     }
90 }
91 
92 #[cfg(feature = "local-time")]
93 impl<F: Formattable> LocalTime<F> {
94     /// Returns a formatter that formats the current [local time] using the
95     /// [`time` crate] with the provided provided format. The format may be any
96     /// type that implements the [`Formattable`] trait.
97     ///
98     ///
99     /// <div class="example-wrap" style="display:inline-block">
100     /// <pre class="compile_fail" style="white-space:normal;font:inherit;">
101     ///     <strong>Warning</strong>: The <a href = "https://docs.rs/time/0.3/time/">
102     ///     <code>time</code> crate</a> must be compiled with <code>--cfg
103     ///     unsound_local_offset</code> in order to use local timestamps. When this
104     ///     cfg is not enabled, local timestamps cannot be recorded, and
105     ///     events will be logged without timestamps.
106     ///
107     ///    See the <a href="https://docs.rs/time/0.3.4/time/#feature-flags">
108     ///    <code>time</code> documentation</a> for more details.
109     /// </pre></div>
110     ///
111     /// Typically, the format will be a format description string, or one of the
112     /// `time` crate's [well-known formats].
113     ///
114     /// If the format description is statically known, then the
115     /// [`format_description!`] macro should be used. This is identical to the
116     /// [`time::format_description::parse`] method, but runs at compile-time,
117     /// throwing an error if the format description is invalid. If the desired format
118     /// is not known statically (e.g., a user is providing a format string), then the
119     /// [`time::format_description::parse`] method should be used. Note that this
120     /// method is fallible.
121     ///
122     /// See the [`time` book] for details on the format description syntax.
123     ///
124     /// # Examples
125     ///
126     /// Using the [`format_description!`] macro:
127     ///
128     /// ```
129     /// use tracing_subscriber::fmt::{self, time::LocalTime};
130     /// use time::macros::format_description;
131     ///
132     /// let timer = LocalTime::new(format_description!("[hour]:[minute]:[second]"));
133     /// let collector = tracing_subscriber::fmt()
134     ///     .with_timer(timer);
135     /// # drop(collector);
136     /// ```
137     ///
138     /// Using [`time::format_description::parse`]:
139     ///
140     /// ```
141     /// use tracing_subscriber::fmt::{self, time::LocalTime};
142     ///
143     /// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
144     ///     .expect("format string should be valid!");
145     /// let timer = LocalTime::new(time_format);
146     /// let collector = tracing_subscriber::fmt()
147     ///     .with_timer(timer);
148     /// # drop(collector);
149     /// ```
150     ///
151     /// Using the [`format_description!`] macro requires enabling the `time`
152     /// crate's "macros" feature flag.
153     ///
154     /// Using a [well-known format][well-known formats] (this is equivalent to
155     /// [`LocalTime::rfc_3339`]):
156     ///
157     /// ```
158     /// use tracing_subscriber::fmt::{self, time::LocalTime};
159     ///
160     /// let timer = LocalTime::new(time::format_description::well_known::Rfc3339);
161     /// let collector = tracing_subscriber::fmt()
162     ///     .with_timer(timer);
163     /// # drop(collector);
164     /// ```
165     ///
166     /// [local time]: time::OffsetDateTime::now_local()
167     /// [`time` crate]: time
168     /// [`Formattable`]: time::formatting::Formattable
169     /// [well-known formats]: time::format_description::well_known
170     /// [`format_description!`]: time::macros::format_description!
171     /// [`time::format_description::parse`]: time::format_description::parse()
172     /// [`time` book]: https://time-rs.github.io/book/api/format-description.html
new(format: F) -> Self173     pub fn new(format: F) -> Self {
174         Self { format }
175     }
176 }
177 
178 #[cfg(feature = "local-time")]
179 impl<F> FormatTime for LocalTime<F>
180 where
181     F: Formattable,
182 {
format_time(&self, w: &mut Writer<'_>) -> fmt::Result183     fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
184         let now = OffsetDateTime::now_local().map_err(|_| fmt::Error)?;
185         format_datetime(now, w, &self.format)
186     }
187 }
188 
189 #[cfg(feature = "local-time")]
190 impl<F> Default for LocalTime<F>
191 where
192     F: Formattable + Default,
193 {
default() -> Self194     fn default() -> Self {
195         Self::new(F::default())
196     }
197 }
198 
199 // === impl UtcTime ===
200 
201 impl UtcTime<well_known::Rfc3339> {
202     /// Returns a formatter that formats the current [UTC time] in the
203     /// [RFC 3339] format, which is a subset of the [ISO 8601] timestamp format.
204     ///
205     /// # Examples
206     ///
207     /// ```
208     /// use tracing_subscriber::fmt::{self, time};
209     ///
210     /// let collector = tracing_subscriber::fmt()
211     ///     .with_timer(time::UtcTime::rfc_3339());
212     /// # drop(collector);
213     /// ```
214     ///
215     /// [local time]: time::OffsetDateTime::now_utc
216     /// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
217     /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
rfc_3339() -> Self218     pub fn rfc_3339() -> Self {
219         Self::new(well_known::Rfc3339)
220     }
221 }
222 
223 impl<F: Formattable> UtcTime<F> {
224     /// Returns a formatter that formats the current [UTC time] using the
225     /// [`time` crate], with the provided provided format. The format may be any
226     /// type that implements the [`Formattable`] trait.
227     ///
228     /// Typically, the format will be a format description string, or one of the
229     /// `time` crate's [well-known formats].
230     ///
231     /// If the format description is statically known, then the
232     /// [`format_description!`] macro should be used. This is identical to the
233     /// [`time::format_description::parse`] method, but runs at compile-time,
234     /// failing  an error if the format description is invalid. If the desired format
235     /// is not known statically (e.g., a user is providing a format string), then the
236     /// [`time::format_description::parse`] method should be used. Note that this
237     /// method is fallible.
238     ///
239     /// See the [`time` book] for details on the format description syntax.
240     ///
241     /// # Examples
242     ///
243     /// Using the [`format_description!`] macro:
244     ///
245     /// ```
246     /// use tracing_subscriber::fmt::{self, time::UtcTime};
247     /// use time::macros::format_description;
248     ///
249     /// let timer = UtcTime::new(format_description!("[hour]:[minute]:[second]"));
250     /// let collector = tracing_subscriber::fmt()
251     ///     .with_timer(timer);
252     /// # drop(collector);
253     /// ```
254     ///
255     /// Using the [`format_description!`] macro requires enabling the `time`
256     /// crate's "macros" feature flag.
257     ///
258     /// Using [`time::format_description::parse`]:
259     ///
260     /// ```
261     /// use tracing_subscriber::fmt::{self, time::UtcTime};
262     ///
263     /// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
264     ///     .expect("format string should be valid!");
265     /// let timer = UtcTime::new(time_format);
266     /// let collector = tracing_subscriber::fmt()
267     ///     .with_timer(timer);
268     /// # drop(collector);
269     /// ```
270     ///
271     /// Using a [well-known format][well-known formats] (this is equivalent to
272     /// [`UtcTime::rfc_3339`]):
273     ///
274     /// ```
275     /// use tracing_subscriber::fmt::{self, time::UtcTime};
276     ///
277     /// let timer = UtcTime::new(time::format_description::well_known::Rfc3339);
278     /// let collector = tracing_subscriber::fmt()
279     ///     .with_timer(timer);
280     /// # drop(collector);
281     /// ```
282     ///
283     /// [UTC time]: time::OffsetDateTime::now_utc()
284     /// [`time` crate]: time
285     /// [`Formattable`]: time::formatting::Formattable
286     /// [well-known formats]: time::format_description::well_known
287     /// [`format_description!`]: time::macros::format_description!
288     /// [`time::format_description::parse`]: time::format_description::parse
289     /// [`time` book]: https://time-rs.github.io/book/api/format-description.html
new(format: F) -> Self290     pub fn new(format: F) -> Self {
291         Self { format }
292     }
293 }
294 
295 impl<F> FormatTime for UtcTime<F>
296 where
297     F: Formattable,
298 {
format_time(&self, w: &mut Writer<'_>) -> fmt::Result299     fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
300         format_datetime(OffsetDateTime::now_utc(), w, &self.format)
301     }
302 }
303 
304 impl<F> Default for UtcTime<F>
305 where
306     F: Formattable + Default,
307 {
default() -> Self308     fn default() -> Self {
309         Self::new(F::default())
310     }
311 }
312 
313 // === impl OffsetTime ===
314 
315 #[cfg(feature = "local-time")]
316 impl OffsetTime<well_known::Rfc3339> {
317     /// Returns a formatter that formats the current time using the [local time offset] in the [RFC
318     /// 3339] format (a subset of the [ISO 8601] timestamp format).
319     ///
320     /// Returns an error if the local time offset cannot be determined. This typically occurs in
321     /// multithreaded programs. To avoid this problem, initialize `OffsetTime` before forking
322     /// threads. When using Tokio, this means initializing `OffsetTime` before the Tokio runtime.
323     ///
324     /// # Examples
325     ///
326     /// ```
327     /// use tracing_subscriber::fmt::{self, time};
328     ///
329     /// let collector = tracing_subscriber::fmt()
330     ///     .with_timer(time::OffsetTime::local_rfc_3339().expect("could not get local offset!"));
331     /// # drop(collector);
332     /// ```
333     ///
334     /// Using `OffsetTime` with Tokio:
335     ///
336     /// ```
337     /// use tracing_subscriber::fmt::time::OffsetTime;
338     ///
339     /// #[tokio::main]
340     /// async fn run() {
341     ///     tracing::info!("runtime initialized");
342     ///
343     ///     // At this point the Tokio runtime is initialized, and we can use both Tokio and Tracing
344     ///     // normally.
345     /// }
346     ///
347     /// fn main() {
348     ///     // Because we need to get the local offset before Tokio spawns any threads, our `main`
349     ///     // function cannot use `tokio::main`.
350     ///     tracing_subscriber::fmt()
351     ///         .with_timer(OffsetTime::local_rfc_3339().expect("could not get local time offset"))
352     ///         .init();
353     ///
354     ///     // Even though `run` is written as an `async fn`, because we used `tokio::main` on it
355     ///     // we can call it as a synchronous function.
356     ///     run();
357     /// }
358     /// ```
359     ///
360     /// [local time offset]: time::UtcOffset::current_local_offset
361     /// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
362     /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
local_rfc_3339() -> Result<Self, time::error::IndeterminateOffset>363     pub fn local_rfc_3339() -> Result<Self, time::error::IndeterminateOffset> {
364         Ok(Self::new(
365             UtcOffset::current_local_offset()?,
366             well_known::Rfc3339,
367         ))
368     }
369 }
370 
371 impl<F: time::formatting::Formattable> OffsetTime<F> {
372     /// Returns a formatter that formats the current time using the [`time` crate] with the provided
373     /// provided format and [timezone offset]. The format may be any type that implements the
374     /// [`Formattable`] trait.
375     ///
376     ///
377     /// Typically, the offset will be the [local offset], and format will be a format description
378     /// string, or one of the `time` crate's [well-known formats].
379     ///
380     /// If the format description is statically known, then the
381     /// [`format_description!`] macro should be used. This is identical to the
382     /// [`time::format_description::parse`] method, but runs at compile-time,
383     /// throwing an error if the format description is invalid. If the desired format
384     /// is not known statically (e.g., a user is providing a format string), then the
385     /// [`time::format_description::parse`] method should be used. Note that this
386     /// method is fallible.
387     ///
388     /// See the [`time` book] for details on the format description syntax.
389     ///
390     /// # Examples
391     ///
392     /// Using the [`format_description!`] macro:
393     ///
394     /// ```
395     /// use tracing_subscriber::fmt::{self, time::OffsetTime};
396     /// use time::macros::format_description;
397     /// use time::UtcOffset;
398     ///
399     /// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
400     /// let timer = OffsetTime::new(offset, format_description!("[hour]:[minute]:[second]"));
401     /// let collector = tracing_subscriber::fmt()
402     ///     .with_timer(timer);
403     /// # drop(collector);
404     /// ```
405     ///
406     /// Using [`time::format_description::parse`]:
407     ///
408     /// ```
409     /// use tracing_subscriber::fmt::{self, time::OffsetTime};
410     /// use time::UtcOffset;
411     ///
412     /// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
413     /// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
414     ///     .expect("format string should be valid!");
415     /// let timer = OffsetTime::new(offset, time_format);
416     /// let collector = tracing_subscriber::fmt()
417     ///     .with_timer(timer);
418     /// # drop(collector);
419     /// ```
420     ///
421     /// Using the [`format_description!`] macro requires enabling the `time`
422     /// crate's "macros" feature flag.
423     ///
424     /// Using a [well-known format][well-known formats] (this is equivalent to
425     /// [`OffsetTime::local_rfc_3339`]):
426     ///
427     /// ```
428     /// use tracing_subscriber::fmt::{self, time::OffsetTime};
429     /// use time::UtcOffset;
430     ///
431     /// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
432     /// let timer = OffsetTime::new(offset, time::format_description::well_known::Rfc3339);
433     /// let collector = tracing_subscriber::fmt()
434     ///     .with_timer(timer);
435     /// # drop(collector);
436     /// ```
437     ///
438     /// [`time` crate]: time
439     /// [timezone offset]: time::UtcOffset
440     /// [`Formattable`]: time::formatting::Formattable
441     /// [local offset]: time::UtcOffset::current_local_offset()
442     /// [well-known formats]: time::format_description::well_known
443     /// [`format_description!`]: time::macros::format_description
444     /// [`time::format_description::parse`]: time::format_description::parse
445     /// [`time` book]: https://time-rs.github.io/book/api/format-description.html
new(offset: time::UtcOffset, format: F) -> Self446     pub fn new(offset: time::UtcOffset, format: F) -> Self {
447         Self { offset, format }
448     }
449 }
450 
451 impl<F> FormatTime for OffsetTime<F>
452 where
453     F: time::formatting::Formattable,
454 {
format_time(&self, w: &mut Writer<'_>) -> fmt::Result455     fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
456         let now = OffsetDateTime::now_utc().to_offset(self.offset);
457         format_datetime(now, w, &self.format)
458     }
459 }
460 
format_datetime( now: OffsetDateTime, into: &mut Writer<'_>, fmt: &impl Formattable, ) -> fmt::Result461 fn format_datetime(
462     now: OffsetDateTime,
463     into: &mut Writer<'_>,
464     fmt: &impl Formattable,
465 ) -> fmt::Result {
466     let mut into = WriteAdaptor::new(into);
467     now.format_into(&mut into, fmt)
468         .map_err(|_| fmt::Error)
469         .map(|_| ())
470 }
471