]>
Commit | Line | Data |
---|---|---|
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 subscriber = tracing_subscriber::fmt() | |
80 | /// .with_timer(time::LocalTime::rfc_3339()); | |
81 | /// # drop(subscriber); | |
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 | |
87 | 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 subscriber = tracing_subscriber::fmt() | |
134 | /// .with_timer(timer); | |
135 | /// # drop(subscriber); | |
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 subscriber = tracing_subscriber::fmt() | |
147 | /// .with_timer(timer); | |
148 | /// # drop(subscriber); | |
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 subscriber = tracing_subscriber::fmt() | |
162 | /// .with_timer(timer); | |
163 | /// # drop(subscriber); | |
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 | |
173 | 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 | { | |
183 | 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 | { | |
194 | 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 subscriber = tracing_subscriber::fmt() | |
211 | /// .with_timer(time::UtcTime::rfc_3339()); | |
212 | /// # drop(subscriber); | |
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 | |
218 | 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 subscriber = tracing_subscriber::fmt() | |
251 | /// .with_timer(timer); | |
252 | /// # drop(subscriber); | |
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 subscriber = tracing_subscriber::fmt() | |
267 | /// .with_timer(timer); | |
268 | /// # drop(subscriber); | |
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 subscriber = tracing_subscriber::fmt() | |
279 | /// .with_timer(timer); | |
280 | /// # drop(subscriber); | |
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 | |
290 | 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 | { | |
299 | 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 | { | |
308 | 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 subscriber = tracing_subscriber::fmt() | |
330 | /// .with_timer(time::OffsetTime::local_rfc_3339().expect("could not get local offset!")); | |
331 | /// # drop(subscriber); | |
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 | |
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 subscriber = tracing_subscriber::fmt() | |
402 | /// .with_timer(timer); | |
403 | /// # drop(subscriber); | |
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 subscriber = tracing_subscriber::fmt() | |
417 | /// .with_timer(timer); | |
418 | /// # drop(subscriber); | |
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 subscriber = tracing_subscriber::fmt() | |
434 | /// .with_timer(timer); | |
435 | /// # drop(subscriber); | |
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 | |
446 | 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 | { | |
455 | 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 | ||
461 | 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 | } |