Skip to content
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

Add OffsetTime formatter as an alternative to LocalTime without unsoundness #1771

Closed
marienz opened this issue Dec 7, 2021 · 2 comments
Closed

Comments

@marienz
Copy link

marienz commented Dec 7, 2021

Feature Request

Crates

tracing-subscriber v0.3.3

Motivation

Provide a workaround for LocalTime typically requiring --cfg unsound_local_offset (as described in #1688) for programs that can accept their tracing output not following timezone changes while the program runs.

Proposal

Add an OffsetTime formatter that is initialized with a format and a timezone offset. This formats time the same way LocalTime does, but with a fixed offset. Make this easy to initialize with the local time's UTC offset. Document that this will typically fail unless called early at startup.

Even if initializing OffsetTime fails, that failure is easier to handle than it is for LocalTime: failure occurs when the formatter is initialized (typically at program startup, in application code), not when it is used (when outputting an event, in tracing code).

I've implemented a working proof of concept. It needs tests, docstring polish, and some API polish, but I've used this to confirm this approach actually works without unsound_local_offset (in a program where LocalTime did not).

I should be able to turn that code into a proper PR if this proposal is accepted.

Alternatives

  • Replace LocalTime with this approach. Keeps the API simpler, but not following DST changes is not ideal.
  • The status quo: require unsound_local_offset or single-threadedness. unsound_local_offset is (more or less deliberately) inconvenient to use, and enables unsound behavior. Although the circumstances under which programs are actually unsound are rare, proving they will not occur for any program is difficult, particularly if they link to any non-Rust code.
  • Wait and see if the time crate comes up with a way of avoiding the unsound behavior (Better solution for getting local offset on unix time-rs/time#380). This is not trivial: the most straightforward approach involves reimplementing parsing zoneinfo files, and as far as I know their format and location are both implementation-defined. They are considering other approaches involving forking, but I don't see how those will work without then exec-ing a helper program that tells us our local timezone offset, which does not seem very practical for a low-dependency library like time.
  • This proposal, but with our own mechanism for automatically updating the local offset. I don't think this is really any easier for tracing to do than it is for time, but for tokio users we might be able to implement something involving a long-running (single-threaded) helper process communicating timezone changes back to the main program. This seems like a lot of complexity (for a feature I personally do not need), but possible.
@hawkw
Copy link
Member

hawkw commented Dec 7, 2021

This proposal seems reasonable to me, and I'd happily review a PR for it if you open one. 👍

I don't think we should remove the LocalTime formatter, since removing it would be a breaking change. I think we should probably add an OffsetTime formatter separately from LocalTime, like you suggested. If the time crate provides a better solution in the future, we can internally change the LocalTime formatter to use that, but in the meantime, I think this approach is probably the best one for now.

@marienz
Copy link
Author

marienz commented Dec 8, 2021

Thanks, I'll send a PR (probably this weekend).

I didn't explain the "Replace" alternative properly. I did not mean to remove LocalTime and add OffsetTime: I meant replace the implementation of LocalTime with OffsetTime, with the offset always the local offset at initialization. That's a non-breaking change as far as the API is concerned, and unlike my proposal it's a change that could be rolled back in the future without a breaking change. But I don't like the behavioral change, and I don't like that the reimplemented LocalTime would not be able to fail to initialize if it cannot get the local offset (because the current LocalTime::new returns Self, not a Result).

I think adding a separate OffsetTime is the best approach for now, but if time does manage to find a better solution OffsetTime is no longer very useful (but would have to stick around as discouraged API until the next breaking change).

hawkw pushed a commit that referenced this issue Dec 28, 2021
## Motivation

Currently, the `time` crate refuses to determine the local timezone
offset on Unix unless the program is single-threaded or the
`unsound_local_offset` feature is enabled. Because `LocalTime`
determines the local timezone offset every time it formats a message,
and `tracing` is frequently used in multi-threaded programs, `LocalTime`
effectively requires `unsound_local_offset`. `unsound_local_offset` is
inconvenient to enable and potentially dangerous.

## Solution

Add an `OffsetTime` formatter that formats time with a fixed offset,
passed in when it is initialized. Make it convenient to initialize this
with the local timezone offset.

The main advantage of this formatter over `LocalTime` is that if it is
initialized early, it will succeed without `unsound_local_offset`. An
additional advantage is that the program can handle errors determining
the local offset, because they now occur in the program's initialization
code rather than in tracing's formatting code.

This is not a drop-in replacement for LocalTime, because it behaves
differently if the program keeps running across a DST change.
`LocalTime` will pick up the new local offset, while `OffsetTime` will
continue to use the offset in effect when the program started.

Fixes #1771
@marienz marienz closed this as completed Dec 30, 2021
kaffarell pushed a commit to kaffarell/tracing that referenced this issue May 22, 2024
## Motivation

Currently, the `time` crate refuses to determine the local timezone
offset on Unix unless the program is single-threaded or the
`unsound_local_offset` feature is enabled. Because `LocalTime`
determines the local timezone offset every time it formats a message,
and `tracing` is frequently used in multi-threaded programs, `LocalTime`
effectively requires `unsound_local_offset`. `unsound_local_offset` is
inconvenient to enable and potentially dangerous.

## Solution

Add an `OffsetTime` formatter that formats time with a fixed offset,
passed in when it is initialized. Make it convenient to initialize this
with the local timezone offset.

The main advantage of this formatter over `LocalTime` is that if it is
initialized early, it will succeed without `unsound_local_offset`. An
additional advantage is that the program can handle errors determining
the local offset, because they now occur in the program's initialization
code rather than in tracing's formatting code.

This is not a drop-in replacement for LocalTime, because it behaves
differently if the program keeps running across a DST change.
`LocalTime` will pick up the new local offset, while `OffsetTime` will
continue to use the offset in effect when the program started.

Fixes tokio-rs#1771
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants