use std::collections::HashMap;
use tracing::{
field::{Field, Visit},
span, Subscriber,
};
use tracing_subscriber::{layer::Context, Layer};
#[derive(Default)]
pub struct PrimaJsonVisitor<'a> {
fields: HashMap<&'a str, serde_json::Value>,
}
#[derive(Default)]
pub struct PrimaJsonStorage;
pub fn layer() -> PrimaJsonStorage {
PrimaJsonStorage
}
impl<S> Layer<S> for PrimaJsonStorage
where
S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
{
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
let span = ctx.span(id).expect("Span not found, this is a bug");
let mut extensions = span.extensions_mut();
if let Some(visitor) = extensions.get_mut::<PrimaJsonVisitor>() {
attrs.record(visitor);
} else {
let mut visitor = PrimaJsonVisitor::default();
attrs.record(&mut visitor);
extensions.insert(visitor);
}
}
fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
let span = ctx.span(id).expect("Span not found, this is a bug");
let mut extensions = span.extensions_mut();
if let Some(visitor) = extensions.get_mut::<PrimaJsonVisitor>() {
values.record(visitor);
} else {
let mut visitor = PrimaJsonVisitor::default();
values.record(&mut visitor);
extensions.insert(visitor);
}
}
}
impl PrimaJsonVisitor<'_> {
pub fn fields(&self) -> &HashMap<&str, serde_json::Value> {
&self.fields
}
}
impl<'a> PrimaJsonVisitor<'a> {
pub fn get<T: FromValue<'a>>(&'a self, field: &'a str) -> Option<T> {
self.fields.get(field).and_then(T::from_value)
}
}
impl Visit for PrimaJsonVisitor<'_> {
fn record_i64(&mut self, field: &Field, value: i64) {
self.fields
.insert(field.name(), serde_json::Value::from(value));
}
fn record_u64(&mut self, field: &Field, value: u64) {
self.fields
.insert(field.name(), serde_json::Value::from(value));
}
fn record_bool(&mut self, field: &Field, value: bool) {
self.fields
.insert(field.name(), serde_json::Value::from(value));
}
fn record_str(&mut self, field: &Field, value: &str) {
self.fields
.insert(field.name(), serde_json::Value::from(value));
}
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
self.fields
.insert(field.name(), serde_json::Value::from(format!("{value:?}")));
}
}
pub trait FromValue<'a> {
fn from_value(value: &'a serde_json::Value) -> Option<Self>
where
Self: Sized;
}
impl<'a> FromValue<'a> for &'a str {
fn from_value(value: &'a serde_json::Value) -> Option<Self>
where
Self: Sized,
{
match value {
serde_json::Value::String(string) => Some(string),
_ => None,
}
}
}
impl<'a> FromValue<'a> for u32 {
fn from_value(value: &'a serde_json::Value) -> Option<Self>
where
Self: Sized,
{
match value {
serde_json::Value::Number(number) => number.as_u64().map(|number| number as u32),
_ => None,
}
}
}