-
-
Save yoshuawuyts/008de7642aa376338e47268c837a5df1 to your computer and use it in GitHub Desktop.
Converting between `tracing` key values and `log` key values
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::fmt; | |
use log::kv::{self, Source, value::{self, Fill}}; | |
use tracing::{Value, Field, field::{self, Visit}}; | |
#[doc(hidden)] | |
pub struct LogField<'kvs>(&'kvs Field, &'kvs dyn Value); | |
impl fmt::Debug for LogField<'_> { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
let mut visitor = f.debug_tuple(""); | |
self.visit(&mut visitor)?; | |
visitor.finish() | |
} | |
} | |
impl Source for LogField<'_> { | |
fn visit<'kvs>(&'kvs self, visitor: &mut dyn kv::Visitor<'kvs>) -> Result<(), kv::Error> { | |
visitor.visit_pair(kv::Key::from_str(self.0.as_ref()), kv::Value::from_fill(self)) | |
} | |
} | |
/* | |
This `Fill` trait lets us capture a value that might not be able to produce | |
its concrete representation for the lifetime `log` requires of its values. | |
This is an issue here because `tracing`'s record API essentially 'forgets' | |
the lifetime of the value being recorded. As an example, to record some | |
`Debug`, we go from: | |
``` | |
&'kvs tracing::Value -> tracing::Visit::record_debug -> &'_ Debug | |
``` | |
while in `log` we need to go from: | |
``` | |
&'kvs log::kv::Source -> log::kv::Visitor<'kvs>::visit_pair -> &'kvs Debug -> log::kv::Value<'kvs>; | |
``` | |
so that values can be tied to the lifetime of their source and outlive | |
a single call to `log::kv::Source::visit`. The lifetime of the `&Debug` | |
we can pull from `tracing` simply doesn't match up with the lifetime we | |
need in `log`. | |
So this trait gives us a place to host the code that translates a `tracing::Value` | |
into a `log::Value`, and doesn't require the lifetime of the concrete value | |
materialized by `tracing` to satisfy the lifetime needed by `log`. | |
*/ | |
impl<'kvs> Fill for LogField<'kvs> { | |
fn fill(&self, slot: value::Slot) -> Result<(), value::Error> { | |
struct VisitValue<'slot> { | |
slot: value::Slot<'slot>, | |
err: Option<value::Error>, | |
} | |
impl VisitValue<'_> { | |
fn record(&mut self, value: kv::Value) { | |
if let Err(err) = self.slot.fill(value) { | |
self.err = Some(err); | |
} | |
} | |
} | |
impl Visit for VisitValue<'_> { | |
fn record_i64(&mut self, _field: &Field, value: i64) { | |
self.record(value.into()); | |
} | |
fn record_u64(&mut self, _field: &Field, value: u64) { | |
self.record(value.into()); | |
} | |
fn record_bool(&mut self, _field: &Field, value: bool) { | |
self.record(value.into()); | |
} | |
fn record_str(&mut self, _field: &Field, value: &str) { | |
self.record(value.into()); | |
} | |
fn record_debug(&mut self, _field: &Field, value: &dyn fmt::Debug) { | |
self.record(kv::Value::from_debug(&value)); | |
} | |
} | |
let mut visit = VisitValue { | |
slot, | |
err: None, | |
}; | |
self.1.record(self.0, &mut visit); | |
if let Some(err) = visit.err { | |
Err(err) | |
} else { | |
Ok(()) | |
} | |
} | |
} | |
fn example_record(mut iter: field::Iter) { | |
log::Record::builder() | |
.key_values(&(&[ | |
LogField( | |
&iter | |
.next() | |
.expect("FieldSet corrupted (this is a bug)"), | |
&42 as &dyn Value, | |
), | |
LogField( | |
&iter | |
.next() | |
.expect("FieldSet corrupted (this is a bug)"), | |
&false as &dyn Value, | |
), | |
] as &[_])) | |
.build(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment