-
Notifications
You must be signed in to change notification settings - Fork 798
subscriber: Use DateTime
as format for std::time::SystemTime
#807
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This PR copies DateTime type from https://github.com/danburkert/kudu-rs and updates the default FormatTime impl when chrono is disabled to use DateTime Formatting. Signed-off-by: Tarun Pothulapati <[email protected]>
tracing-subscriber/src/fmt/time.rs
Outdated
/// [1] http://git.musl-libc.org/cgit/musl/tree/src/time/__secs_to_tm.c | ||
/// [2] https://c2rust.com/ | ||
/// | ||
/// This is directly copy-pasted from https://github.com/danburkert/kudu-rs/blob/c9660067e5f4c1a54143f169b5eeb49446f82e54/src/timestamp.rs#L5-L18 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I directly copy pasted this code from along with tests, Is this the right way to give credits? or is there a better way?
@hawkw
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This personally works for me, although I also think it'd be fair to just credit musl, since the code is really just a machine translation of __secs_to_tm
with very light cleanup (essentially doing whatever clippy said).
Signed-off-by: Tarun Pothulapati <[email protected]>
tracing-subscriber/src/fmt/time.rs
Outdated
#[test] | ||
fn test_datetime() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this test is failing on Windows: https://github.com/tokio-rs/tracing/pull/807/checks?check_run_id=861424835#step:4:891
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried testing on a windows machine, and found that it was these test cases that were causing to panic.
https://github.com/Pothulapati/tracing/blob/1c7c70b91ba7504aa486f69209d609544eb17a4e/tracing-subscriber/src/fmt/time.rs#L463 (and this is not the conversion logic to DateTime but it was in the case
helper closure) as they were using i64::Max
ori64::Min
with UNIX_EPOCH + Duration::new(secs as u64, micros * 1_000)
and impl Add<Duration> for SystemTime
panics if the resulting point in time cannot be represented by the underlying data structure as per https://doc.rust-lang.org/std/time/struct.SystemTime.html#panics
It is mentioned in the doc that The size of a SystemTime struct may vary depending on the target operating system.
and as per rust-lang/rust#44394 (comment), the range for windows is way less than that of UNIX. 🤔
Also, As the duration in the test case is pretty high i.e giving the time +292277026596-12-04T15:30:07.000000Z
. Should it matter?
Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. I think timestamps in the year 292277026596 are pretty unlikely to occur any time soon. However, there are situations where weird timestamps can occur other than actually being in that year --- the system clock might be misconfigured or something.
I think it would be better to handle this case a little more gracefully than wrapping. We could probably add a check before doing that add, and if it would wrap, we could just print "Invalid timestamp" or something, rather than panicking.
For the short term, I think it would be okay to disable the specific tests which use the really high year value on Windows. We can do that by adding a cfg
attribute:
#[cfg(not(target_os = "windows"))]
{
case("+292277026596-12-04T15:30:07.000000Z", i64::MAX, 0);
case("+292277026596-12-04T15:30:06.000000Z", i64::MAX - 1, 0);
case("-292277022657-01-27T08:29:53.000000Z", i64::MIN + 1, 0);
}
If we do that, it would be good to have a comment explaining why those tests are disabled on Windows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably also worth ccing @danburkert — he might want to be aware that the upstream version of this test doesn't work with Windows timestamps, too. :)
This commit uses conditional compilation primitives to make high SystemTime tests be skipped on windows along with some explanations Signed-off-by: Tarun Pothulapati <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great now that the tests that fail on Windows are skipped. I had some pretty minor suggestions, and then I think this will be good to merge.
tracing-subscriber/src/fmt/time.rs
Outdated
/// | ||
/// This is directly copy-pasted from https://github.com/danburkert/kudu-rs/blob/c9660067e5f4c1a54143f169b5eeb49446f82e54/src/timestamp.rs#L5-L18 | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub struct DateTime { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't part of the public API --- it's only used internally by the SystemTime
formatter. Therefore, it doesn't need to be pub
:
pub struct DateTime { | |
struct DateTime { |
tracing-subscriber/src/fmt/time.rs
Outdated
while i32::from(DAYS_IN_MONTH[months as usize]) <= remdays { | ||
remdays -= i32::from(DAYS_IN_MONTH[months as usize]); | ||
months += 1 | ||
} | ||
|
||
if months >= 10 { | ||
months -= 12; | ||
years += 1; | ||
} | ||
|
||
DateTime { | ||
year: years + 2000, | ||
month: (months + 3) as u8, | ||
day: (remdays + 1) as u8, | ||
hour: (remsecs / 3600) as u8, | ||
minute: (remsecs / 60 % 60) as u8, | ||
second: (remsecs % 60) as u8, | ||
nanos, | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use std::i32; | ||
use std::time::{Duration, UNIX_EPOCH}; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn test_datetime() { | ||
let case = |expected: &str, secs: i64, micros: u32| { | ||
let timestamp = if secs >= 0 { | ||
UNIX_EPOCH + Duration::new(secs as u64, micros * 1_000) | ||
} else { | ||
(UNIX_EPOCH - Duration::new(!secs as u64 + 1, 0)) + Duration::new(0, micros * 1_000) | ||
}; | ||
assert_eq!( | ||
expected, | ||
format!("{}", DateTime::from(timestamp)), | ||
"secs: {}, micros: {}", | ||
secs, | ||
micros | ||
) | ||
}; | ||
|
||
// Mostly generated with: | ||
// - date -jur <secs> +"%Y-%m-%dT%H:%M:%S.000000Z" | ||
// - http://unixtimestamp.50x.eu/ | ||
|
||
case("1970-01-01T00:00:00.000000Z", 0, 0); | ||
|
||
case("1970-01-01T00:00:00.000001Z", 0, 1); | ||
case("1970-01-01T00:00:00.500000Z", 0, 500_000); | ||
case("1970-01-01T00:00:01.000001Z", 1, 1); | ||
case("1970-01-01T00:01:01.000001Z", 60 + 1, 1); | ||
case("1970-01-01T01:01:01.000001Z", 60 * 60 + 60 + 1, 1); | ||
case( | ||
"1970-01-02T01:01:01.000001Z", | ||
24 * 60 * 60 + 60 * 60 + 60 + 1, | ||
1, | ||
); | ||
|
||
case("1969-12-31T23:59:59.000000Z", -1, 0); | ||
case("1969-12-31T23:59:59.000001Z", -1, 1); | ||
case("1969-12-31T23:59:59.500000Z", -1, 500_000); | ||
case("1969-12-31T23:58:59.000001Z", -60 - 1, 1); | ||
case("1969-12-31T22:58:59.000001Z", -60 * 60 - 60 - 1, 1); | ||
case( | ||
"1969-12-30T22:58:59.000001Z", | ||
-24 * 60 * 60 - 60 * 60 - 60 - 1, | ||
1, | ||
); | ||
|
||
case("2038-01-19T03:14:07.000000Z", i32::MAX as i64, 0); | ||
case("2038-01-19T03:14:08.000000Z", i32::MAX as i64 + 1, 0); | ||
case("1901-12-13T20:45:52.000000Z", i32::MIN as i64, 0); | ||
case("1901-12-13T20:45:51.000000Z", i32::MIN as i64 - 1, 0); | ||
|
||
// Skipping these tests on windows as std::time::SysteTime range is low | ||
// on Windows compared with that of Unix which can cause the following | ||
// high date value tests to panic | ||
#[cfg(not(target_os = "windows"))] | ||
{ | ||
case("+292277026596-12-04T15:30:07.000000Z", i64::MAX, 0); | ||
case("+292277026596-12-04T15:30:06.000000Z", i64::MAX - 1, 0); | ||
case("-292277022657-01-27T08:29:53.000000Z", i64::MIN + 1, 0); | ||
} | ||
|
||
case("1900-01-01T00:00:00.000000Z", -2208988800, 0); | ||
case("1899-12-31T23:59:59.000000Z", -2208988801, 0); | ||
case("0000-01-01T00:00:00.000000Z", -62167219200, 0); | ||
case("-0001-12-31T23:59:59.000000Z", -62167219201, 0); | ||
|
||
case("1234-05-06T07:08:09.000000Z", -23215049511, 0); | ||
case("-1234-05-06T07:08:09.000000Z", -101097651111, 0); | ||
case("2345-06-07T08:09:01.000000Z", 11847456541, 0); | ||
case("-2345-06-07T08:09:01.000000Z", -136154620259, 0); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the DateTime
code is only used for formatting SystemTime
s when the chrono
feature is not enabled. We might want to guard it with #[cfg(not(feature = "chrono"))]
conditional compilation attribute, so that it's not compiled in when chrono
is used instead.
We'd need to add that attribute to the DateTime
struct, and to every impl
block for DateTime
(as well as the tests). The easiest way to do that is probably to stick the whole thing in a module and put the attribute on the whole module:
#[cfg(not(feature = "chrono"))]
mod datetime {
// ... code goes here ...
}
If you like, you could also put that module in a separate file. Up to you.
This change creates datetime into a new mod, which is only included and used when crono is disabled. Signed-off-by: Tarun Pothulapati <[email protected]>
tracing-subscriber/src/fmt/mod.rs
Outdated
#[cfg(not(feature = "chrono"))] | ||
mod datetime; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit/take it or leave it: since this is used only in the time
module, it could probably be a submodule of time
rather than of fmt
. but, not a big deal.
Signed-off-by: Tarun Pothulapati <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! Thank you!
Changed - **fmt**: When the `chrono` dependency is enabled, the `SystemTime` timestamp now emits human-readable timestamps rather than using `SystemTime`'s fmt::Debug`implementation (`chrono` is still required for customized timestamp formatting) (#807) - **ansi**: Updated `ansi_term` dependency to 0.12 (#816) Added - **json**: `with_span_list` method to configure the JSON formatter to include a list of all spans in the current trace in formatting events (similarly to the text formatter) (#741) - **json**: `with_current_span` method to configure the JSON formatter to include a field for the _current_ span (the leaf of the trace) in formatted events (#741) - **fmt**: `with_thread_names` and `with_thread_ids` methods to configure `fmt::Subscriber`s and `fmt::Layer`s to include the thread name and/or thread ID of the current thread when formatting events (#818) Thanks to new contributors @mockersf, @keetonian, and @Pothulapati for contributing to this release!
### Changed - **fmt**: When the `chrono` dependency is enabled, the `SystemTime` timestamp now emits human-readable timestamps rather than using `SystemTime`'s fmt::Debug`implementation (`chrono` is still required for customized timestamp formatting) (#807) - **ansi**: Updated `ansi_term` dependency to 0.12 (#816) ### Added - **json**: `with_span_list` method to configure the JSON formatter to include a list of all spans in the current trace in formatting events (similarly to the text formatter) (#741) - **json**: `with_current_span` method to configure the JSON formatter to include a field for the _current_ span (the leaf of the trace) in formatted events (#741) - **fmt**: `with_thread_names` and `with_thread_ids` methods to configure `fmt::Subscriber`s and `fmt::Layer`s to include the thread name and/or thread ID of the current thread when formatting events (#818) Thanks to new contributors @mockersf, @keetonian, and @Pothulapati for contributing to this release!
Motivation
Fixes #791
Solution
This PR copies
DateTime
type from https://github.com/danburkert/kudu-rsand updates the default
FormatTime
impl forSystemTime
when chrono is disabled to useDateTime
Formatting instead ofDebug
After this, the following output is emitted instead of the one shown in the issue.
Signed-off-by: Tarun Pothulapati [email protected]