Review of key-value feature #584
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!584
Loading…
Reference in a new issue
No description provided.
Delete branch "review"
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?
Discussion for https://github.com/rust-lang/log/issues/328.
@ -49,6 +49,8 @@ impl<'k> Key<'k> {self.key}// REVIEW: do we still want this? Isn't most of log's API based on borrowedIt has some niche use, which at this stage I think makes it a fair candidate for removal. If someone discovers a need for it we can bring it back.
@ -374,6 +374,12 @@ mod std_support {}}// REVIEW: I'm not a 100% convinced that AsmAp and AsList are pulling theirThese also seem fair to remove and consider adding later, or adding in a utilities crate 👍
@ -83,6 +83,11 @@ macro_rules! as_sval {/// - Using the `ToValue` trait./// - Using the standard `From` trait.///// REVIEW: maybe add a section about which conversion method to use and when? OrGood call 👍
The preference would be for
capture_*, thenfrom_*. The reason being if youValue::capture_display(&42)you'll get a number, but if you useValue::from_display(&42)you'll get a string. I'd be open to some different naming that made this more obvious. We may consider also only adding them forDisplayandDebugsinceserdeandsvalare naturally structured. We could also consider offering just a singlecapturemethod that is whatcapture_displaycurrently is, and using that by default in the macros. The goal is that by default if you log a boolean or number you should get a boolean or number, and you shouldn't have to implement any non-standard-library traits likeToValueto log something.I'm open to suggestions though. What do you think?
Good point 👍 I'd be up for removing them.
@ -635,6 +646,9 @@ pub trait Visit<'v> {/// more specific methods that aren't overridden./// The `Value` may be formatted using its `fmt::Debug` or `fmt::Display` implementation,/// or serialized using its `sval::Value` or `serde::Serialize` implementation.Good point 👍 There's probably some precedence in the standard library to look at.
That's a good question. The design here is that you shouldn't need separate methods, because you don't know what framework a user logged their structured data with, and as a consumer it doesn't matter to you. They pick the one that best suits them and you just pick the one that best suits you.
Valuewill bridge them for you.The drawback is it's really non-obvious how to work with it. You try to visit a
Valueto see what's in it and you get a method that gives you... aValue. This could just be an issue of documentation. The API itself doesn't give you a lot of clues about how to use it. That could be a reason to add separate methods, and examples alongside them on how to forwardvisit_anyinto them.The only thing we'd need to worry about is whether
Visitorneeds to be object safe. I think it doesn't? In which case it's fine to add separate methods.@ -49,6 +49,8 @@ impl<'k> Key<'k> {self.key}// REVIEW: do we still want this? Isn't most of log's API based on borrowedDone in https://github.com/rust-lang/log/pull/585.
@ -374,6 +374,12 @@ mod std_support {}}// REVIEW: I'm not a 100% convinced that AsmAp and AsList are pulling theirDone in https://github.com/rust-lang/log/pull/585.
Done in https://github.com/rust-lang/log/pull/585.
@ -83,6 +83,11 @@ macro_rules! as_sval {/// - Using the `ToValue` trait./// - Using the standard `From` trait.///// REVIEW: maybe add a section about which conversion method to use and when? OrDoes it make sense to change the macros to always call
log::ToValue::to_value($value)? Then at least for the primitive types users don't have to worry about it. For non-primitive types users will have to useas_{debug,display,serde,sval}. Then, once specialisation is stablised (and log's MSRV is updated), we could even addimpl<T: {fmt::Display,fmt::Debug,serde::Serialize,sval::Value}> ToValue for T { .. }.As for the
capture_*vs.from_*, can we drop the'staticrequirement of the type? If maybe we can merge them (or at least some methods).@ -635,6 +646,9 @@ pub trait Visit<'v> {/// more specific methods that aren't overridden./// The `Value` may be formatted using its `fmt::Debug` or `fmt::Display` implementation,/// or serialized using its `sval::Value` or `serde::Serialize` implementation.I can't find much precedence in std lib. Using
rg --fixed-strings -e '-> Cow<'all I could find wasIntoCow::into_cowas a testing trait.Maybe
to_cow_str?I saw the
Visitortrait as a sort of callback mechanism where you ask the type "what is your concrete type?", where the "most accurate" method gets called. However currently I have to do another layer of matching invisit_anyto determine if serde or sval is used. Making this separate methods avoid that and you can just fall back to usingfmt::Displayinvisit_any.I don't think
Visitorneeds to be object safe, but I rarely use dynamic objects like that. I can imagine case for object safeLogtrait, but forVisitorit's much harder.If it's every needed to I recently ran into
askama::DynTemplate, which I found an interesting work around.I think this is the part you shouldn't need to do in
visit_any. Instd_logger's JSON format for instance, yourvisit_anyimplementation could be:where
self.0isimpl std::io::Write. You don't need to know whether the value comes fromserdeorsval, or even need to override any of the otherVisitormethods. You'll get the most accurate structured representation no matter what the value was captured with.So you're saying that
kv::Valueimplementsserde::Deserializeregardless of the source of the actual value (i.e. asvalvalue would be deserialised correctly?). And vice versa?If so, it does essentially mean that any logging implementation that want to support structured value needs to use either
serdeorsval.That's essentially right 👍 It always implements
serde::Serializeand theSerializeimplementation will always use the least-lossy representation of the data you've captured.For simple values like numbers, strings, and booleans you don't need to. If you want to log complex values like maps or sequences then you'll need to use one of them. If you're happy to treat complex values as text then you can also always use
std::fmtfor them.@ -635,6 +646,9 @@ pub trait Visit<'v> {/// more specific methods that aren't overridden./// The `Value` may be formatted using its `fmt::Debug` or `fmt::Display` implementation,/// or serialized using its `sval::Value` or `serde::Serialize` implementation.to_cow_strsounds totally fine to me here.@ -83,6 +83,11 @@ macro_rules! as_sval {/// - Using the `ToValue` trait./// - Using the standard `From` trait.///// REVIEW: maybe add a section about which conversion method to use and when? OrThe idea was to try discourage libraries from implementing
ToValuethemselves so it doesn't become yet another widespread piece of trivia that library authors need to keep on top of. Defaulting toDisplay + 'staticmeans a lot of library types likeUuidcan be captured, but the drawback of throwing lifetime errors at you if you try log a non-'staticvalue would be unpleasant.For what it's worth, I would be ok with using
ToValueby default in addition to adding some easy syntax forDisplay/Debug/Serialize/Value. If we did that then we could remove thecapture_*methods altogether; they only exist to support downcasting to tell if we're logging numbers/booleans etc.🥲
Thanks for all the time you've spent digging through this so far @Thomasdezeeuw! I think we're getting towards something that feels appropriate for the
logcrate without losing capability.@ -83,6 +83,11 @@ macro_rules! as_sval {/// - Using the `ToValue` trait./// - Using the standard `From` trait.///// REVIEW: maybe add a section about which conversion method to use and when? Or👍 makes sense
We can keep using the
as_display/as_debugmacros, or shorten them to /?, but perhaps that a different discussion.Indeed.
That makes sense. Then I think we should add some docs to the
log::kv::value::Visittrait and leave it as is.@ -635,6 +646,9 @@ pub trait Visit<'v> {/// more specific methods that aren't overridden./// The `Value` may be formatted using its `fmt::Debug` or `fmt::Display` implementation,/// or serialized using its `sval::Value` or `serde::Serialize` implementation.Created https://github.com/rust-lang/log/pull/592.
https://github.com/rust-lang/log/pull/593
Just checking back in on this, is the main blocker we've got deciding whether to default to capturing using
ToValueand supporting sometracing-like syntax for changing it?Yes, I think those are the two main things from this pr.
I'm doing some work on this now; removing the
Value::capture_*and downcasting methods, and improving the overall documentation for thekvmodule.It looks like we already default to using
ToValuefor key-values, so nothing needs to change there. That just leaves some nicer macro support for tweaking how values are captured.Are you happy with that plan @Thomasdezeeuw?
I've opened #613 for this. I'll cc there once it's ready for review, but wanted to point it out here too.
Yes, sounds good 👍
Pull request closed