Skip to content

Commit 6614b2f

Browse files
authored
subscriber: replace chrono with time for timestamp formatting (#1646)
## Motivation Currently, `tracing-subscriber` supports the `chrono` crate for timestamp formatting, via a default-on feature flag. When this code was initially added to `tracing-subscriber`, the `time` crate did not have support for the timestamp formatting options we needed. Unfortunately, the `chrono` crate's maintainance status is now in question (see #1598). Furthermore, `chrono` depends on version 0.1 of the `time` crate, which contains a security vulnerability (https://rustsec.org/advisories/RUSTSEC-2020-0071.html). This vulnerability is fixed in more recent releases of `time`, but `chrono` still uses v0.1. ## Solution Fortunately, the `time` crate now has its own timestamp formatting support. This branch replaces the `ChronoLocal` and `ChronoUtc` timestamp formatters with new `LocalTime` and `UtcTime` formatters. These formatters use the `time` crate's formatting APIs rather than `chrono`'s. This removes the vulnerable dependency on `time` 0.1 Additionally, the new `time` APIs are feature flagged as an _opt-in_ feature, rather than as an _opt-out_ feature. This should make it easier to avoid accidentally depending on the `time` crate when more sophisticated timestamp formatting is _not_ required. In a follow-up branch, we could also add support for `humantime` as an option for timestamp formatting. Naturally, since this removes existing APIs, this is a breaking change, and will thus require publishing `tracing-subscriber` 0.3. We'll want to do some other breaking changes as well. Fixes #1598.
1 parent bbe3c1d commit 6614b2f

File tree

11 files changed

+368
-191
lines changed

11 files changed

+368
-191
lines changed

examples/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ tracing-core = { path = "../tracing-core", version = "0.2"}
1515
tracing-error = { path = "../tracing-error" }
1616
tracing-flame = { path = "../tracing-flame" }
1717
tracing-tower = { version = "0.1.0", path = "../tracing-tower" }
18-
tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = ["json", "chrono"] }
18+
tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = ["json"] }
1919
tracing-futures = { version = "0.3", path = "../tracing-futures", features = ["futures-01"] }
2020
tracing-attributes = { path = "../tracing-attributes", version = "0.2"}
2121
tracing-log = { path = "../tracing-log", version = "0.2", features = ["env_logger"] }

tracing-subscriber/Cargo.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@ keywords = ["logging", "tracing", "metrics", "subscriber"]
2323

2424
[features]
2525

26-
default = ["env-filter", "smallvec", "fmt", "ansi", "chrono", "tracing-log", "json"]
26+
default = ["env-filter", "smallvec", "fmt", "ansi", "tracing-log", "json"]
2727
env-filter = ["matchers", "regex", "lazy_static", "tracing"]
2828
fmt = ["registry"]
2929
ansi = ["fmt", "ansi_term"]
3030
registry = ["sharded-slab", "thread_local"]
3131
json = ["tracing-serde", "serde", "serde_json"]
32+
# Enables support for local time when using the `time` crate timestamp
33+
# formatters.
34+
local-time = ["time/local-offset"]
3235

3336
[dependencies]
3437
tracing-core = { path = "../tracing-core", version = "0.2" }
@@ -43,7 +46,7 @@ lazy_static = { optional = true, version = "1" }
4346
# fmt
4447
tracing-log = { path = "../tracing-log", version = "0.2", optional = true, default-features = false, features = ["log-tracer", "std"] }
4548
ansi_term = { version = "0.12", optional = true }
46-
chrono = { version = "0.4.16", optional = true, default-features = false, features = ["clock", "std"] }
49+
time = { version = "0.3", features = ["formatting"], optional = true }
4750

4851
# only required by the json feature
4952
serde_json = { version = "1.0", optional = true }
@@ -65,6 +68,8 @@ criterion = { version = "0.3", default_features = false }
6568
regex = { version = "1", default-features = false, features = ["std"] }
6669
tracing-futures = { path = "../tracing-futures", version = "0.3", default-features = false, features = ["std-future", "std"] }
6770
tokio = { version = "0.2", features = ["rt-core", "macros"] }
71+
# Enable the `time` crate's `macros` feature, for examples.
72+
time = { version = "0.3", features = ["formatting", "macros"] }
6873

