Skip to content

Instantly share code, notes, and snippets.

@yoshuawuyts
Forked from KodrAus/tracing_log.rs
Created June 29, 2020 19:11
Show Gist options
  • Save yoshuawuyts/008de7642aa376338e47268c837a5df1 to your computer and use it in GitHub Desktop.
Save yoshuawuyts/008de7642aa376338e47268c837a5df1 to your computer and use it in GitHub Desktop.
Converting between `tracing` key values and `log` key values
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