Ensure 0.3 works with the next major release #148
Labels
No labels
bug
duplicate
easy
enhancement
help wanted
invalid
question
rfc
structured-logging
wontfix
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gh-rust-lang/log#148
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
We'll want to publish a version of 0.3 that works as a compatibility layer to the 0.4 version of the
logcrateIf it's alright and no one else has started, I can start working on a shared backend crate for this.
I'm trying to think of the best way to have it be efficient, while still allowing for maximum change. Right now my best idea is to have a versioned API that would be somewhat like the following:
V1Recordwould be a mirror of what thelogcrate's currentLogRecordstruct is, and thelogcrate would simply have a newtype wrapper around it for the exportedLogRecordtype.V1Recordwould be constructed with aV1RecordBuilder:#[inline(always)]builder methods should allow for the same performance we have now without a builder, and this would allow us to add more fields without bumping toV2Recordimmediately.V1Logwould have a blanket impl like this inlog:For the next version of
logwhich brings an incompatibleLogRecordformat,log_implwould introduceV2LogandV2Recordto match, and wouldimpl From<V1Record> for V2Recordand vice versa. I would nameV1rather thanV0_3just because I wouldn't expect every singlelogcrate release to need a new, incompatible record version.Internally,
log_implwould store an enumVersionedLogwhich could be a box of any of theV*Logtraits, and would convert records accordingly when logged.The
log_implwould provide one other feature, a set of macrosif_trace_enabled!()throughif_error_enabled!(), which would be controlled throughmax_level_*andrelease_max_level_*crate features, similar tolog's current ones. These macros would then be used inloginstead of the features, and thelogfeatures would simply forward to depending on thelog_implones.There would be overhead of converting records if the
logmacro version and logger version don't match, but I believe a scheme like this would fully support both public breaking API changes inlog, and breaking changes in howLogRecordis internally structured.If we don't implement any breaking changes in
LogRecords format, we could even just havelog_implwithV1*always, and there would be no overhead.Unresolved question with this idea: v0.3 takes in
Box<Log>, which I don't believe can be converted toBox<V1Log>, even if a blanket implementation exists. Might make this unreasonable.I'm not clear on what needs to be done here. @alexcrichton @sfackler have comments on @daboross's comments?
I'll implement what I described if it is deemed an alright thing.
The one difference may need to be that log just re-exports log_inner::V1Log as Log in order to not need double boxing but I'm not sure how this can be shown well in documentation... (without having users see the log_inner implementation detail of log).
I think it'll need to be a bit weirder than that because we're getting rid of shutdown in 0.4. One possible route is that 0.3 depends on 0.4 and forwards log events over to it. We'd then have a wrapping shim in 0.3 that would handle shutdown if you registered a logger through it.
We'll want 0.4 to be able to log events in a 0.3 logger too though, right?
With an inner/backend crate though, what if it just took
(fn(&Metadata) -> bool, fn(&Record))instead ofBox<Log>? That way we could have bothlog 0.3andlog 0.4have separate ways of storing theLog, and then we'd be able to retain support for shutdown in log 0.3 while removing it in 0.4. Both versions would just register their specific function to access their global log storage when they register a logger.I think passing just an
fnpair to the backend might also solve the issue of having to double-box things in order to have a nice API. This would involve another indirect dynamic call, but I'm thinking that's a cost we could live with?Yeah - this would handle that - an 0.3 logger is turned into an 0.4 logger with some shim to handle shutdown.
I'd also like to avoid allocating on the log path.
Definitely agree here with avoiding allocating.
I still think having a shared backend would be a more long-term solution, as then we wouldn't have to update 0.3 and 0.4 both to depend on 1.0 when we release it.
Is the benefit of having 0.3 depend on 0.4 just less code complexity, or is there another reason as well? I mean having less duplication in the project structure is valid, I just didn't think of it as too much of a concern.
It seems pretty reasonable to me to trade way less code complexity for making two commits when we cut 1.0.
Alright! I can get behind that.
This commit should do what we need:
github.com/sfackler/log@55549a34b1.Is there anything more we need to do here @sfackler? Do we just need to do a bump of
0.3when it comes time to release0.4?Nope, should be good to go.
I just noticed one caveat: log 0.4 requires Rust 1.16.0+. Otherwise you run into this:
Since 0.3 will depend on 0.4, this adds the same requirement there as well. Are we okay with that?
This seems fine to me, the current stable is 1.21 and that's 5 versions back
That's still a compatibility break in a minor version for a very widely used crate though, right? I'm sure some of the 1120 reverse dependencies have a guarantee on their rust version support - even if it is a required upgrade to a 7-month-old compiler.
@daboross what's the alternative?
@sfackler you're right there. I was trying to think of a way to fix this, but there isn't really a reasonable one. Restricting log 0.4 to earlier versions of rust wouldn't be great at all, and it is an old version already that 0.3 is requiring an upgrade to.
So
randjust got hit by thelog 0.3depends onlog 0.4thing @sfackler mentioned above (CI pins rustc 1.15). I guess we can solve that (either by pinning log <= 0.3.8 or updating to rustc 1.16), but in general it's not cool having 0.3.9 depend on 0.4.0 (especially since I had to search around to find out why; put a note in the 0.3.x readme maybe?).Ok, I understand that you want to avoid Cargo linking two copies of
loginto peoples dependency graphs, but what you've done automatically updates people with an explicit dependency on log^0.3to use0.4. Two evils it seems.That is not what I want to avoid! What I want to avoid is totally bifurcating the log messages of the entire crate ecosystem for an arbitrary amount of time, with the 0.4 messages disappearing into the ether if a crate is using a logger implementation which uses 0.3 and vice versa.
One one side we have the cost of bumping a number in a yaml file in rand's repository, and in the other we have massive ecosystem trauma. It seems like a pretty straightforward choice to me?
Looks like it would be possible to add support for 1.15 by tossing in a transmute.
What you're saying @sfackler is that log 0.3 is essentially dead, any crate using log must be using 0.4, and as a side-effect Rustc < 1.16 is also dead (for anything using log, which is a lot of things). I don't really care about Rustc 1.15 but it seems like there should be some kind of warning and advisory handling (e.g. I just pinned log <= 0.3.8 which it sounds like is exactly what you don't want people to do; luckily it's only a dev-dependency so doesn't affect anything else).
I wouldn't normally advise caring about old Rust versions without known requirements, but in this case I think using the workaround posted would make sense, just because so many people use
log(if it's not already too late).@dtolnay I'd be happy to do that, but I'd want to first figure out what changed in 1.16 to make it compile. If it was just the borrow checker becoming a bit smarter then we're fine, but if it was due to a change in e.g. format_args's internal guts then we could run into memory safety issues.
"Let's just transmute, what's the worst that could happen" is not an approach I'm on board with.
@dhardy in what library is it not the case that the 0.x series is dead once 0.(x-1) comes out and crates depending on it should move forward? If you don't care about 1.15, then why did you pin the log version instead of bumping to 1.16?
It was the borrow checker becoming smarter. This is what was newly allowed in 1.16.0:
The
format_args!("")contains a slice with some data pointer and zero length. All we do with it in std::fmt is iterate over the arguments, so casting the zero-length slice to a longer lifetime seems fine.I've requested this in a number of other nursery crates but can we just add a minimum version in the Travis file and test against that. When it needs to be bumped then it needs to be bumped but it will at least let people know "uh oh something was hit" and others can splunk through the history to find what works with what.
@cardoe we already do that: https://github.com/rust-lang-nursery/log/blob/master/.travis.yml#L4
Yeah I was responding to an issue on a crate I support for Rust 1.15.1 that still had log 0.3 in it.