datadog-formatting-layer 5.0.0

A crate providing a tracing-subscriber layer for formatting events so Datadog can parse them
Documentation
use std::{cmp::Ordering, collections::HashMap};
use tracing::{field::Visit, span::Attributes, Event, Subscriber};
use tracing_subscriber::{
    layer::Context,
    registry::{LookupSpan, Scope},
};

#[derive(Debug, Clone)]
pub struct FieldStore {
    pub fields: Vec<FieldPair>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FieldPair {
    pub name: String,
    pub value: String,
}

impl PartialOrd for FieldPair {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.name.cmp(&other.name))
    }
}

impl Ord for FieldPair {
    fn cmp(&self, other: &Self) -> Ordering {
        self.name.cmp(&other.name)
    }
}

pub fn from_attributes(attrs: &Attributes<'_>) -> Vec<FieldPair> {
    let mut visitor = Visitor::default();
    attrs.values().record(&mut visitor);

    visitor
        .fields
        .into_iter()
        .map(|(key, value)| FieldPair { name: key, value })
        .collect()
}

pub fn from_event(event: &Event<'_>) -> Vec<FieldPair> {
    let mut visitor = Visitor::default();
    event.record(&mut visitor);

    visitor
        .fields
        .into_iter()
        .map(|(key, value)| FieldPair { name: key, value })
        .collect()
}

pub fn from_spans<S: Subscriber + for<'a> LookupSpan<'a>>(
    ctx: &Context<'_, S>,
    event: &Event<'_>,
) -> Vec<FieldPair> {
    ctx.event_scope(event)
        .into_iter()
        .flat_map(Scope::from_root)
        .flat_map(|span| {
            #[allow(clippy::expect_used)]
            let fields_from_span = span
                .extensions()
                .get::<FieldStore>()
                .expect("No Fields found in span extensions")
                .clone();

            fields_from_span.fields
        })
        .collect()
}

#[derive(Default)]
struct Visitor {
    fields: HashMap<String, String>,
}

impl Visit for Visitor {
    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
        self.fields
            .insert(field.name().to_string(), format!("{value:?}"));
    }
}