6974
[badges]
7075
maintenance = { status = "experimental" }

tracing-subscriber/benches/reload.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use tracing_subscriber::{
33
filter::LevelFilter,
44
fmt::{
55
format::{DefaultFields, Format, Full},
6-
time::ChronoLocal,
76
Collector, CollectorBuilder,
87
},
98
EnvFilter,
@@ -15,8 +14,7 @@ use support::NoWriter;
1514
/// Prepare a real-world-inspired collector and use it to measure the performance impact of
1615
/// reloadable collectors.
1716
18-
fn mk_builder(
19-
) -> CollectorBuilder<DefaultFields, Format<Full, ChronoLocal>, EnvFilter, fn() -> NoWriter> {
17+
fn mk_builder() -> CollectorBuilder<DefaultFields, Format<Full>, EnvFilter, fn() -> NoWriter> {
2018
let filter = EnvFilter::default()
2119
.add_directive("this=off".parse().unwrap())
2220
.add_directive("also=off".parse().unwrap())
@@ -28,9 +26,6 @@ fn mk_builder(
2826
Collector::builder()
2927
.with_env_filter(filter)
3028
.with_writer(NoWriter::new as _)
31-
.with_timer(ChronoLocal::with_format(
32-
"%Y-%m-%d %H:%M:%S%.3f".to_string(),
33-
))
3429
.with_ansi(true)
3530
.with_target(true)
3631
.with_level(true)

tracing-subscriber/src/fmt/fmt_subscriber.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,15 +191,18 @@ where
191191
{
192192
/// Use the given [`timer`] for span and event timestamps.
193193
///
194-
/// See [`time`] for the provided timer implementations.
194+
/// See the [`time` module] for the provided timer implementations.
195195
///
196-
/// Note that using the `chrono` feature flag enables the
197-
/// additional time formatters [`ChronoUtc`] and [`ChronoLocal`].
196+
/// Note that using the `"time`"" feature flag enables the
197+
/// additional time formatters [`UtcTime`] and [`LocalTime`], which use the
198+
/// [`time` crate] to provide more sophisticated timestamp formatting
199+
/// options.
198200
///
199-
/// [`time`]: mod@super::time
200201
/// [`timer`]: super::time::FormatTime
201-
/// [`ChronoUtc`]: super::time::ChronoUtc
202-
/// [`ChronoLocal`]: super::time::ChronoLocal
202+
/// [`time` module]: mod@super::time
203+
/// [`UtcTime`]: super::time::UtcTime
204+
/// [`LocalTime`]: super::time::LocalTime
205+
/// [`time` crate]: https://docs.rs/time/0.3
203206
pub fn with_timer<T2>(self, timer: T2) -> Subscriber<C, N, format::Format<L, T2>, W> {
204207
Subscriber {
205208
fmt_event: self.fmt_event.with_timer(timer),

tracing-subscriber/src/fmt/format/json.rs

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
use super::{Format, FormatEvent, FormatFields, FormatTime};
22
use crate::{
33
field::{RecordFields, VisitOutput},
4-
fmt::fmt_subscriber::{FmtContext, FormattedFields},
4+
fmt::{
5+
fmt_subscriber::{FmtContext, FormattedFields},
6+
writer::WriteAdaptor,
7+
},
58
registry::LookupSpan,
69
};
710
use serde::ser::{SerializeMap, Serializer as _};
811
use serde_json::Serializer;
912
use std::{
1013
collections::BTreeMap,
1114
fmt::{self, Write},
12-
io,
1315
};
1416
use tracing_core::{
1517
field::{self, Field},
@@ -471,44 +473,6 @@ impl<'a> field::Visit for JsonVisitor<'a> {
471473
};
472474
}
473475
}
474-
475-
/// A bridge between `fmt::Write` and `io::Write`.
476-
///
477-
/// This is needed because tracing-subscriber's FormatEvent expects a fmt::Write
478-
/// while serde_json's Serializer expects an io::Write.
479-
struct WriteAdaptor<'a> {
480-
fmt_write: &'a mut dyn fmt::Write,
481-
}
482-
483-
impl<'a> WriteAdaptor<'a> {
484-
fn new(fmt_write: &'a mut dyn fmt::Write) -> Self {
485-
Self { fmt_write }
486-
}
487-
}
488-
489-
impl<'a> io::Write for WriteAdaptor<'a> {
490-
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
491-
let s =
492-
std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
493-
494-
self.fmt_write
495-
.write_str(s)
496-
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
497-
498-
Ok(s.as_bytes().len())
499-
}
500-
501-
fn flush(&mut self) -> io::Result<()> {
502-
Ok(())
503-
}
504-
}
505-
506-
impl<'a> fmt::Debug for WriteAdaptor<'a> {
507-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508-
f.pad("WriteAdaptor { .. }")
509-
}
510-
}
511-
512476
#[cfg(test)]
513477
mod test {
514478
use super::*;

tracing-subscriber/src/fmt/format/mod.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,13 +362,16 @@ impl<F, T> Format<F, T> {
362362
///
363363
/// See [`time` module] for the provided timer implementations.
364364
///
365-
/// Note that using the `chrono` feature flag enables the
366-
/// additional time formatters [`ChronoUtc`] and [`ChronoLocal`].
365+
/// Note that using the `"time"` feature flag enables the
366+
/// additional time formatters [`UtcTime`] and [`LocalTime`], which use the
367+
/// [`time` crate] to provide more sophisticated timestamp formatting
368+
/// options.
367369
///
368370
/// [`timer`]: super::time::FormatTime
369371
/// [`time` module]: mod@super::time
370-
/// [`ChronoUtc`]: super::time::ChronoUtc
371-
/// [`ChronoLocal`]: super::time::ChronoLocal
372+
/// [`UtcTime`]: super::time::UtcTime
373+
/// [`LocalTime`]: super::time::LocalTime
374+
/// [`time` crate]: https://docs.rs/time/0.3
372375
pub fn with_timer<T2>(self, timer: T2) -> Format<F, T2> {
373376
Format {
374377
format: self.format,

tracing-subscriber/src/fmt/mod.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -611,15 +611,18 @@ where
611611
{
612612
/// Use the given [`timer`] for log message timestamps.
613613
///
614-
/// See [`time`] for the provided timer implementations.
614+
/// See the [`time` module] for the provided timer implementations.
615615
///
616-
/// Note that using the `chrono` feature flag enables the
617-
/// additional time formatters [`ChronoUtc`] and [`ChronoLocal`].
616+
/// Note that using the `"time`"" feature flag enables the
617+
/// additional time formatters [`UtcTime`] and [`LocalTime`], which use the
618+
/// [`time` crate] to provide more sophisticated timestamp formatting
619+
/// options.
618620
///
619-
/// [`time`]: mod@time
620621
/// [`timer`]: time::FormatTime
621-
/// [`ChronoUtc`]: time::ChronoUtc
622-
/// [`ChronoLocal`]: time::ChronoLocal
622+
/// [`time` module]: mod@time
623+
/// [`UtcTime`]: time::UtcTime
624+
/// [`LocalTime`]: time::LocalTime
625+
/// [`time` crate]: https://docs.rs/time/0.3
623626
pub fn with_timer<T2>(self, timer: T2) -> CollectorBuilder<N, format::Format<L, T2>, F, W> {
624627
CollectorBuilder {
625628
filter: self.filter,

tracing-subscriber/src/fmt/time/mod.rs

Lines changed: 7 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22
use std::fmt;
33
use std::time::Instant;
44

5-
#[cfg(not(feature = "chrono"))]
65
mod datetime;
76

7+
#[cfg(feature = "time")]
8+
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
9+
mod time_crate;
10+
#[cfg(feature = "time")]
11+
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
12+
pub use time_crate::{LocalTime, UtcTime};
13+
814
/// A type that can measure and format the current time.
915
///
1016
/// This trait is used by `Format` to include a timestamp with each `Event` when it is logged.
@@ -81,9 +87,6 @@ impl FormatTime for fn(&mut dyn fmt::Write) -> fmt::Result {
8187
}
8288

8389
/// Retrieve and print the current wall-clock time.
84-
///
85-
/// If the `chrono` feature is enabled, the current time is printed in a human-readable format like
86-
/// "Jun 25 14:27:12.955". Otherwise the `Debug` implementation of `std::time::SystemTime` is used.
8790
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
8891
pub struct SystemTime;
8992

@@ -109,14 +112,6 @@ impl From<Instant> for Uptime {
109112
}
110113
}
111114

112-
#[cfg(feature = "chrono")]
113-
impl FormatTime for SystemTime {
114-
fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result {
115-
write!(w, "{}", chrono::Local::now().format("%b %d %H:%M:%S%.3f"))
116-
}
117-
}
118-
119-
#[cfg(not(feature = "chrono"))]
120115
impl FormatTime for SystemTime {
121116
fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result {
122117
write!(
@@ -133,112 +128,3 @@ impl FormatTime for Uptime {
133128
write!(w, "{:4}.{:09}s", e.as_secs(), e.subsec_nanos())
134129
}
135130
}
136-
137-
/// The RFC 3339 format is used by default and using
138-
/// this struct allows chrono to bypass the parsing
139-
/// used when a custom format string is provided
140-
#[cfg(feature = "chrono")]
141-
#[derive(Debug, Clone, Eq, PartialEq)]
142-
enum ChronoFmtType {
143-
Rfc3339,
144-
Custom(String),
145-
}
146-
147-
#[cfg(feature = "chrono")]
148-
impl Default for ChronoFmtType {
149-
fn default() -> Self {
150-
ChronoFmtType::Rfc3339
151-
}
152-
}
153-
154-
/// Retrieve and print the current UTC time.
155-
#[cfg(feature = "chrono")]
156-
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
157-
#[derive(Debug, Clone, Eq, PartialEq, Default)]
158-
pub struct ChronoUtc {
159-
format: ChronoFmtType,
160-
}
161-
162-
#[cfg(feature = "chrono")]
163-
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
164-
impl ChronoUtc {
165-
/// Format the time using the [`RFC 3339`] format
166-
/// (a subset of [`ISO 8601`]).
167-
///
168-
/// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339
169-
/// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601
170-
pub fn rfc3339() -> Self {
171-
ChronoUtc {
172-
format: ChronoFmtType::Rfc3339,
173-
}
174-
}
175-
176-
/// Format the time using the given format string.
177-
///
178-
/// See [`chrono::format::strftime`]
179-
/// for details on the supported syntax.
180-
///
181-
pub fn with_format(format_string: String) -> Self {
182-
ChronoUtc {
183-
format: ChronoFmtType::Custom(format_string),
184-
}
185-
}
186-
}
187-
188-
#[cfg(feature = "chrono")]
189-
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
190-
impl FormatTime for ChronoUtc {
191-
fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result {
192-
let time = chrono::Utc::now();
193-
match self.format {
194-
ChronoFmtType::Rfc3339 => write!(w, "{}", time.to_rfc3339()),
195-
ChronoFmtType::Custom(ref format_str) => write!(w, "{}", time.format(format_str)),
196-
}
197-
}
198-
}
199-
200-
/// Retrieve and print the current local time.
201-
#[cfg(feature = "chrono")]
202-
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
203-
#[derive(Debug, Clone, Eq, PartialEq, Default)]
204-
pub struct ChronoLocal {
205-
format: ChronoFmtType,
206-
}
207-
208-
#[cfg(feature = "chrono")]
209-
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
210-
impl ChronoLocal {
211-
/// Format the time using the [`RFC 3339`] format
212-
/// (a subset of [`ISO 8601`]).
213-
///
214-
/// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339
215-
/// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601
216-
pub fn rfc3339() -> Self {
217-
ChronoLocal {
218-
format: ChronoFmtType::Rfc3339,
219-
}
220-
}
221-
222-
/// Format the time using the given format string.
223-
///
224-
/// See [`chrono::format::strftime`]
225-
/// for details on the supported syntax.
226-
///
227-
pub fn with_format(format_string: String) -> Self {
228-
ChronoLocal {
229-
format: ChronoFmtType::Custom(format_string),
230-
}
231-
}
232-
}
233-
234-
#[cfg(feature = "chrono")]
235-
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
236-
impl FormatTime for ChronoLocal {
237-
fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result {
238-
let time = chrono::Local::now();
239-
match self.format {
240-
ChronoFmtType::Rfc3339 => write!(w, "{}", time.to_rfc3339()),
241-
ChronoFmtType::Custom(ref format_str) => write!(w, "{}", time.format(format_str)),
242-
}
243-
}
244-
}

0 commit comments

Comments
 (0)