Get structured logging API ready for stabilization #613
No reviewers
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!613
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/kv-cleanup"
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?
Closes #149
Closes #328
Closes #357
Closes #436
Closes #584
Based on the review in #584
The plan is to:
Value::capture_*andValue::downcast_*methods. All deprecated APIs are gated under the oldkv_unstablefeatures and will be removed once we remove those old unstable features.ToValue(key:? = value).value-bagas a dependency if eitherserdeorsvalis also needed. In cases where you just want basic structured logging, we should do it in a dependency-free way. This will just mean adding a simpleValueInnerenum.I'll cc once this is ready for review, to save a bit of notification noise.
Once this PR is complete, we should be able to stabilize structured logging support in the
logcrate.We currently have
kv::source::Visitorandkv::value::Visit. They're both visitor types (separate because they serve separate purposes), but should probably still be named consistently. @Thomasdezeeuw what do you think? Should we rename eithersource::Visitororvalue::Visit? My preference would be to renamevalue::Visittovalue::Visitor.I think
Visitoris more common? So I would go with that.I would understand possible confusing between the two traits, so maybe we need to rename one of the two? What about renaming
source::VisitortoKeyValuePairsor justKeyValues, to indicate its a collection of key-value pairs?That's a fair point. Perhaps we could keep the names verb-y and use
VisitSourceandVisitValue?Sounds good 👍
Ok, I think the last remaining thing here is macro syntax. We could adopt exactly
tracing's syntax of:where
<modifier>may be?forfmt::Debugor%forfmt::Display. I'm not a huge fan of leaning on sigils, or on prefixed modifiers here, because it leaves us almost no room for extensibility, but does have the benefit of being what's already there. I was thinking something like:where
<modifier> may be blank forfmt::Display,?forfmt::Debug, or the literal textserdeorsvalfor those respective libraries. In practice, it would look something like:data: captured usingToValuedata:: captured usingfmt::Displaydata:?: captured usingfmt::Debugdata:serde: captured usingserde::Serializedata:sval: captured usingsval::ValueWe could use a simple lowering scheme so
data:?lowered todata:debuganddata:lowered todata:display, so it would be less easy to miss the presence of the:.Another potential position could be on the key name:
That would more easily let us expected
argto be a full expression in the future.Any thoughts?
I like this, feels similar to
write!(andprint!, etc.) macros.Small thing is versioning, in case serde/sval ever publish a v2, how do we want to handle that? We can go
serdeV1orserde1or something else with a version in it, but if a v2 never comes it will just be annoying. So maybeserde/svalmeans v1, andserde2/sval2could v2?Because we'll be copying the standard formatting found in the std lib, I don't think will become an issue.
How would be limiting ourselves using the above
arg : <modifier>? I suppose we won't be able to support full expressions?That seems like a good scheme to me 👍 I spelled this out explicitly in
value-bag, but it's not really meant to be user-facing. I think it's reasonable to make the "unversioned" ident refer to the current stable versions inlog.We'd just need to wrap full expressions in parenthesis or braces, because the
:character can't appear directly after an expression in a macro rule.This is quite a big change but I think it's ready for a review now. Some things to call out:
key:modifier = value, sokey:? = valuefor debug for instance. This was easier to implement and didn't require breaking any existing users.kv::sourceandkv::valuemodules private now there's no types in them that aren't exported in the rootkvmodule? I think we probably should.I like it.
The only concern I have is that people find the modified on the key odd/unexpected, when coming from the
std::fmtimplementation. That might be worth calling out in the documentation.@ -46,25 +46,31 @@ release_max_level_trace = []std = []More a question about Cargo I suppose, but does enabling
kv_unstable_{sval,std,serde}also addvalue-bagas a dependency due to the enabling of `"value-bag/{sval,error,serde}"?@ -87,3 +82,44 @@ pub fn log<'a, K>(pub fn enabled(level: Level, target: &str) -> bool {crate::logger().enabled(&Metadata::builder().level(level).target(target).build())}Why the double reference?
@ -13,0 +8,4 @@//! features = ["kv"]//! ```//!//! # Structured logging in `log`Or I think the word can be removed entirely.
I like this 👍
Nit: want to move this above the
info!statement so they stay in sync?I think we'll want to show this in the example so that people understand they need to implement/derive
serde::Serializefor log (andserde_json::to_string)Nit: maybe?
I would also show this in the example.
@ -13,0 +87,4 @@//! struct Collect<'kvs>(BTreeMap<Key<'kvs>, Value<'kvs>>);//!//! impl<'kvs> VisitSource<'kvs> for Collect<'kvs> {//! fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> {Nit: maybe add
assert_eq!(a, Value::from(1));to make it super clear?@ -10,1 +12,4 @@/// Use the [`VisitSource`](trait.VisitSource.html) trait to inspect the structured data/// in a source.////// A source is like an iterator over its key-values, except with a push-based APINit: don't think it's needed any more. But if we have it in other places, then let's leave it and do it some other time for all links.
@ -34,15 +74,14 @@ pub trait Source {get_default(self, key)Nit.
@ -46,25 +46,31 @@ release_max_level_trace = []std = []It will, yes 👍 We technically don't need
value-bagfor capturingstd::error::Errors, but do forserdeorsval.@ -10,1 +12,4 @@/// Use the [`VisitSource`](trait.VisitSource.html) trait to inspect the structured data/// in a source.////// A source is like an iterator over its key-values, except with a push-based APIGood call. I think I still need to do a pass over links and make sure they're all resolved correctly so will simplify them wherever possible.
@ -87,3 +82,44 @@ pub fn log<'a, K>(pub fn enabled(level: Level, target: &str) -> bool {crate::logger().enabled(&Metadata::builder().level(level).target(target).build())}I left a comment in the source on why we do this. It's just so we can capture
T: ?Sizedwhile still erasing it behind a dyn Trait. Passing in the double reference in just helps the lifetimes work out.The
way of capturing an error using the
std::error::Errortrait is maybe something we can improve on someday 🙂I think the last thing remaining here is what to do about the
_unstablesuffix on our features. I was thinking making them just shims for the unsuffixed versions, but continuing to accept them so that we don't break every current user of the feature. What do you think @Thomasdezeeuw?We can, but we a also have other breakages, e.g. the removal of the
as_{display,debug, etc.}macros. Some of the API onValuehas changes, which is also breaking.So I'm wondering if we should keep some of the old API, if possible, if
kv_unstableis used? Marking it all as deprecated show it at least generates warnings (I don't know if that is possible for features). Then e.g. 6 months after the stable release we can remove the_unstablefeature and all it's related API? This would give people some time to migrate their code.@ -48,3 +47,1 @@- run: cargo test --verbose --features kv_unstable_sval- run: cargo test --verbose --features kv_unstable_serde- run: cargo test --verbose --features "kv_unstable kv_unstable_std kv_unstable_sval kv_unstable_serde"- run: cargo test --verbose --features kvWe could start using cargo-hack for this at some point (for another pr).
@ -46,25 +46,31 @@ release_max_level_trace = []To bad there is only
compile_error!, notcompile_warn!otherwise it would be nice to add that.Actually on second though... we're going to break a lot of (unstable) code already by removing the
as_serdemacros, do we just rip the bandaid in one go and break everything? Or should we add those macros back if [kv_unstable] is used (and mark them as#[deprecated])?@ -46,25 +46,31 @@ release_max_level_trace = []I think this is a reasonable thing to do 👍
Unfortunately you can’t mark them as deprecated but we can also re-export the renamed visitor traits under their old names when the old
_unstablefeatures are enabled.How does this sound as a plan for stabilization:
kv_unstablefeature is enabled, deprecating wherever possible.kv_unstablefeature altogether and all those deprecated items. If we keep rough tabs on public code using the old features then we could get some idea of when would be a good time to do this.The idea being new code and any maintained in that time period should move from the unstable to the stable API without much fuss, but there will be a point where the bandaid comes off completely. The only unfortunate part is we can’t deprecate old trait names so we don’t have a good way of making users of them aware things have changed.
Sounds good to me 👍
In this latest round of changes I've restored the old APIs under their respective
*_unstablefeature flags and added support for shorthand capturing, so instead of having to writeinfo!(a = a; "")you can writeinfo!(a; "").Ok, I think this is ready for its last review now. Thank you so much for all the time you've invested in helping push this forwards so far @Thomasdezeeuw 🙏 We'd never have reached this point without you.
Nice! Let's merge.
I'm gonna go over the API with https://github.com/Enselic/cargo-public-api/ see what, if any, difference there are
@ -110,3 +113,4 @@trace!(target = "yak_events", yak:serde; "Commencing yak shaving");loop {match find_a_razor() {Nit: maybe this makes the example a little confusing? Making it look like
:is the separator for key-value pairs?@ -46,25 +46,31 @@ release_max_level_trace = []I think we need to enable these features.
This resolved most of the compilation errors for my crate, except for one. It can't find
log::kv::value::Visit.@ -765,13 +596,586 @@ where}Should this be visit?
@ -110,3 +113,4 @@trace!(target = "yak_events", yak:serde; "Commencing yak shaving");loop {match find_a_razor() {That's a good point. It could also look like it's trying to be a pattern binding 🤔 I'll try to clear it up.